class Specification {public static void main(String[] args) {// 底层实现原理,Specification接口底层就是利用EntityManager实现EntityManager entityManager = SpringUtil.getBean(EntityManager.class);// 固定套路,总共十一步// 第一步: 获取条件构造器CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();// 第二步: 构造标准查询API,这里使用Object[]而不是使用User是因为下面有别名,函数的情况,所以用实体无法接收,还有很多的方式CriteriaQuery<Object[]> criteriaQuery = criteriaBuilder.createQuery(Object[].class);// 第三步: 指定查询的根实体,说白了就是主表内容,Root就表示主表的字段信息Root<User> root = criteriaQuery.from(User.class);// 第四步(可选): 多表关联,User表与IdCard表关联,使用的都是属性名,自动设置连接条件Join<User, IdCard> join = root.join("idCard", JoinType.LEFT);// 如果设置一对多,或者多对多,使用joinCollection// Join<User, Address> join = root.joinCollection("idCard", JoinType.LEFT);// 第四-五步: 要查询的字段汇总// 查询所有// criteriaQuery.select(root);// 查询指定字段和使用聚合函数并且取别名// 给字段取别名,记得接受结果的返回值的属性需要对应上// 注意: 控制台打印的SQL没有取别名,Hibernate生成的SQL语句不会包含Criteria API中设置的别名Selection<String> name = root.get("name").as(String.class).alias("name");Selection<Integer> age = root.get("age").as(Integer.class).alias("age");// 给聚合函数取别名Expression<Long> count = criteriaBuilder.count(root.get("age"));criteriaBuilder.construct(Long.class, count.alias("count"));// 查询字段,其中,root表示主表select root.*,join表示关联表select join.*criteriaQuery.multiselect(root, join, name, age, count);// 第五步: 构建条件并设置条件,tip: 下面criteriaQuery操作实际上不区分先后顺序// 如果要设置主表的字段条件,使用root.get("字段名")// 如果要设置关联表的字段条件,join.get("字段名")Predicate rootIsDelete = criteriaBuilder.isFalse(join.get("deleted"));Predicate idCardDelete = criteriaBuilder.isFalse(root.get("deleted"));// 如果要使用and,或or,直接使用conjunction,disjunction,构建一个空条件Predicate,然后在进行拼接和and,or方法是一样的Predicate andEmptyPredicate = criteriaBuilder.conjunction();// 构造条件Predicate nameEqualPredicate = criteriaBuilder.equal(root.get("name"), "luck");Predicate ageGePredicate = criteriaBuilder.ge(root.get("age"), 10);// 将构造条件统一存入到空的Predicate,并且他们使用and连接// user.name = 'luck' and user.age >= 10andEmptyPredicate.getExpressions().add(nameEqualPredicate);andEmptyPredicate.getExpressions().add(ageGePredicate);// 上面两行代码等于下面一行代码// user.name = 'luck' and user.age >= 10Predicate andPredicate = criteriaBuilder.and(nameEqualPredicate, ageGePredicate);// 将构造条件统一存入到空的Predicate,并且他们使用or连接// user.name = 'luck' or user.age >= 10Predicate orEmptyPredicate = criteriaBuilder.disjunction();orEmptyPredicate.getExpressions().add(nameEqualPredicate);orEmptyPredicate.getExpressions().add(ageGePredicate);// 上面两行代码等于下面一行代码// user.name = 'luck' or user.age >= 10Predicate orPredicate = criteriaBuilder.or(nameEqualPredicate, ageGePredicate);// 如果要实现 (x1) or (x2)这种情况,那么x1和x2都是一个使用and连接的Predicate,然后x1和x2使用or连接Predicate twoAndOrPredicate = criteriaBuilder.or(andPredicate, andEmptyPredicate);// 嵌套也是一样的(x1 or x2) and (x3 or x4)// 步骤1: x1和x2对应Predicate使用or进行连接,结果为x1x2// 步骤2: x3和x4对应Predicate使用or进行连接,结果为x3x4// 步骤3: 再将步骤一结果和步骤二结果x1x2与x3x4使用and进行连接得到x1x2Andx3x4,最终作为结果// (user.name = ? or username = ?) and (user.age >= 100 or user.age < 10)Predicate x1 = criteriaBuilder.equal(root.get("name"), "luck");Predicate x2 = criteriaBuilder.equal(root.get("name"), "java");Predicate x1x2 = criteriaBuilder.or(x1, x2);Predicate x3 = criteriaBuilder.ge(root.get("age"), 100);Predicate x4 = criteriaBuilder.lt(root.get("age"), 10);Predicate x3x4 = criteriaBuilder.or(x3, x4);Predicate x1x2Andx3x4 = criteriaBuilder.and(x1x2, x3x4);// 以上所有构建的条件都可以使用上,这个就是为了说明sql,上面构造的条件意义不大criteriaQuery.where(rootIsDelete, idCardDelete, orPredicate, twoAndOrPredicate, x1x2Andx3x4);// 第六步(可选): 构造分组条件并设置// 指定字段分组,可以有多个criteriaQuery.groupBy(root.get("age"));// 第七步(可选): 构造分组后条件并设置// 这个和上面讲的where一样criteriaQuery.having();// 第八步(可选): 构造排序条件并设置// 指定排序字段,并且设置排序方式criteriaQuery.orderBy(criteriaBuilder.asc(root.get("age")));// 第九步: 使用标准查询API(CriteriaQuery)构建一个可执行标准查询的Query对象TypedQuery<Object[]> query = entityManager.createQuery(criteriaQuery);// 第十步(可选): 设置分页参数,limit 1,10query.setFirstResult(1).setMaxResults(10);// 第十一步: 执行查询,获取结果List<Object[]> users = query.getResultList();for (Object[] user : users) {for (Object u : user) {System.out.print(u + ",");}System.out.println();}System.out.println(users);}
}