项目已上传:https://codechina.csdn.net/qq_36286039/javaee
自定义查询
问题:内置的crud功能不满足需求时如何添加自定义查询?
几种自定义查询方法
- 方法命名规则查询
- 按照 JPA 定义的规则,查询方法以
find|read|get
开头(比如 find、findBy、read、readBy、get、getBy),涉及条件查询时,条件的属性用条件关键字连接, - 注意:条件属性首字母需大写。
- JPA 框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对
剩下部分进行解析,最后会自动创建查询。 - 示例:
findByUsernameAndPassword(String name, String pwd)
findByUsernameLike(String name)
详细的方法命名规则
- JPQL 查询 – JPA Query Language
- JPQL和SQL很像,查询关键字都是一样的,主要区别是:JPQL 是面向对象的,是针对实体类进行的操作,即将数据库表名、列名等信息替换为实体类对象及其属性(区分大小写)。
- JPQL 语句可以是 select 语句、update 语句或 delete 语句。
- 常用
@Query
注解。
JPQL 示例
@Transactional 是声明式事务管理编程中使用的注解.
涉及到数据修改操作,可以使用 @Modifying 注解, @Query 与 @Modifying 这两个 annotation一起声明,可定义个性化更新操作
- 示例1:查询所有
@Query("select u from User u")
public List<User> findAllUser();
- 示例2:条件查询
@Query("select u from User u where u.id = ?1")
//或
@Query("select u from User u where u.id = :id")
public User getUserById(long id);
- 示例3:update操作
@Transactional
@Modifying
@Query("update User u set u.username = ?1 where u.id = ?2")
int updateUsernameById(String username, Long id);
- 原生 SQL 查询
- 使用 @Query 注解,value参数写SQL语句,nativeQuery参数为 true。
- 示例1:
@Query(value = "select * from user where id = ?1", nativeQuery = true)
public User getUserById2(Long id);
- 示例2:
@Transactional
@Modifying
@Query(value="update user set username = ?1 where id = ?2", nativeQuery = true)
int updateUsernameById2(String username, Long id);
实操演练:
方法命名规则查询示例
UserRepository.java里添加
// 只需按JPA指定规则写出方法名即可,JPA会自动生成 SQL (太强大了)
// 方法名常见规则:findByXX, countByXX XX为属性名,例如:
public User findByUsername(String username); //根据username精确查找
public List<User> findByUsernameLike(String s); //查询username中包含s串的user,需要手工加%
public List<User> findByUsernameContaining(String s); //查询username中包含s串的user,无需加%
public List<User> findByRegdateAfter(Date mydate); //查询在mydate日期之后注册的user
public Long countByUsernameContaining(String s); //count是统计查询结果的总数
public List<User> findByIdLessThanEqualOrderByUsernameDesc(Long id);
//查询id值小于等于某个值,且按username降序排序
新建一个TestController.java
说明:
3L其实就是3。
java中经常会碰到“long c = 1L”的写法,L表示long ,long占用8个字节,表示范围:-9223372036854775808 ~ 9223372036854775807
常量后面跟这个一般是指类型,1L表示1是长整型,如果是1f 表示是float型
package com.example.jpademo2.controller;
import com.example.jpademo2.entity.User;
import com.example.jpademo2.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
@RestController
public class TestController {
@Autowired
private UserRepository userRepository;
@RequestMapping("/testuser1")
public User test1() {
User user = userRepository.findByUsername("admin");
return user;
}
//查找username包含"m"串的用户
@RequestMapping("/testuser2")
public List<User> test2() {
//需要手动添加“%”
List<User> users = userRepository.findByUsernameLike("%m%");
return users;
}
//查找username包含"m"串的用户
@RequestMapping("/testuser3")
public List<User> test3() {
List<User> users = userRepository.findByUsernameContaining("m");
return users;
}
//查询在mydate日期之后注册的user
@RequestMapping("/testuser4")
public List<User> test4() throws ParseException {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date myDate = df.parse("2020-10-1 23:59:59");
List<User> users = userRepository.findByRegdateAfter(myDate);
return users;
}
//统计username包含"m"的用户个数
@RequestMapping("/testuser5")
public Long test5() {
return userRepository.countByUsernameContaining("m");
}
//查询id值小于等于3L,且按username降序排序
@RequestMapping("/testuser6")
public List<User> test6() {
List<User> users = userRepository.findByIdLessThanEqualOrderByUsernameDesc(3L);
return users;
}
}
用postman测试一下
当前数据库:
http://localhost:8080/testuser1:找admin
http://localhost:8080/testuser2:找用户名包含m的用户
http://localhost:8080/testuser3:找用户名包含m的用户
http://localhost:8080/testuser4:找Mydate之后注册的用户
http://localhost:8080/testuser5:统计username包含"m"的用户个数(2个)
http://localhost:8080/testuser6:查询id值小于等于3L,且按username降序排序
JPQL 查询示例
在UserRepository.java 中添加
//JPQL 查询示例
@Transactional
@Modifying
@Query("update User u set u.username = ?1 where u.id = ?2")
int updateUsernameById(String username, Long id);
@Query(value = "select * from user where id = ?1", nativeQuery = true)
public User getUserById2(long id);
@Transactional
@Modifying
@Query(value="update user set username = ?1 where id = ?2", nativeQuery = true)
int updateUsernameById2(String username, Long id);
在TestController.java中添加
//找所有
@RequestMapping("/testuser7")
public List<User> test7() {
List<User> users = userRepository.findAllUser();
return users;
}
//找id为2的User
@RequestMapping("/testuser8")
public User test8() {
User user = userRepository.getUserById(2L);
return user;
}
//更新Id为2的username
@RequestMapping("/testuser9")
public int test9() {
int count= userRepository.updateUsernameById("dustId2",2L);
return count;
}
使用postman测试:
username被修改了之后在试试
原生 SQL 查询示例
UserRepository.java 中添加
//原生SQL查询示例
@Query(value = "select * from user where id = ?1", nativeQuery = true)
public User getUserById2(long id);
@Transactional
@Modifying
@Query(value="update user set username = ?1 where id = ?2", nativeQuery = true)
int updateUsernameById2(String username, Integer id);
TestController里添加
//找id为2的User
@RequestMapping("/testuser10")
public User test10() {
return userRepository.getUserById2(2L);
}
//更新Id为2的username
@RequestMapping("/testuser11")
public int test11() {
int count= userRepository.updateUsernameById2("dust@qq.com",2);
return count;
}
使用postman测试:
分页查询
- JPA 在查询的方法中传入参数 Pageable 对象 来实现分页功能,通过该参数可得到和分页相关的所有信息。
- 有多个参数时,Pageable 作为最后一个参数
page:第几页,从0开始,默认为第0页
size:每一页的大小,默认为20
sort:排序方式
- Pageable 对象创建方法 :
Pageable pageable = PageRequest.of( page, size, sort );
- sort定义示例:按 id 升序排序
Sort sort = Sort.by(Sort.Direction.ASC, "id");
- 查询方法返回 Page ,可以得到数据的总体信息(如数据总数、总页数
等)以及当前页数据的信息(当前数据的集合、当前页数等): - Page 对象相关方法
方法名 | 功能 |
---|---|
List getContent(); | 将所有数据返回为List |
boolean hasContent(); | 返回数据是否有内容。 |
int getNumber() | 当前第几页(从0开始),总是非负的 |
int getTotalPages() | 返回分页总数 |
long getTotalElements() | 返回元素总数 |
boolean hasPreviousPage() | 如果有上一页。 |
boolean hasNextPage() | 如果有下一页 |
boolean isFirstPage() | 当前页是否为第一页。 |
boolean isLastPage() | 当前页是否为最后一页。 |
int getSize() | 返回当前页面的大小。 |
Sort getSort() | 返回页的排序参数。 |
int getNumberOfElements() | 返回当前页上的元素数。 |
分页查询示例:
UserRepository.java中添加:
//自定义分页查询
//import org.springframework.data.domain.Page;
//import org.springframework.data.domain.Pageable;
//这里很容易引入错误的包
public Page<User> findAllBy(Pageable pageable);//在查询的方法中传入 Pageable 参数
public Page<User> findByUsernameContaining(String username,Pageable pageable);//有多个参数时,Pageable 作为最后一个参数
TestController.java中添加:
@RequestMapping("/testuser12")
public Page<User> test12() {
Sort sort = Sort.by(Sort.Direction.ASC, "id"); //id升序排序
Pageable pageable = PageRequest.of( 0, 10, sort ); // 第0页,每一页10个
Page<User> page = userRepository.findAll( pageable );
return page; //返回结果见下页
}
@RequestMapping("/testuser13")
public List<User> test13() {
Sort sort = Sort.by(Sort.Direction.DESC, "username"); //username降序排序
Pageable pageable = PageRequest.of( 0, 10, sort ); // 第0页,每一页10个
Page<User> page = userRepository.findAll( pageable );
List<User> list = page.getContent(); //得到 List 数据
return list; //返回List<User>
}
分页查询示例2:
服务层:UserService 接口层
package com.example.jpademo2.service;
import com.example.jpademo2.entity.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
//服务层接口
public interface UserService {
//根据也无需要定义业务功能接口方法
public List<User> getUserList();
public User findUserById(Long id);
public void save(User user);
public void edit(User user);
public void delete(Long id);
// 添加两个分页业务功能接口
public Page<User> getUserList(Pageable pageable);
public Page<User> getUserListByUsername(String username,Pageable pageable);
}
服务层:UserServiceImp 实现
package com.example.jpademo2.service;
import com.example.jpademo2.entity.User;
import com.example.jpademo2.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.util.List;
//实现服务层接口
@Service
public class UserServiceImp implements UserService{
@Autowired
private UserRepository userRepository;
@Override
public List<User> getUserList(){
return userRepository.findAll();//直接调用Repository内置的CURD方法
}
@Override
public User findUserById(Long id){
return userRepository.findById(id).get();//直接调用Repository内置的CURD方法
//findById(id)返回的是Optional类(一个可以为null的容器对象)
//如果Optional容器中存在对象,则调用get()方法返回该对象
}
@Override
public void edit(User user){
userRepository.save(user);//直接调用Repository内置的CURD方法
}
@Override
public void save(User user){
userRepository.save(user);//直接调用Repository内置的CURD方法
}
@Override
public void delete(Long id){
userRepository.deleteById(id);//直接调用Repository内置的CURD方法
}
// 实现两个分页业务
@Override
public Page<User> getUserList(Pageable pageable) {
return userRepository.findAll(pageable);
}
@Override
public Page<User> getUserListByUsername(String username,Pageable pageable) {
return userRepository.findByUsernameContaining(username,pageable);
}
}
控制器层:修改的 list 方法
参数说明:实际传递的只有 page,size这两个参数。model和request为系统参数
用了 session 存储"return_url"
@RequestMapping("/list")
public String list(Model model, HttpServletRequest request, @RequestParam(value="page",
defaultValue="0")Integer page, @RequestParam(value="size", defaultValue="5")Integer size) {
if (page==null || page<=0) {
page = 1; } // 将第0页转换为习惯的第1页
if (size==null || size<=0) {
size = 5; }
Sort sort = Sort.by(Sort.Direction.ASC, "id"); // 排序字段是实体类的属性
Pageable pageable = PageRequest.of(page - 1, size, sort); // (当前页,每页记录数,排序方式)
Page<User> users = userService.getUserList(pageable);
model.addAttribute("users", users);
model.addAttribute("size", size);
request.getSession().setAttribute("return_url", request.getServletPath()+"?"+request.getQueryString());
//新建编辑删除中返回的url
return "user/list";
}
视图层:修改 list.html
说明:后台传来的 users 是
Page<User>
通过getContent()
可得到List<User>
<tr th:each="user : ${ users.getContent() }">
<!--添加分页,放在 table 标签后-->
<div th:replace="/common/page :: page1"></div>
修改 add.html、edit.html、delete.html 的所有 “返回” 链接
将原来的
th:href = "@{/list}"
全部修改为th:href = "@{${session.return_url}}"
<a th:href = "@{${session.return_url}}">返回</a>
fragement布局视图:page.html
在 templates 中新建 common 目录,并创建 page.html。
page.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Page</title>
</head>
<body>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="container" th:fragment="page1">
<div style="text-align: right;">
<div>
当前第<span th:text="${users.getNumber()} + 1"></span>页 /
总<span th:text="${users.getTotalPages()}"></span>页
共<span th:text="${users.getTotalElements()}"></span>条记录
<a class="btn btn-info" th:href="@{/list(page=1,size=${size})}">首页</a>
<a class="btn btn-info" th:href="@{/list(page = ${users.hasPrevious()} ? ${users.getNumber()} : 1,size=${size})}">上一页</a>
<a class="btn btn-info" th:href="@{/list(page = ${users.hasNext()} ? ${users.getNumber()} + 2 : ${users.getTotalPages()},size=${size})}">下一页</a>
<a class="btn btn-info" th:href="@{/list(page = ${users.getTotalPages()},size=${size})}">尾页</a>
</div>
</div>
</div>
</body>
</html>
</body>
</html>
运行结果: