使用JPA Specification EntityManager 多条件 复杂动态查询 子查询 组合 and or 分组 分页 排序 案例

在这里插入图片描述

前言

  • 注意: sql的and的优先级比or高,默认先查and的,比如:
 SELECT 3 > 1 or 3 > 4 and 3 > 4   //结果为true

等价于 SELECT 3 > 1 or	( 3 > 4 and 3 > 4)
	

实例1(基于Specification)

原生sql为

SELECT * from A A WHERE 
(a = 'xx' and b = 'xx' and c in (xx1,xx2)) 
and 
(d = '真(写死)' or ( d= '假(写死)' and 0 < select count(*) from B where e = 'xx' and f = A.a))

对应jpa语句为

  • xx代表动态传参数
Specification<T> specification = (root, cQ, cB)->{
			// 条件集合
            List<Predicate> predicates = new LinkedList<>();
              //动态生成条件a
            if (StringUtils.isNotBlank(a)){
                predicates.add(cB.equal(root.get("a"), xx));
            }
            // 动态生成条件b
            if (StringUtils.isNotBlank(b)){
                predicates.add(cB.equal(root.get("b"), xx));
            }
            // 动态生成范围查询条件c     c是一个数组  比如: string[] c
            if ( c != null && c.length>0){
                cB.In<String> in = cB.in(root.get("c"));
                for (String e : c) {
                    in.value(e);
                }
                predicates.add(in);
            }
            // 生成第一个条件d
            Predicate p3 = cB.equal(root.get("d"), "真(写死)");
            // 生成第二个条件d
            Predicate p1 =  cB.equal(root.get("d"), "假(写死)");
       
  			// 创建子查询 =========   即select count(*) from B where e = 'xx' and f = A.a)部分==============
            Subquery<Long> subquery = cQ.subquery(Long.class); //子查询select的输出类型 (这里是求count)
            Root<ExamStaffList> subRoot = subquery.from(B.class);  //  相当于 from B 表
  					
  			// 生成子查询条件1
            Predicate subP1 = cB.equal(subRoot.get("e"), xx);
  
  			// 生成子查询条件2
            Predicate subP2 = cB.equal(subRoot.get("f"), root.get("a"));
  					
  			// 将 subP1 和 subP1 用 and拼接生成新的条件,  并作为subquery的where条件
            subquery.where(cB.and(subPredicate1,subPredicate2));
            // 选择select出什么
            subquery.select(cB.count(subRoot.get("e"))); // 相当于 select count(e)

  			// 构建复合条件  0 < select count(*) from B where e = 'xx' and f = A.a)
            Predicate p4 = cB.greaterThan(subquery,(long)0);
            ==================================================================================================
         
			//  用 and 将条件集合所有条件用and拼接生成新的条件  
			Predicate tp1 = cB.and(predicates.toArray(new Predicate[0]));
			
			// 相当于拼接 (d = '真(写死)' or ( d= '假(写死)' and 0 < select count(*) from B where e = 'xx' and f = A.a))
  			Predicate tp2 =cB.or(p1,cB.and(p3,p4))
			
			// 最后用and拼接 tp1 和 tp2 后把最终条件返回, 等价于原sql最外层的那个 and 操作
			return cB.and(tp1,tp2);
        };
return specification;

实例2:(基于EntityManager)

原生sql:

SELECT m.name,m.type , count(m.name), (SELECT count(*) from E WHERE  name = e.name and username = xx) as rightCOunt
from E e INNER JOIN M m on e.username = m.name
WHERE m.type in ('网优') and m.name like "%2%" 
GROUP BY m.name
limit 0,10

假设E和M关系为

class E {
   private  Long id;
    
  private String username;
	
  @ManyToOne
  @JoinColumn(name = "name")
  private M m;
}

class M{
	private String name;
	private String type;
}

对应jpa为:

  • 首先创建出要接收结果集的对象,根据原生sql的select字段:
    如:
class ResultDto  {
	private String name;
	private String type;
	private Integer nameCount;
	private Integer roghtCount;
	//  注意: 一定要创建构造函数而且构造函数的参数数量要跟select的个数一样,不然结果集无法注入
	省略构造函数......
}




// 注入 EntityManager
  @PersistenceContext
 private EntityManager entityManager;

//
int page = 0;
int size = 10;

 CriteriaBuilder cB = entityManager.getCriteriaBuilder();
 // 泛型是设置要查询的结果集	
 CriteriaQuery<ResultDto> query = cB.createQuery(ResultDto.class);
 // 从哪张表查
 Root<E> root = query.from(E.class);
// 动态拼接where条件
List<Predicate> predicateList = new ArrayList<>();
if (StringUtils.isNotBlank(examName)){
    predicateList.add(cB.like(root.get("examMission").get("name").as(String.class),"%"+examName+"%"));
}
if (type != null && type.length > 0){
    CriteriaBuilder.In<Object> in = cB.in(root.get("examMission").get("type"));
    for (String e : type) {
        in.value(e);
    }
    predicateList.add(in);
}

if (predicateList.size() > 0)
      query.where(predicateList.toArray(new Predicate[0]));

// 创建子查询,对应原生sql中select的(SELECT count(*) from E WHERE  name = e.name and username = xx) as rightCOunt
Subquery<Long> subquery = query.subquery(Long.class);  // 子查询的结果集类型 ,这里是count(*)所以用long或者int都行
Root<E> subRoot =  subquery.from(E.class); // 子查询从哪张表查,这里是  from E

// 先把原生sql要select的字段列出来
Expression<String> nameField = root.get("m").get("name");
Expression<String> typeField = root.get("m").get("type").as(String.class);
Expression<Integer> nameCountFidld = cB.count(root).as(Integer.class);

Predicate subP1 = cB.equal(subRoot.get("m").get("name"), nameField); // 构建查询条件: name = e.name
Predicate subP2 = cB.equal(subRoot.get("username").as(String.class), xx); // 构建查询条件:  username = xx

// 拼接subquery的select和where条件
subquery.select(cB.count(subRoot).as(Long.class)).where(cB.and(subP1,subP2));

// 拼接各部分
query.multiselect(nameField,typeField,nameCountFidld,subquery.getSelection())
	 .groupBy(root.get("examMission").get("name"));;
	 

List<ResultDto> counts = entityManager.createQuery(query).getResultList();
int totalCount = counts.size();  // 分页返回的总记录数

// 执行查询
TypedQuery<ExamResultStaDto> typedQuery = entityManager.createQuery(query);
typedQuery.setFirstResult(page * size); // 设置分页limit a,b 的a
typedQuery.setMaxResults(size);  // 设置分页limit a,b 的b

// 获得结果集
List<ExamResultStaDto> resultList = typedQuery.getResultList();

// 构造分页对象并返回
PageRequest pageRequest = PageRequest.of(page, size);
return new PageImpl<>(resultList,pageRequest,totalCount);

实例3(基于Entitymanger的原生SQL)

  • 如果还有更复杂的需求,直接上拼接原生SQL字符串吧

这里就不给对比原生sql和jpa实现的对比了, 就是自定义根据条件动态条件拼接sql字符串

// 假设这里是动态拼接后的字符串
String sql = "select ......case when..... where ...... group by...order by limie ....";

// 一般需要分页就要计算总记录数,这个sql跟上面不同是只select count即查出总记录数
String countSql =  "select count(*) ....."; 

// 使用entityManager 创建原生sql查询
Query nativeQuery = entityManager.createNativeQuery(sql); 
Query countQuery = entityManager.createNativeQuery(countSql); 

// 获得结果, 返回的结果集会用二维集合保存,即数据库select出来的每一行记录用Object[]保存
List<Object[]> resultList = nativeQuery.getResultList();
Object totalSize = countQuery.getSingleResult(); // 总记录数

// 一般需要将 resultList 转换为对象存储 省略....... 
 假设resultList 被转换为  List<XXXX> xxlist 对象集合 

// 返回自定义Page对象
return new  PageImpl<XXXX>(xxlist,PageRequest.of(page,size),Long.value(totalSize+""));

总结

  • 人生苦短,在动态拼接sql语句时jpa使用起来异常麻烦,还是选择mybatis 好…

打赏

如果觉得文章有用,你可鼓励下作者

在这里插入图片描述

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
JPA中,我们可以使用Criteria API来实现复杂/多条件组合分页查询。 首先,我们需要创建一个CriteriaBuilder对象,它可以帮助我们构建查询条件排序规则。然后,我们可以通过调用CriteriaBuilder的createQuery方法来创建查询对象,并指定查询的返回结果类型。接下来,我们需要创建一个Root对象,它表示我们要查询的实体类,并以此为基础构建查询条件排序规则。 对于多条件组合查询,我们可以使用Predicate对象来表示不同的查询条件,然后使用CriteriaQuery对象的where方法来添加这些条件。例如,假设我们要查询年龄大于20且性别为女性的用户,我们可以使用如下代码来添加条件: ```java CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery<User> query = criteriaBuilder.createQuery(User.class); Root<User> userRoot = query.from(User.class); Predicate agePredicate = criteriaBuilder.greaterThan(userRoot.get("age"), 20); Predicate genderPredicate = criteriaBuilder.equal(userRoot.get("gender"), "女性"); query.where(criteriaBuilder.and(agePredicate, genderPredicate)); ``` 如果我们还需要对查询结果进行分页,我们可以使用CriteriaQuery对象的setFirstResult和setMaxResults方法来设置查询的起始位置和返回的最大记录数。例如,如果我们要查询第一页的10条记录,我们可以使用如下代码: ```java int pageNumber = 1; int pageSize = 10; query.setFirstResult((pageNumber - 1) * pageSize); query.setMaxResults(pageSize); ``` 最后,我们可以通过调用EntityManager的createQuery方法来执行查询,并获取结果集。例如,我们可以使用以下代码来执行查询: ```java TypedQuery<User> typedQuery = entityManager.createQuery(query); List<User> userList = typedQuery.getResultList(); ``` 以上就是使用JPA实现复杂/多条件组合分页查询的基本步骤。通过使用Criteria API,我们可以灵活地构建查询条件排序规则,实现个性化的查询需求。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值