本文已参与「新人创造礼」活动,一同开启创造之路。
在上一篇文章中,咱们介绍了QueryByExampleExecutor动态查询的办法,那么今天咱们来学习JpaSpecificationExecutor的详细用法。
1、JpaSpecificationExecutor用法
咱们来创立实体类,第一步
:创立User类和UserAddress类
// User类
@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString(exclude = "address")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
private String email;
@Enumerated(value = EnumType.STRING)
private SexEnum sex;
private Integer age;
private LocalDateTime createTime;
private LocalDateTime updateTime;
@OneToMany(mappedBy = "user",fetch = FetchType.EAGER)
private List<UserAddress> address;
}
// Address类
@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = "user")
public class UserAddress {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String address;
@ManyToOne(cascade = CascadeType.ALL)
private User user;
}
// 性别枚举类
public enum SexEnum {
BOY,
GIRL
}
第二步
:创立UserRepo ,咱们承继JpaSpecificationExecutor接口
public interface UserRepo extends JpaSpecificationExecutor<User> {
}
第三步
:测试,构造查询条件
- name含糊查询
- sex精准查询
- age范围查询
- address的in查询
@Test
public void test02(){
User userQuery = User.builder()
.name("jack")
.email("123456@126.com")
.sex(SexEnum.BOY)
.age(20)
.address(Lists.newArrayList(UserAddress.builder().address("shanghai").build()))
.build();
List<User> userList = userRepo.findAll(new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> predicateList = new ArrayList<>();
// name含糊查询
if(StringUtils.isNotBlank(userQuery.getName())) {
predicateList.add(cb.like(root.get("name"),userQuery.getName()));
}
// sex精准查询
if(userQuery.getSex()!=null) {
predicateList.add(cb.equal(root.get("sex"),userQuery.getSex()));
}
// age范围查询
if(userQuery.getAge()!=null){
predicateList.add(cb.greaterThanOrEqualTo(root.get("age"),userQuery.getAge()));
}
// 相关查询
if(!ObjectUtils.isEmpty(userQuery.getAddress())) {
predicateList.add(cb.in(root.join("address").get("address")).value(userQuery.getAddress().stream().map(a->a.getAddress()).collect(Collectors.toList())));
}
return query.where(predicateList.toArray(new Predicate[predicateList.size()])).getRestriction();
}
});
System.out.println(userList);
}
SQL履行成果如下
:
select user0_.id as id1_4_, user0_.age as age2_4_, user0_.create_time as create_t3_4_, user0_.email as email4_4_, user0_.name as name5_4_, user0_.sex as sex6_4_, user0_.update_time as update_t7_4_ from user user0_ inner join user_address address1_ on user0_.id=address1_.user_id where (user0_.name like ?) and user0_.sex=? and user0_.age>=20 and (address1_.address in (?))
2、JpaSpecificationExecutor语法详解
先看源码
:
public interface JpaSpecificationExecutor<T> {
Optional<T> findOne(@Nullable Specification<T> spec);
List<T> findAll(@Nullable Specification<T> spec);
Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);
List<T> findAll(@Nullable Specification<T> spec, Sort sort);
long count(@Nullable Specification<T> spec);
boolean exists(Specification<T> spec);
}
- findOne(@Nullable Specification spec):依据Specification 条件查询单个目标
- findAll(@Nullable Specification spec):依据Specification 条件, 查询List成果
- findAll(@Nullable Specification spec, Pageable pageable):依据Specification 条件, 分页查询
- findAll(@Nullable Specification spec, Sort sort):依据Specification 条件,带排序的查询成果
- count(@Nullable Specification spec): 依据Specification 条件,查询数量
- exists(Specification spec):依据Specification 条件,查询是否存在
2.1 Specification 接口
咱们首要来看一下需求完成的办法:toPredicate(xx,xx,xx)
@Nullable
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);
调试代码
咱们能够分别看到Root的完成类是RootImpl,CriteriaQuery的完成类是CriteriaQueryImpl,CriteriaBuilder的完成类是CriteriaBuilderImpl。这三个完成类都是由Hibernate完成
,也就是说JpaSepcificationExecutor封装了原本需求咱们直接操作Hibernate中Criteria的API。
2.2 Root< User >root
解说
:这个root就相当于查询和操作的实体目标的根,咱们就能够经过Path get(xx)的办法,来获取咱们想要操作的字段。
<Y> Path<Y> get(String attributeName);
例如
:获取User实体类中的name字段
predicateList.add(cb.like(root.get("name"),userQuery.getName()));
2.3 CriteriaQuery<?> query
这是一个Specific的顶层查询目标,它包含着查询的各个部分
,比如select、from、where、group by 、Order by、distinct等。供给查询Root的办法
,咱们来看一下源码:
咱们能够在上面的事例中看到query的用法:
return query.where(predicateList.toArray(new Predicate[predicateList.size()])).getRestriction();
咱们能够再加一个groupBy的例子看看,如下所示:能够链式拼接
return query.where(predicateList.toArray(new Predicate[predicateList.size()])).groupBy(root.get("age")).getRestriction();
履行的SQL如下所示:
2.4 CriteriaBuilder cb
CriteriaBuilder是用来构建CritiaQuery的构建目标,其实就相当于条件或者条件组合
,并以Predicate的形式回来,基本供给了所有常用的办法。
经过源码咱们能够看到CriteriaBuilder 供给了and、any等用来查询条件的组合;还供给了between、equal、exist等用来做查询条件
的查询。
例如:equal
predicateList.add(cb.equal(root.get("sex"),userQuery.getSex()));
例如:like
predicateList.add(cb.like(root.get("name"),userQuery.getName()));
例如:greaterThanEqualTo
predicateList.add(cb.greaterThanOrEqualTo(root.get("age"),userQuery.getAge()));
解说
: 咱们利用equal、like、greaterThanEqualTo 能够回来Predicate,而Predicate又能够组合起来,就构成了杂乱的查询条件,完全满意日常开发运用。