# Spring boot解析: chapter5 JPA, SQL db ![image](https://hackmd.io/_uploads/ryf0GRSS6.png) Figure. 高層次抽象, 以JPA為例 ![image](https://hackmd.io/_uploads/BJyOICSHa.png) Figure. Repository interfaces: 藍色為spring data核心interface ### :microscope: Specification 動態條件查詢 --- > JPA提供Criteria查詢, Spring Data JPA則對Criteria進行封裝 - api interface: SpecificationExecutor - long count(Specification<T> spec); - Optional<T> findOne(Specification<T> spec) - List<T> findAll(Specification<T> spec) - List<T> findAll(Specification<T> spec, Sort sort) - Page<T> findAll(Specification<T> spec, Pageable pageable) - Class specification 組合動態查詢條件 - 定義一個方法需覆寫: Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder); - root 物件本身 - query 最終語法 - criteriaBuilder: 生成各種查詢語法(equal() ==, ge() >=, gt() >) ```java Specification<查詢目標類> specification = (root, query, criteriabuilder) -> { List<Predicate> predicates = new ArrayList<>(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd"); LocalDate start = LocalDate.parse(transReq.getStartDate(), formatter); LocalDate end = LocalDate.parse(transReq.getEndDate(), formatter); predicates.add(criteriabuilder.between(root.get("executeDate"), start, end)); // select t from 目標類 where t.apid = ?1 predicates.add(criteriabuilder.equal(root.get("apid"), apid)); if (StringUtils.hasText(transReq.getiContactId())) { predicates.add(criteriabuilder.equal(root.get("eformId"), transReq.getiContactId())); } if (StringUtils.hasText(transReq.getChangeTypeId())) { predicates.add(criteriabuilder.equal(root.get("changeTypeId"), transReq.getChangeTypeId())); } if (StringUtils.hasText(transReq.getIsActive())) { predicates.add(criteriabuilder.equal(root.get("isActive"), transReq.getIsActive())); } // where: 根據特定的predicate產生特定的specification, 參數為陣列 // 轉成陣列: predicates.toArray(new Predicate[predicates.size()]) // 最後透過getRestriction()取得對應的predicate return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction(); }; ``` Repository 需繼承JpaSpecificationExecutor ```java public interface SreEformChangeinfoRepository extends JpaRepository<SreEformChangeinfo, String>, JpaSpecificationExecutor<SreEformChangeinfo> { // 通常會再寫靜態方法來組合查詢條件, 如前面的Specification的部分 } ``` Parameter binding --- 預設以位置(position-based) binding,這種方法容易導致錯誤,@Query做name binding的方式 (1) `@Param`註解,例如: ![image](https://hackmd.io/_uploads/SkPhBBftp.png) (2) 在Java 8以上的版本,javac compile flag 能傳入 `-parameter`,gradle可傳入參數 TODO: 傳入flag,但測試API仍未解決 ![image](https://hackmd.io/_uploads/BJnnSBfF6.png) (3) Eclipse對應的設定方法:store information about method parameters ![image](https://hackmd.io/_uploads/HkH6rHfYa.png) -parameters @Param的差別 --- 1. **-parameters Compiler Option** 編譯Java source code時使用,若Java class包含方法參數,由於效能考量及減少編譯程式的size,參數名稱通常不會從編譯好的bytecode取得,這個參數告訴compiler要包含parameter names在compiled bytecode,包含參數名稱有利於依賴反射的tools及frameworks,像是在web frameworks HTTP request binding的parameters `javac -parameters MyClass.java` 2. **@param Javadoc Tag (Spring data JPA)** 用來標記Java方法及constructor的參數,map方法參數到自定義sql query的placeholder(外部取用),不涉略編譯過程,而 @param 主要是用於生成文檔,提供對方法參數的說明,顯示指定名稱。 :point_right: 兩者的用途不同,使用-parameters可omit @Param,但就要另加注意命名與sql query的變數名稱一致 ```java @Repository public interface MyEntityRepository extends JpaRepository<MyEntity, Long> { @Query("SELECT e FROM MyEntity e WHERE e.someProperty = :paramValue") List<MyEntity> findBySomeProperty(@Param("paramValue") String testValue); } ``` 參考 - [Spring Data JPA 何时必须使用@Param注解](https://blog.csdn.net/mryang125/article/details/100604645) - [Using Named Parameters](https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html) - [Use @Param for query method parameters, or when on Java 8+ use the javac flag -parameters](https://stackoverflow.com/questions/66398543/use-param-for-query-method-parameters-or-when-on-java-8-use-the-javac-flag-p) - [如何省略springboot项目中的@Param注解](https://blog.csdn.net/qqgg0216/article/details/134132118?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EYuanLiJiHua%7EPosition-2-134132118-blog-87954102.235%5Ev40%5Epc_relevant_3m_sort_dl_base1&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EYuanLiJiHua%7EPosition-2-134132118-blog-87954102.235%5Ev40%5Epc_relevant_3m_sort_dl_base1&utm_relevant_index=5)