1. 程式人生 > >SprigData-JPA之Query進行復雜查詢並對映到自定物件

SprigData-JPA之Query進行復雜查詢並對映到自定物件

場景介紹

有時候用JPA的時候,想擁有mybatis如此輕鬆的對映,將一堆複雜場景的sql語句對映到一個java類來返回。而JPA本身是很坑的,Query之支援簡單查詢,複雜查詢需要去構造Specification來進行復雜查詢。但是=。=
我就不!!!我就要用Query進行復雜查詢。來吧,ShowTime。

首先,介紹一下場景,就是有這麼一個介面,引數是傳入視窗號和狀態,查詢排隊的佇列。主表是一個叫做check_queue的表,需要關聯wicket視窗表,facility裝置表,check_register檢查使用者資訊表。通過一個複雜的sql來查詢出要的資料並展示出來,表結構就不貼出來,但是通過sql就可以體會大概的條件和場景。

資料查詢預覽

這裡寫圖片描述

SELECT c.id, c.createtime, c.lastupdatetime, c.check_Wicket, c.facility_num
    , c.check_num, c.status, f.facility_name, r.name AS check_name
FROM check_queue c
    LEFT JOIN wicket w ON c.check_Wicket = w.check_Wicket
    LEFT JOIN facility f ON f.facility_num = w.facility_num
    LEFT
JOIN check_register r ON r.check_num = c.check_num AND date_format(r.lastupdatetime, '%y-%m-%d') = date_format(now(), '%y-%m-%d') WHERE c.check_Wicket = 1 AND c.status = 0 AND date_format(c.lastupdatetime, '%y-%m-%d') = date_format(now(), '%y-%m-%d') ORDER BY c.lastupdatetime ASC

可以看到有個條件是**AND date_format(r.lastupdatetime, ‘%y-%m-%d’) = date_format(now(),
‘%y-%m-%d’)**,這個是mysql是查詢今天的。

然後可以看到主表只有c.id, c.createtime, c.lastupdatetime, c.check_Wicket, c.facility_num, c.check_num, c.status,其他兩個欄位f.facility_name, r.name AS check_name是來自關聯的表。

JPA常規查詢並返回

 public interface QueueList {
     String getCheckName();
     String getFacilityName();
     int getId();
     String getCheckNum();
     String getCheckWicket();
     Timestamp getCreatetime();
     String getFacilityNum();
     Timestamp getLastupdatetime();
     String getStatus();
}

那麼如果直接用JPA的Query查詢,返回的資料是

{
    "errorCode": "00",
    "errorMessage": "操作成功",
    "returnObject": [
        {
            "createtime": 1526358195000,
            "id": 49,
            "lastupdatetime": 1526358195000,
            "status": "2",
            "target": {
                "createtime": 1526358195000,
                "lastupdatetime": 1526358195000,
                "check_Wicket": "1",
                "facility_name": "血壓測量",
                "facility_Num": "C3",
                "id": 49,
                "status": "2",
                "check_name": "小湯154",
                "check_Num": "BY185201805140001"
            },
            "targetClass": "org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap"
        }
    ]
}

顯示targetClass是個org.springframework.data.jpa.repository.query.AbstractJpaQueryTupleConverterTupleBackedMap的東西。這一開始是讓我很絕望的。

封裝處理

好了, 接下來,核心的地方來了。
這個是JPA對映的entity類,那兩個關聯的facilityName和checkName,非資料庫的欄位要加@Transient註解

@Entity
@Table(name="check_queue")
@NamedQuery(name="CheckQueue.findAll", query="SELECT c FROM CheckQueue c")
public class CheckQueue implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    private int id;

    private String checkNum;

    private String checkWicket;

    private Timestamp createtime;

    private String facilityNum;

    private Timestamp lastupdatetime;

    private String status;
    @Transient   
    private String facilityName;
    @Transient   
    private String checkName;
}

這裡要吐槽,JPA最傻的地方,就是不給你對映到這個類上面去,這才導致下文的曲線救國。如果可以,早就迎刃而解了。

那我們在Controller裡面這麼處理一下:

    /**
     * 排隊佇列
     */
    @GetMapping("/queue/{checkWicket}/{status}")
    public ApiReturnObject findQueue(@PathVariable String checkWicket,@PathVariable String status) {
        if (StringUtils.isBlank(checkWicket)|| StringUtils.isBlank(status)) {
            return ApiReturnUtil.error("操作失敗,視窗號不能為空");
        }else {//####請注意,前方核心封裝,請勿錯過###
            //獲取佇列,對映到interface查詢類上 
            List<QueueList> findQueue = lineQueueRepository.queryQueueList(checkWicket, status);
            //回遷到新的主資料list上
            List<CheckQueue> dataQueue = new ArrayList<CheckQueue>();
            for (QueueList item:findQueue) {
                //先把interface類的內容格式化為string,就是一些屬性,然後target裡面才是主資料
                String jsonStr=JSON.toJSONString(item);
                //格式化string為JSONObject,方便獲取target屬性
                JSONObject obj=JSON.parseObject(jsonStr);
                //從將QueueList的target的資料真正格式化到主資料類CheckQueue
                CheckQueue data=JSON.toJavaObject(JSON.parseObject(obj.getString("target")), CheckQueue.class);
                //新增到list
                dataQueue.add(data);
                data=null;
            }
            //用這個可以返回一個TupleBackedMap型別的我們寫的interface
            //return ApiReturnUtil.success("操作成功",findQueue);
            //用這個可以返回我們封裝的資料,請看下面封裝後的效果
            return ApiReturnUtil.success("操作成功",dataQueue);
        }       
    }

下面請看本次封裝後的結果。噔噔噔噔。

{
    "errorCode": "00",
    "errorMessage": "操作成功",
    "returnObject": [
        {
            "checkName": "小湯154",
            "checkNum": "BY185201805140001",
            "checkWicket": "1",
            "createtime": 1526358195000,
            "facilityName": "血壓測量",
            "facilityNum": "C3",
            "id": 49,
            "lastupdatetime": 1526358195000,
            "status": "2"
        }
    ]
}

後言

經過後來的摸索,其實如果只是為了返回JSON,也可以直接在Repository層直接用List<Map<String,Object>>來返回,並不需要這麼折騰,這裡純屬瞎折騰,但是思路確實有那麼一點點借鑑的意義吧。