背景
某個查詢,是流水-發券類的查詢,查詢流水的同時,想要取得關聯的卡券,關系是一對多的關系。希望返回給前端的結果如下:
{
"orderId":123,
"num":1,
"cardList": [
{
"id":0001,
"cardCode":ASDAS15QWE,
}
]
}
問題點:
- 由於是訂單流水,考慮數量比較大的情況,所以必須使用分頁,且是使用服務器端的分頁。
- 為了便於前端展示,最好使用[]的數組形式進行顯示
原本的處理(存在bug)
數組的返回結果使用了resultMap的collection來進行處理,分頁是mybatis-plus,其分頁的本質是在sql 里加limit。
本來數據少的時候沒發現什么問題,造了點數據,數據多了就發現,單頁存在card數組內容不全,重復等問題。
比如假設數據:
訂單id | 數量 |
---|---|
001 | 1 |
002 | 2 |
003 | 3 |
004 | 4 |
005 | 5 |
卡券id | 訂單id | 卡號 |
---|---|---|
1 | 001 | c921dd66f922 |
2 | 002 | 1991f9230af2 |
3 | 002 | bdf214812363 |
4 | 003 | f4b43652fc91 |
5 | 004 | d1663166f32c |
6 | 004 | 17a22b31fe7a |
7 | 004 | 0c9b35eda5e1 |
如果訂單分頁限制是5條,正常的時候應該活全部取出,但是使用collection是,sql類似是如下:
SELECT 訂單id,數量,卡券id,卡號 FROM 訂單 JOIN 卡券 ON 卡券.訂單id = 訂單.訂單id
以上sql 的result結果是8條,如果使用sql的limit,限制5條的話,后三條就無法取得。
初版解決:
卡券的select另寫sql的mapper,使用單獨查詢,比如:
<collection column="{條件}" select="另一sql"/>
但是,這種情況,是mybatis在取得查詢結果之后,對每條單獨查詢。比如一次查詢取得5條件結果,則以上sql會執行5次。效率太低。
最后解決(GROUP_CONCAT + 自定義typeHandler):
首先,使用GROUP_CONCAT轉換成String形式的json數據,如下:
SELECT 訂單id,
數量,
(SELECT CONCAT('[',IFNULL(GROUP_CONCAT(CONCAT('{ 卡券id:',卡券id,',卡號:',卡號,'}') Separator ','),''),']') FROM 卡券 WHERE 卡券.訂單id = 訂單.訂單id) AS JSON
FROM 訂單
以上的sql的查詢結果是[]形式的json數據。這樣的數據其實返回給前端,讓前端JSON.parse(返回數據)的方式進行處理也是可以的。但為了符合需求,使用mybatis自定義typeHandler的方法,給前端返回json的數據。
相關代碼如下:(json使用hutool)
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JsonArrayTypeHandler extends BaseTypeHandler {
private static final Log log = LogFactory.get();
// 核心的轉換處理
private JSONArray parse(String json) {
try {
if (json == null || json.length() == 0) {
return null;
}
return JSONUtil.parseArray(json);
}catch (JSONException e){
log.error(json);
log.error(e);
return null;
}
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i,parameter.toString());
}
@Override
public JSONArray getNullableResult(ResultSet rs, String columnName) throws SQLException {
return parse(rs.getString(columnName));
}
@Override
public JSONArray getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return parse(rs.getString(columnIndex));
}
@Override
public JSONArray getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return parse(cs.getString(columnIndex));
}
}