Jpa Specification如何实现and和or同时使用查询

网友投稿 1586 2022-11-20

Jpa Specification如何实现and和or同时使用查询

Jpa Specification如何实现and和or同时使用查询

目录同时使用and和or的查询JPA 动态查询之AND、OR结合使用问题描述代码示例

同时使用and和or的查询

UserServiceImpl 类,service实现类

import org.springframework.beans.BeanUtils;

import org.springframework.beans.factory.ahttp://nnotation.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.criteriaLrmVrf.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 specification = new Specification(){

@Override

public Predicate toPredicate(Root root, CriteriaQuery> criteriaQuery, CriteriaBuilder criteriaBuilder) {

//封装and语句

List listAnd = new ArrayList();

//这里是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 listOr = new ArrayList();

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 , JpaSpecificationExecutor {

}

注意:使用Specification之前,RongUserRepository接口必须实现JpaSpecificationExecutor,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 {

/**

* 其实,这个功能一般用作 list 接口使用,一般结合分页查询使用。这里,我不做介绍,看情况要不要后期加上教程

*/

List findAll(Specification querySpec);

}

下面是前端传过来的动态查询的参数对象:

@Data

public class UserDTO {

/**

* 用户名,用于模糊搜索

*/

private String username;

/**

* 用户ID,用于 In 查询

*/

private List userIdList;

/**

* 用户年龄,用于 OR In 查询

*/

private List 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 getListSpec(UserDTO userDTO) {

return (root, criteriaQuery, criteriaBuilder) -> {

List predicateList = new ArrayList<>();

// 未删除标识,只查询未删除的数据

predicateList.add(criteriaBuilder.equal(root.get("deleteFlag"), 0));

// 根据 用户名 或 年龄List 查询

List usernameOrAgePredicate = new ArrayList<>();

String username = userDTO.getUsername();

if (!StringUtils.isEmpty(username)) {

// 用户名这里,用模糊匹配

usernameOrAgePredicate.add(criteriaBuilder.like(root.get("username"), "%" + username + "%"));

}

List ageList = userDTO.getAgeList();

if (!CollectionUtils.isEmpty(ageList)) {

// 下面是一个 IN查询

CriteriaBuilder.In 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 userIdList = reqDTO.getUserIdList();

if (!CollectionUtils.isEmpty(userIdList)) {

CriteriaBuilder.In 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 querySpec = this.getListSpec(userDTO);

List 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条件。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Mybatis系列之前端显示时间格式问题解决方法
下一篇:Oracle基础之define用法简介教程
相关文章

 发表评论

暂时没有评论,来抢沙发吧~