[JAVA EE] JPA 查询用法:自定义查询,分页查询

本文详细介绍了在Spring Data JPA中如何进行自定义查询,包括方法命名规则查询、JPQL查询和原生SQL查询。通过实例展示了如何找到特定用户、执行更新操作以及进行分页查询。此外,还提供了测试用例以验证查询效果。
摘要由CSDN通过智能技术生成

项目已上传: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>

运行结果:
在这里插入图片描述
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值