MybatisPlus使用多字段处理器List带来的问题及对应解决方案

需求背景:将表的某字段类型改成列表。
因映射关系变动,导致字段仍需以字符串类型进行存储。如javatype仍为String,代码层面需要进行适配,一方面需要修改的代码块大大增加,另一方面会大大降低代码语义,影响阅读质量。
MybatisPlus的字段处理器能够很好完成JavaType和jdbc的转换。

问题引出:

数据示例:
image.png
针对多个算法进行提测时,算法id列表以逗号分隔形式的字符串进行保留

@TableName(value = "t_image_test")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ImageTest extends Model<ImageTest> {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 算法ID
     * 在多算法模式提测下,这里可能有多个
     */
    @TableField(typeHandler = ListInt2StringHandler.class)
    private List<Integer> algoId;

    /**
     * 算法版本
     */
    @TableField(typeHandler = ListStr2StringHandler.class)
    private List<String> algoVersion;
   
    // ***省略
}

字段处理器

public class ListInt2StringHandler extends BaseTypeHandler<List<Integer>> {

    private static final String DELIM = ",";

    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, List<Integer> strings, JdbcType jdbcType) throws SQLException {
        String value = StringUtils.collectionToDelimitedString(strings, DELIM);
        preparedStatement.setString(i, value);
    }

    @Override
    public List<Integer> getNullableResult(ResultSet resultSet, String s) throws SQLException {
        String value = resultSet.getString(s);
        return split2List(value);
    }

    @Override
    public List<Integer> getNullableResult(ResultSet resultSet, int i) throws SQLException {
        String value = resultSet.getString(i);
        return split2List(value);
    }

    @Override
    public List<Integer> getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        String value = callableStatement.getString(i);
        return split2List(value);
    }

    private List<Integer> split2List(String value) {
        if (StrUtil.isEmpty(value)) {
            return null;
        }

        String[] split = value.split(DELIM);
        return StreamUtil.of(split).mapToInt(Integer::parseInt).boxed().collect(Collectors.toList());
    }

}

测试代码如下:

@PostMapping("/test")
public R<ImageTest> test() {
    return R.ok(imageTestService.getById(2004));
}

控制台输出:

Caused by: java.lang.NumberFormatException: For input string: "cas_v1.1.0_1"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:580)
	at java.lang.Integer.parseInt(Integer.java:615)
    /*~~~*/
	at com.ev.amp.config.mybatisplus.handler.ListInt2StringHandler.split2List(ListInt2StringHandler.java:55)

由控制台输出日志可知是algoVersion字段引用了错误的处理器导致的类型转换异常。

问题分析

mybatis为什么会选择错了处理器?mybatis又是如何选择处理器的?
接下来根据线程调用栈一步步分析,mybatis到底是如何选择处理器的
image.png
image.png
mybatis通过反射获取当前字段类型,再通过字段类型从处理器注册器中获取对应的字段处理器(注册器就是一个map,key为字段类型,value为具体的处理器)。
image.png
那处理器是如何进行注册的?mybatis进行加载上下文配置时,扫描所有Handler并注册进来。由于List和List类类型相同(泛型擦除),后面加载的处理器将之前的覆盖掉了,导致使用该类型的所有的字段都使用了该处理器。

解决方案

上述使用自动映射(resultType)就必然选择不了正确的处理器,可以试试使用手动映射方式(使用resultMap)
1.实体类添加autoResultMap
image.png
mapper.xml文件添加对应处理器

    <resultMap id="BaseResultMap" type="com.ev.amp.plant.infra.po.ImageTest">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="algo_id" property="algoId"  typeHandler="com.ev.amp.config.mybatisplus.handler.ListInt2StringHandler"/>
        <result column="sdk_id" property="sdkId" />
        <result column="algo_server_id" property="algoServerId" />
        <result column="algo_version" property="algoVersion" typeHandler="com.ev.amp.config.mybatisplus.handler.ListStr2StringHandler" />
    </resultMap>

image.png
控制台输出
image.png
另一方面,我们可以创建一个子类继承集合List,不同字段使用不同集合类处理也可以解决如上问题。

  • 27
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值