2020年5月23日 星期六

Map Join


本文為【Spring Boot情境式網站開發指南:使用Spring Data JPA、Spring Security、Spring Web Flow】一書的【 第4章 Criteria API入門】延續,完整範例程式碼可至出版社下載

Entity類別以可以關連到Map型態的欄位,如同List或Set,也可以使用@ElementCollection、@ManyToMany、@OneToMany等標註。在JPQL中提供KEY、 VALUE、ENTRY等關鍵字存取Map物件,Criteria API中則為:
  1. Root<Entity>使用join()方法和Map<Key, Value>屬性欄位聯結後,可以取得MapJoin<Entity, Key, Value>物件。
  2. MapJoin<Entity, Key, Value>具備以下方法存取關連到Map型態的欄位:
  • key():取得Map物件的key值,等同JPQL的KEY敘述。
  • value():取得Map物件的value值,等同JPQL的VALUE敘述。
  • entry():同時取得Map物件的key與value值,等同JPQL的ENTRY敘述。
 當JPQL使用以下查詢時:

  1. TypedQuery typedQuery = em.createQuery(
  2. "SELECT c.name, KEY(map), VALUE(map) FROM Customer c JOIN c.itemQtyMap map",
  3. Tuple.class);
對應的Criteria API用法如下:
  1. @Test
  2. public void Criteria_GetMapKeyValueByTuple() {
  3. EntityManager em = emf.createEntityManager();
  4. CriteriaBuilder cb = em.getCriteriaBuilder();
  5. CriteriaQuery sql = cb.createTupleQuery();
  6. Root cust = sql.from(Customer.class);
  7. MapJoin itemQtyMap =
  8. cust.join(Customer_.itemQtyMap);
  9. sql.multiselect(cust.get(Customer_.name),
  10. itemQtyMap.key(),
  11. itemQtyMap.value() );
  12. TypedQuery typedQuery = em.createQuery(sql);
  13. List resultList = typedQuery.getResultList();
  14. List stringList = resultList.stream()
  15. .map(t -> t.get(0) + ", " + t.get(1) + ", " + t.get(2))
  16. .collect(Collectors.toList());
  17. stringList.forEach(System.out::println);
  18. assertThat(stringList, containsInAnyOrder("jim, computer, 1",
  19. "jim, mouse, 4",
  20. "colin, notebook, 3"));
  21. }
  • 行5 
使用Tuple封裝查詢結果的複數欄位。

  • 行7-8

使用Root<Customer> cust的join()方法聯結Map型態的欄位,並回傳MapJoin<Customer, String, Integer>物件。介面MapJoin<1, 2, 3>的泛型指定3種型態:
1. Entity型態,本例為Customer。
2. Map欄位的key型態,本例為String。
3. Map欄位的value型態,本例為Integer。

  • 行9-11

使用CriteriaBuilder物件的multiselect()方法查詢name欄位, Map欄位的key欄位由itemQtyMap.key()表示,Map欄位的value欄位由itemQtyMap.value()表示。
當JPQL使用以下查詢時:
  1. TypedQuery typedQuery = em.createQuery(
  2. "SELECT ENTRY(map) FROM Customer c JOIN c.itemQtyMap map",
  3. Map.Entry.class);

對應的Criteria API用法如下。本例只查詢Map的key與value等2個欄位,使用Map.Entry型態封裝查詢後的結果:
  1. @Test
  2. public void Criteria_GetEntryByMapEntry() {
  3. EntityManager em = emf.createEntityManager();
  4. CriteriaBuilder cb = em.getCriteriaBuilder();
  5. CriteriaQuery sql = cb.createQuery(Map.Entry.class);
  6. Root cust = sql.from(Customer.class);
  7. MapJoin orderMap =
  8. cust.join(Customer_.itemQtyMap);
  9. sql.select(orderMap.entry());
  10. TypedQuery typedQuery = em.createQuery(sql);
  11. // hibernate.version=5.2.10.Final will throw ClassCastException
  12. List resultList = typedQuery.getResultList();
  13. List stringList = resultList.stream()
  14. .map(entry -> entry.getKey()+ ", " + entry.getValue())
  15. .collect(Collectors.toList());
  16. stringList.forEach(System.out::println);
  17. assertThat(stringList, containsInAnyOrder("computer, 1",
  18. "mouse, 4",
  19. "notebook, 3"));
  20. }
  • 行5
使用Map.Entry封裝查詢後的Map的key與value欄位。
  • 行9-11
使用CriteriaBuilder物件的select()方法查詢itemQtyMap.entry(),將同時取得key與value欄位。
值得注意的是,本例在5.2.10.Final的hibernate版本下將拋出ClassCastException的例外,使用較新版本則無此問題。

沒有留言:

張貼留言