Total Pageviews

2011/07/21

Wrapping JPQL Query Results with Instances of Custom Result Classes

JPA supports wrapping JPQL query results with instances of custom result classes. This is mainly useful for queries with multiple SELECT expressions, where custom result objects can provide an object oriented alternative to representing results as Object[] or Object elements.

The fully qualified name of the result class is specified in a NEW expression, as follows:
1
2
3
4
5
6
7
8
   StringBuilder sql = new StringBuilder();
   sql.append(" select new gov.fdc.nig.bean.NIG451Bean(t1.manageCd, t1.baTaxMk, ");
   sql.append(" t1.vioHostNm, t1.vioHostIdnBan, t1.vioYr, t1.addrHsnNm, ");
   sql.append(" t1.addrTownNm, t1.addrVillNm, t1.addrLin, t1.addrRoadNo, ");
   sql.append(" t1.respNm, t1.respIdn, t1.prstAdtrCd, t1.nigt007.adtrNm, ");
   sql.append(" t1.authMk, t1.adtSgstMk) from Nigt001 t1");
   sql.append(" where t1.id.dlvUnit = ?1 and t1.id.taxCd = ?2 and t1.id.dlvYr = ?3 ");
   sql.append(" and t1.id.flgTp = ?4 and t1.id.serialNo = ?5 ");


The result class must have a compatible constructor that matches the SELECT result expressions, as follows:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
   public class NIG451Bean extends BeanBase {
       // 管理代號
       private String manageCd;
       // 本稅註記
       private String baTaxMk;
       // 違章主體名稱
       private String vioHostNm;
       // 違章主體統一編號
       private String vioHostIdnBan;
       // 違章年度
       private String vioYr;
       // 地址縣市名稱
       private String addrHsnNm;
       // 地址鄉鎮市區名稱
       private String addrTownNm;
       // 地址村里名稱
       private String addrVillNm;
       // 地址鄰
       private String addrLin;
       // 地址街道門牌
       private String addrRoadNo;
       // 負責人姓名
       private String respNm;
       // 負責人身分證統一編號
       private String respIdn;
       // 目前審理人員代號
       private String prstAdtrCd;
       // 審理人員姓名
       private String adtrNm;
       // 授權註記
       private String authMk;
       // 審查意見補頁註記
       private String adtSgstMk;
       
       public NIG451Bean(String manageCd, String baTaxMk, String vioHostNm,
                         String vioHostIdnBan, String vioYr, String addrHsnNm,
                         String addrTownNm, String addrVillNm, String addrLin,
                         String addrRoadNo, String respNm, String respIdn,
                         String prstAdtrCd, String adtrNm, String authMk, String adtSgstMk) {
           super();
           this.manageCd = manageCd;
           this.baTaxMk = baTaxMk;
           this.vioHostNm = vioHostNm;
           this.vioHostIdnBan = vioHostIdnBan;
           this.vioYr = vioYr;
           this.addrHsnNm = addrHsnNm;
           this.addrTownNm = addrTownNm;
           this.addrVillNm = addrVillNm;
           this.addrLin = addrLin;
           this.addrRoadNo = addrRoadNo;
           this.respNm = respNm;
           this.respIdn = respIdn;
           this.prstAdtrCd = prstAdtrCd;
           this.adtrNm = adtrNm;
           this.authMk = authMk;
           this.adtSgstMk = adtSgstMk;
       }
       //setter and getter method
   }


The following code demonstrates running this query:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
   public NIG451Bean autoCompleteForNIG451(Nigt001PK nigt001pk) {
       StringBuilder sql = new StringBuilder();
       sql.append(" select new gov.fdc.nig.bean.NIG451Bean(t1.manageCd, t1.baTaxMk, ");
       sql.append(" t1.vioHostNm, t1.vioHostIdnBan, t1.vioYr, t1.addrHsnNm, ");
       sql.append(" t1.addrTownNm, t1.addrVillNm, t1.addrLin, t1.addrRoadNo, ");
       sql.append(" t1.respNm, t1.respIdn, t1.prstAdtrCd, t1.nigt007.adtrNm, ");
       sql.append(" t1.authMk, t1.adtSgstMk) from Nigt001 t1");
       sql.append(" where t1.id.dlvUnit = ?1 and t1.id.taxCd = ?2 and t1.id.dlvYr = ?3 ");
       sql.append(" and t1.id.flgTp = ?4 and t1.id.serialNo = ?5 ");
       
       EntityManager entityManager = getEntityManager_A05_TAX();
       Query query = entityManager.createQuery(sql.toString());
       query.setParameter(1, nigt001pk.getDlvUnit());
       query.setParameter(2, nigt001pk.getTaxCd());
       query.setParameter(3, nigt001pk.getDlvYr());
       query.setParameter(4, nigt001pk.getFlgTp());
       query.setParameter(5, nigt001pk.getSerialNo());
       
       return (NIG451Bean)query.getSingleResult();
   }


Any class with a compatible constructor can be used as a result class. It could be a JPA managed class (e.g. an entity class) but it could also be a lightweight 'transfer' class that is only used for collecting and processing query results.

If an entity class is used as a result class, the result entity objects are created in the NEW state, which means that they are not managed. Such entity objects are missing the JPA functionality of managed entity objects (e.g. transparent navigation and transparent update detection), but they are more lightweight, they are built faster and they consume less memory.

No comments: