Google Code Prettify

2017年1月11日 星期三

JPA + Spring: 查詢結果為非實體 table (複合型別)


上圖兩個 table 是一對多的關係,要查詢出飯局的地點在那一個餐廳,及餐廳的相關資料,可以用之前介紹過的一對多方式 …
這篇要說明的是另一種方式,這個方式在三個或三個以上 table join 時,特別有用。假設我們要傳回的欄位如下:
 1 @Entity
 2 @Data
 3 public class AppointmentDetail {
 4     private String email; //電子郵件
 5     private String name; //飯局名稱
 6     private String restaurantName; //餐廳名稱
 7     private String restaurantAddr; //餐廳地址
 8     private String description; //飯局說明
 9     private int people; //參與人數
10     private int willPay; //消費額
11     private Date htime; //舉辦時間
12     
13     @Id
14     private Long sysId; //序號
15 }
如上的類別 AppointmentDetail 包含了兩個 table 的欄位,這個類別一樣要用 @Entity 標示,且也要用 @Id 標示可成為唯一的欄位,特別說明的是第 2 行的 @Data,這和 JPA 或 Spring 無關,這是 Lombok 提供的功能,用來簡化程式設計,在這邊加上 @Data 就可以為類別裡所有的欄位 (field) 加上 getter、setter method,這解決了程式設計人員老是在 POJO 中廢時寫 getter、setter 的困擾。

接著在 DAO 中增加一個 method 如下,寫法沒什麼特別,只是傳回結果擁有兩個 table 的欄位。
1     public AppointmentDetail find(Long sysId) {
2         Query query = manager.createNativeQuery(
3                 "  select a.email, a.name, r.name restaurantName, r.address restaurantAddr, a.description, a.people, a.willPay, a.htime, a.sysId "
4                 + "from appointment a inner join restaurant r on a.restaurant_sysid = r.sysId where a.sysId = :sysId "
5                 , AppointmentDetail.class);
6         query.setParameter("sysId", sysId);
7         return (AppointmentDetail) query.getSingleResult();
8     }
測試程式如下,當然也和之前寫過的使用 DAO 的方式沒什麼不一樣。
1     @Transactional
2     @Test
3     public void testFindDetail() {
4         AppointmentDetail detail = daoApp.find(12L);
5         assertNotNull(detail);
6         System.out.println(detail.toString());
7     }
總結來說,唯一的不同在於 @Entity 類別,在那個類別中,不需要使用 @Table 來指出是屬於資料庫裡的那個 table。
另外,在 spring data 中提供另一種更簡潔的寫法,只要寫一個介面,繼承 Repository 或它的子介面 (CrudRepositoryPagingAndSortingRepository、RevisionRepository),可以直接在 method 上寫 sql,如下:
public interface AppointmentDetailDAO extends Repository<AppointmentDetail, Long> {
    @Query(value="select a.email, a.name, r.name as restaurantName, r.address as restaurantAddr, a.description, a.people, a.willPay, a.htime, a.sysId from Appointment a inner join Restaurant r on a.restaurant_sysId = r.sysId where a.sysId = :sysId", nativeQuery=true)
    public AppointmentDetail findDetailBySysId(@Param("sysId") Long sysId);
}
這裡要注意的是,AppointmentDetail 是複合型別,只能用 native query (nativeQuery 預設值為 false),不能用 JPQL,否則會有無法轉換型別的錯誤訊息「org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.Object[]] to type …」。




【Lombok 的使用】
  1. 從官網上下載 lombok.jar,將它放在 eclipse.ini 所在的目錄裡。
  2. 在 eclipse.ini 中加入如下內容:
  3. -Xms2048m
    -Xmx4096m
    -javaagent:lombok.jar
  4. 重啟 eclipse。

沒有留言:

張貼留言