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.AbstractJpaQueryTupleBackedMap的東西。這一開始是讓我很絕望的。
封裝處理
好了, 接下來,核心的地方來了。
這個是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>>
來返回,並不需要這麼折騰,這裡純屬瞎折騰,但是思路確實有那麼一點點借鑑的意義吧。