# 在 Spring Data JPA 下使用 Criteria API
###### tags: `Spring` `java` `jpa` `persistence`
在實際應用中,資料查詢的條件常常是多變性的,也就是無法100%完全預測使用者所下的限制,所以得使用 [Criteria API](https://javaee.github.io/tutorial/persistence-criteria.html) 以滿足<ruby>動<rt>ㄍㄜˋ</rt></ruby><ruby>態<rt>ㄓㄨㄥˇ</rt></ruby><ruby>搜<rt>ㄕㄞ</rt></ruby><ruby>尋<rt>ㄒㄩㄢˇ</rt></ruby><ruby>條<rt>ㄒㄩ</rt></ruby><ruby>件<rt>ㄑㄧㄡˊ</rt></ruby>。首先 repository 得繼承 ```JpaSpecificationExecutor<T>```<sup>[JavaDoc](https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaSpecificationExecutor.html)</sup>
```
@Repository
public interface SomeoneRepository extends JpaRepository<Someone, Long>, JpaSpecificationExecutor<Someone> {
}
```
使其支援
* ```public long count(Specification<T> spec)```
根據 ```Specification``` 回傳符合的資料集數量。
* ```public List<T> findAll(Specification<T> spec)```
根據 ```Specification``` 回傳符合的資料集。
* ```public Page<T> findAll(Specification<T> spec, Pageable pageable)```
根據 ```Specification``` 及 `Pageable` 回傳符合的資料 ```Page```。
* ```public List<T> findAll(Specification<T> spec, Sort sort)```
根據 ```Specification``` 及```Sort``` 回傳符合的資料集。
* ```public Optional<T> findOne(Specification<T> spec)```
根據 ```Specification``` 回傳符合的資料,若沒有符合的資料則回傳 ```Optional.empty()```。
等 methods。```Page```、```Pageable```請參考此[筆記](https://hackmd.io/@LeeLo/),那什麼是```Specification```呢?
## 建立 Specification
```public final class SomeoneSpecifications {
private SomeoneSpecifications() {
}
/**
* @param nicknames 模糊查詢-會員名稱
* @param tages 查詢-會員名稱
* @return
*/
public static Specification<Someone> likeNickname(Set<String> nicknames, Set<String> tages) {
return new Specification<Someone>() {
@SuppressWarnings("FieldNameHidesFieldInSuperclass")
private static final long serialVersionUID = 6644524130903161741L;
@Override
public Predicate toPredicate(Root<Someone> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
Set<Predicate> set = new HashSet<>();
for (String nickname : nicknames) {
//使用 like 模糊查詢會員名稱
set.add(criteriaBuilder.like(root.get("nickname"), "%" + nickname + "%"));
}
if (!tages.isEmpty()) {
//使用 in 查詢資料
set.add(root.get("nickname").in(tages));
}
Predicate[] predicates = new Predicate[set.size()];
predicates = set.toArray(predicates);
return criteriaBuilder.or(predicates);
}
};
}
}
```
## 實作的 Method 參數介紹
* [Root](https://www.objectdb.com/api/java/jpa/criteria/Root)
* 需要查詢的 Enitity
* [CriteriaQuery](https://docs.oracle.com/javaee/6/api/javax/persistence/criteria/CriteriaQuery.html)
* 用來創建一個查詢對象
* [CriteriaBuilder](https://docs.oracle.com/javaee/7/api/javax/persistence/criteria/CriteriaBuilder.html)
* 可以實作 SQL 的條件式(and, or, equal, like, in,....)
## 在 Controller 可以在 Repository 呼叫 Specifications class method
```
someoneRepository.findAll(SomeoneSpecifications.likeNickname(titles, tags);
```
## 參考來源
> https://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-part-four-jpa-criteria-queries/
> https://developer.ibm.com/articles/j-typesafejpa/
> https://javaee.github.io/tutorial/toc.html