千家信息网

Jpa Specification怎么实现and和or同时使用查询

发表于:2025-02-05 作者:千家信息网编辑
千家信息网最后更新 2025年02月05日,这篇文章主要为大家展示了"Jpa Specification怎么实现and和or同时使用查询",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"Jpa Spec
千家信息网最后更新 2025年02月05日Jpa Specification怎么实现and和or同时使用查询

这篇文章主要为大家展示了"Jpa Specification怎么实现and和or同时使用查询",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"Jpa Specification怎么实现and和or同时使用查询"这篇文章吧。

同时使用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@Transactionalpublic 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   tableWHERE  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层接口:

@Repositorypublic interface UserDAO extends JpaRepository {    /**     * 其实,这个功能一般用作 list 接口使用,一般结合分页查询使用。这里,我不做介绍,看情况要不要后期加上教程     */    List findAll(Specification querySpec);}

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

@Datapublic 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 userIdListAND    user.birthDay > birthDayBegin AND user.birthDay < birthDayEnd ;

当前,需要对应值不为空,才会拼接相应的AND条件。

以上是"Jpa Specification怎么实现and和or同时使用查询"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

0