首頁 > 軟體

Spring Data JPA系列JpaSpecificationExecutor用法詳解

2022-09-30 14:01:59

在上一篇文章中,我們介紹了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又可以組合起來,就構成了複雜的查詢條件,完全滿足日常開發使用。

以上就是Spring Data JPA系列JpaSpecificationExecutor用法詳解的詳細內容,更多關於Spring Data JPA JpaSpecificationExecutor的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com