自定义typehandler配合mybatis,实现自动json化存储数据库,查询时自动解析为正确类型

需求背景:

做项目时,有一些业务数据,需要存储到数据库,它的类型是数组,即一个对象对应多条数据,使用关联表太麻烦了,而且维护起来也不直观,于是就像能不能直接将List,json化之后把json字符串存到数据库,然后查询的时候,再自动映射回来

例如:List<String> →  ["PAUSE", "danger", "Lock"] 

由于我有很多种类型的数据都打算使用这个方式存储,期间花费大量的时间经过大量的尝试,发现在parse的过程中,typeHandler是无法自动获取目标类型的,对于自定义类型和特殊情况,需要针对性处理。

自定义一个JsonTypeHandler继承AbstractJsonTypeHandler:

import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonTypeHandler<T> extends AbstractJsonTypeHandler<T> {

    private static final ObjectMapper objectMapper = new ObjectMapper();
    private TypeReference<T> typeReference;

    // 无参构造函数
    public JsonTypeHandler() {
        // 默认构造函数,初始化为一个通用的类型
        this.typeReference = new TypeReference<T>() {};
    }

    // 带参构造函数,接受 TypeReference
    public JsonTypeHandler(TypeReference<T> typeReference) {
        this.typeReference = typeReference;
    }

    @Override
    protected T parse(String json) {
        try {
            if (typeReference == null) {
                throw new IllegalStateException("TypeReference is not set. Please provide a TypeReference.");
            }
            return objectMapper.readValue(json, typeReference);
        } catch (Exception e) {
            throw new RuntimeException("Failed to parse JSON", e);
        }
    }

    @Override
    protected String toJson(T obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (Exception e) {
            throw new RuntimeException("Failed to convert to JSON", e);
        }
    }
}

对应的实体类使用,注解中需要指定typeHandler :

    @TableField(value = "school_list", typeHandler = JsonTypeHandler.class)
    private List<String> schoolList;

该实体类的头部注解指定表名时,还需要指定 autoResultMap = true:

    @TableName(value = "t_my_table", autoResultMap = true)
    public class MyClass

使用时,遇到第一个问题:

由于我的数据库主键是Long类型,这是考虑到后期数据增长,Integer肯定存不下,于是使用了Long,但是前期由于数据量并不大,主键的值很小,所以在反序列化时,将类似:

[123,456] 的数据,反序列化为了List<Integer>类型,这自然和实体中定义的List<Long>对应不上,所以报错了。

解决方案

需要另外单独写一个自定义typeHandler来继承该JsonTypeHandler,并在定义时就指定类型:

import com.fasterxml.jackson.core.type.TypeReference;

//继承时就指定了类型
public class LongListTypeHandler extends JsonTypeHandler<List<Long>> {

    // 无参构造函数,用于 MyBatis 实例化
    public LongListTypeHandler() {
        super(new TypeReference<>() {});
    }
}

然后,对应使用的地方,由 JsonTypeHandler 改为新的 LongListTypeHandler:

    @TableField(value = "team_member_id_list", typeHandler = LongListTypeHandler.class)
    private List<Long> teamMemberIdList;

第二个问题,对于自定义Object类型无法转换

这是我自定义的一个类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ReceiveInfo {
  
    private String alipayCode;
 
    private String wechatCode;

    private String alipayQrCode;

    private String wechatQrCode;
}

运行也报错了

解决方案

也需要新增一个针对性的typeHandler:

public class ReceiveInfoTypeHandler extends JsonTypeHandler<ReceiveInfo> {
    public ReceiveInfoTypeHandler() {
        super(new TypeReference<>() {
        });
    }
}

以此类推,对于更多层List嵌套,或者转换时,无法识别的情况,需要自己新增一个对应的TypeHandler,指定继承类型即可,使用该特定TypeHandler。

再例如:

@TableField(value = "boss_list", typeHandler = MyClassListTypeHandler.class)
private List<List<MyClass>> bossList = new ArrayList<>();

这里的复杂类型: List<List<BossPlayerDTO>> bossList ,需要自定义typeHandler:

public class MyClassListTypeHandler extends JsonTypeHandler<List<List<MyClass>>> {
    public MyClassListTypeHandler() {
        super(new TypeReference<>() {
        }); 
    }
}

到这里,如果你的查询是通过mybatis-plus自带的list,page,queryWrapper等方式查询,已经能够正常使用了,但是碰到复杂查询,需要自己在mapper.xml中写的SQL进行查询,那还需要对结果集中的字段指定typeHandler进行处理,否则查询到的该字段会为空。

例如:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

    <mapper namespace="com.codawave.pve.mapper.TeamScheduleMapper">


    <resultMap id="TeamSchedulerResultMap" type="com.codawave.pve.model.entity.pve.TeamSchedule">
        <!-- ID字段 -->
        <id column="id" property="id"/>
        <!-- 仅对需要自定义TypeHandler的字段进行明确配置 -->
        <result column="scheduled_player_id_list" property="scheduledPlayerIdList"
                typeHandler="com.codawave.pve.handlers.custom.LongListTypeHandler"
                jdbcType="VARCHAR"/>
        <!-- enum类型字段,没有可不写 -->
        <result column="status" property="status" javaType="com.codawave.pve.enums.TeamScheduleStatus"/>
    </resultMap>


    <select id="getDefaultTeamScheduler" resultMap="TeamSchedulerResultMap">
        SELECT *
        FROM pve_team_schedule
        WHERE team_id = #{teamId}
        ORDER BY CASE
                     WHEN status = 'WAITING' THEN 1
                     WHEN status = 'LOCKED' THEN 2
                     WHEN status = 'SETTLED' THEN 3
                     ELSE 4
                     END,
                 preset_time DESC
        LIMIT 1
    </select>


</mapper>

如果对你有帮助,还请点赞支持!

如果使用过程中遇到问题,欢迎评论提出,可以帮助解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风间琉璃c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值