项目已上传:https://codechina.csdn.net/qq_36286039/javaee

自定义查询

问题:内置的crud功能不满足需求时如何添加自定义查询?

几种自定义查询方法

  1. 方法命名规则查询
  • 按照 JPA 定义的规则,查询方法以 find|read|get 开头(比如 find、findBy、read、readBy、get、getBy),涉及条件查询时,条件的属性用条件关键字连接,
  • 注意:条件属性首字母需大写。
  • JPA 框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对
    剩下部分进行解析,最后会自动创建查询。
  • 示例:
  • findByUsernameAndPassword(String name, String pwd)
  • findByUsernameLike(String name)

详细的方法命名规则


  1. 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);

  1. 原生 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>

运行结果: