mybatis分页与collection的冲突解决

Posted 常烦常乐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis分页与collection的冲突解决相关的知识,希望对你有一定的参考价值。

背景

某个查询,是流水-发券类的查询,查询流水的同时,想要取得关联的卡券,关系是一对多的关系。希望返回给前端的结果如下:

{
  "orderId":123,
  "num":1,
  "cardList": [
    {
	   "id":0001,
	   "cardCode":ASDAS15QWE,
	}
  ]
}

问题点:

  1. 由于是订单流水,考虑数量比较大的情况,所以必须使用分页,且是使用服务器端的分页。
  2. 为了便于前端展示,最好使用[]的数组形式进行显示

原本的处理(存在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));
    }
}

以上是关于mybatis分页与collection的冲突解决的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot-CRUD+分页与网页灰化(附加一个好玩的小玩意)

SQL中分页与distinct冲突解决方案

ES深度分页与批量操作

bootstrap分页与翻页

ES实战ES分页与去重

ES实战ES分页与去重