JPA仕様EntityManagerマルチコンディションコンプレックスダイナミッククエリサブクエリの組み合わせまたはページングソートケースを使用する

ここに写真の説明を挿入

序文

  • 注意: SQLはまたはよりも優先度が高く、デフォルトではチェックして最初に、といった:
 SELECT 3 > 1 or 3 > 4 and 3 > 4   //结果为true

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

例1(仕様に基づく)

ネイティブ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の選択フィールドに従って、結果セットを受け取るオブジェクトを作成します。
    例:
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を選択することをお勧めします...

おすすめ

転載: blog.csdn.net/weixin_41347419/article/details/105098113