Jpa Specification如何实现and和or同时使用查询
作者:qq_41315539 发布时间:2023-10-17 20:49:38
同时使用and和or的查询
UserServiceImpl 类,service实现类
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private RongUserRepository rongUserRepository;
//FriendNumResult 自定的返回类型
//FriendNumParam 自定义的封装参数的类型
//RongUser 实体类型
@Override
public FriendNumResult friendNum(FriendNumParam friendNumParam) {
FriendNumResult friendNumResult=new FriendNumResult();
Specification<RongUser> specification = new Specification<RongUser>(){
@Override
public Predicate toPredicate(Root<RongUser> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
//封装and语句
List<Predicate> listAnd = new ArrayList<Predicate>();
//这里是hql,所以root.get(),方法里面必须是对应的实体属性
listAnd.add(criteriaBuilder.equal(root.get("perLevel").as(Integer.class), friendNumParam.getPerLevel()));
Predicate[] array_and=new Predicate[listAnd.size()];
Predicate Pre_And = criteriaBuilder.and(listAnd.toArray(array_and));
//封装or语句
List<Predicate> listOr = new ArrayList<Predicate>();
listOr.add(criteriaBuilder.equal(root.get("fId").as(Integer.class), friendNumParam.getUid()));
listOr.add(criteriaBuilder.equal(root.get("fId2").as(Integer.class), friendNumParam.getUid()));
Predicate[] arrayOr = new Predicate[listOr.size()];
Predicate Pre_Or = criteriaBuilder.or(listOr.toArray(arrayOr));
return criteriaQuery.where(Pre_And,Pre_Or).getRestriction();
//单独使用 and 或者 or 时 返回
//return criteriaBuilder.and(list.toArray());
}
};
long num=this.rongUserRepository.count(specification);
friendNumResult.setFriendNum(Integer.valueOf((int)num));
return friendNumResult;
}
}
RongUserRepository接口
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
//RongUser 自己的实体类型
public interface RongUserRepository extends JpaRepository<RongUser,Integer> , JpaSpecificationExecutor<RongUser> {
}
注意:使用Specification之前,RongUserRepository接口必须实现JpaSpecificationExecutor<RongUser>,RongUser对应表的实体类
JPA 动态查询之AND、OR结合使用
现在,我负责开发的项目中,使用JPA作为ORM框架。有了JPA,一行SQL都没写过。在昨天,有一个新的需求,需要进行动态查询,这个简单。但是有一个地方需要AND、OR结合使用,这里,我将记录下我的理解与写法,希望能帮助到大家。
问题描述
需要根据条件进行动态查询,实现一条类似下文的语句:
SELECT *
FROM table
WHERE 1 = 1
if (a == 1)
AND table.column1 = a
if (b != null)
AND table.column2 = b
if (cList != null && cList.size() > 0)
AND table.column3 IN cList
if (d == 2 || dd == 2)
AND (table.column4 = d OR table.column5 = dd)
上面是几行伪代码。意思是,几个条件之间是AND连接,但是其中的部分条件,是使用OR连接的。
在我们的实际项目中,这个场景也是很常见的。这里,我将分享下具体的写法。以我们项目中的例子为例。
代码示例
JPA的动态查询,这里我们使用的方式是:实现 Specification 接口,自定义动态查询逻辑。这也是我个人比较推荐的方式。JPA的使用、Specification 接口基础知识这里我就不讲了。有兴趣的朋友可以查阅官方文档学习。
下面,我们首先定义好我们的数据库实体:
@Data
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
/**
* 用户名
*/
private String username;
/**
* 年龄
*/
private Integer age;
/**
* 生日
*/
private Date birthDay;
/**
* 删除标识; 0 - 未删除,1 - 已删除
*/
private Integer deleteFlag;
}
然后定义好DAO层接口:
@Repository
public interface UserDAO extends JpaRepository<User, Long> {
/**
* 其实,这个功能一般用作 list 接口使用,一般结合分页查询使用。这里,我不做介绍,看情况要不要后期加上教程
*/
List<User> findAll(Specification<User> querySpec);
}
下面是前端传过来的动态查询的参数对象:
@Data
public class UserDTO {
/**
* 用户名,用于模糊搜索
*/
private String username;
/**
* 用户ID,用于 In 查询
*/
private List<String> userIdList;
/**
* 用户年龄,用于 OR In 查询
*/
private List<Integer> ageList;
/**
* 生日,开始
*/
@JsonFormat(pattern = "yyyy-MM-dd", locale = "zh", timezone = "GMT+8")
private Date birthDayBegin;
/**
* 生日,结束
*/
@JsonFormat(pattern = "yyyy-MM-dd", locale = "zh", timezone = "GMT+8")
private Date birthDayEnd;
}
然后,重要的地方来了,我们实现 Specification 接口,定义查询逻辑:
在实际代码操作中,我会将这部分逻辑抽离为一个单独的方法,使用lambda表达式完成,其实也就是匿名内部类。
private Specification<User> getListSpec(UserDTO userDTO) {
return (root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> predicateList = new ArrayList<>();
// 未删除标识,只查询未删除的数据
predicateList.add(criteriaBuilder.equal(root.get("deleteFlag"), 0));
// 根据 用户名 或 年龄List 查询
List<Predicate> usernameOrAgePredicate = new ArrayList<>();
String username = userDTO.getUsername();
if (!StringUtils.isEmpty(username)) {
// 用户名这里,用模糊匹配
usernameOrAgePredicate.add(criteriaBuilder.like(root.get("username"), "%" + username + "%"));
}
List<Integer> ageList = userDTO.getAgeList();
if (!CollectionUtils.isEmpty(ageList)) {
// 下面是一个 IN查询
CriteriaBuilder.In<Integer> in = criteriaBuilder.in(root.get("age"));
ageList.forEach(in::value);
usernameOrAgePredicate.add(in);
}
/* 下面这一行代码很重要。
* criteriaBuilder.or(Predicate... restrictions) 接收多个Predicate,可变参数;
* 这多个 Predicate条件之间,是使用OR连接的;该方法最终返回 一个Predicate对象;
*/
predicateList.add(criteriaBuilder.or(usernameOrAgePredicate.toArray(new Predicate[0])));
// 用户ID List,IN 查询
List<Integer> userIdList = reqDTO.getUserIdList();
if (!CollectionUtils.isEmpty(userIdList)) {
CriteriaBuilder.In<Integer> in = criteriaBuilder.in(root.get("id"));
userIdList.forEach(in::value);
predicateList.add(in);
}
// 生日时间段查询
Date birthDayBegin = reqDTO.getBirthDayBegin();
Date birthDayEnd = reqDTO.getBirthDayEnd();
if (birthDayBegin != null && birthDayEnd != null) {
// DateUtils 是我自定义的一个工具类
Date begin = DateUtils.startOfDay(birthDayBegin);
Date end = DateUtils.endOfDay(birthDayEnd);
predicateList.add(criteriaBuilder.greaterThanOrEqualTo(root.get("birthDay"), begin));
predicateList.add(criteriaBuilder.lessThanOrEqualTo(root.get("birthDay"), end));
}
// 最终,使用AND 连接 多个 Predicate 查询条件
return criteriaBuilder.and(predicateList.toArray(new Predicate[0]));
};
}
这样,我们的动态查询部分就构建完毕了。具体怎么使用呢?如下:
Specification<User> querySpec = this.getListSpec(userDTO);
List<User> userList = userDAO.findAll(querySpec);
就这样,我们就执行了一次动态查询,并获取到了结果。
上面的动态查询,实际上等价于下面的伪代码:
SELECT *
FROM user
WHERE user.deleteFlag = 0
AND ( user.username like '%{username}%' OR user.age IN ageList )
AND user.id IN userIdList
AND user.birthDay > birthDayBegin AND user.birthDay < birthDayEnd ;
当前,需要对应值不为空,才会拼接相应的AND条件。
至此,JPA 动态查询之 AND OR结合使用,教程到这里就结束了。以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
来源:https://blog.csdn.net/qq_41315539/article/details/104557199


猜你喜欢
- 本文为大家分享了Android Studio debug功能的具体使用方法,供大家参考,具体内容如下运行debug模式 1. 进入
- 本文实例讲述了C#简单实现SNMP的方法。分享给大家供大家参考。具体如下:/**C# Network Programming by Rich
- 引言前边两章说了点基础的,从这章开始,我们挖挖源码。看看RocketMQ是怎么工作的。首先呢,这个生产者就是送孩子去码头的家长,孩子们呢,就
- C#是一种编译型的语言,程序执行,首先要经过编译器编译,如何让C#像一种脚本一样,在要执行的时候,进行编译,这里,我们可以用Microsof
- 在java中常常会遇到这样一个问题,在实际应用中,总会碰到对List排序并过滤重复的问题,如果List中放的只是简单的String类型过滤s
- 方法有4种:使用 String 类的 valueOf() 方法使用字符串连接使用 Character 类的 toString() 方法使用字
- MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对
- springboot + docker + jenkins自动化部署项目,jenkins、mysql、redis都是docker运行的,并且
- 在一个完整的项目中,如果每一个控制器的方法都返回不同的结果,那么对项目的维护和扩展都会很麻烦;并且现在主流的开发模式时前后端分离的模式,如果
- async和awaitasync微软文档:使用 async 修饰符可将方法、lambda 表达式或匿名方法指定为异步。使
- 一、需求Jenkins大多数情况下都是用来部署Java项目,Java项目有一个特点是>需要编译和打包的,一般情况下编译和打包都是用ma
- 多态性1理解多态性:可以理解为一个事物的多种形态。2何为多态性:对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)3多态的
- 前言一般数据库的表结构都会有update_time,修改时间,因为这个字段基本与业务没有太大关联,因此开发过程中经常会忘记设置这两个字段的值
- 本文实例讲述了C#简单实现防止多个程序运行的方法。分享给大家供大家参考,具体如下:/// <summary>/// 应用程序的主
- java加载properties文件的六种方法总结java加载properties文件的六中基本方式实现java加载properties文件
- ——声明,脑残人士远离,本博客的核心不是if-else+前缀,而是如何通过URL协议处理框架定义私有协议URI与URL的区别URI (uni
- 本文实例为大家分享了C++实现连连看游戏的具体代码,供大家参考,具体内容如下这个项目还是挺不错的,运行后也比较有意思,可以看看。#inclu
- Android安全加密专题文章索引Android安全加密:对称加密Android安全加密:非对称加密Android安全加密:消息摘要Mess
- 1、线程的控制很常见,如文件传送到一半时,需要暂停文件传送,或终止文件传送,这实际上就是控制线程的运行。2、线程有创建、可运行、运行中、阻塞
- Android 自定义 Toast 显示时间实现代码:package com.wm.realname.util;import android