Mybatis-Plus自定义集合类型的类型处理器详解

目录
  • 1.配合xml文件
  • 2.手动注册

两种方法,第一种很麻烦,对mp自带的插入操作有限制,后来改为更简洁的第二种方法

1.配合xml文件

TypeHandler

/**
 * 描述:fastjson的集合对象类型处理器,将mysql表中的json字段映射到实体类中的{@code List<?>}属性
 * 对照MP自带的FastjsonTypeHandler,自带的类型处理器会把所有的{@code List<?>}都会解析为{@code List<JsonObject>},
 * 这样在遍历其中对象时,调用对象属性的get、set方法就会发送类型转换JsonObject->?,这种情况转换错误。
 * 该处理器必须配合xml文件使用,不然无法获取要解析的对象类型,同时不能配合MP自带的{@link com.baomidou.mybatisplus.annotation.TableField}
 * 使用,默认情况下@TableField会将JavaType设置为字段的类型,如果是List<?>则type = List.class,无法明确其中的泛型,类型转换会变成JsonObject。
 * 用法:
 * <pre>{@code
 * 1.实体类上使用注解@TableName(value = "表名", resultMap = "xml文件中的resultMap的id")
 * 2.xml文件中自定义resultMap并设置需要JSON转换的字段
 * <result property="实体类属性名" column="表字段名" javaType="List中的对象全类名,例如List<Email>,则JavaType为Email的全类名" typeHandler="自定义处理器全类名"/>
 * 3.自定义方法上的用法
 * @Mapper
 * public interface DemoDao extends BaseMapper<DemoEntity> {
 *    @Select("select * from demo where demo_id = #{demoId}")
 *    @ResultMap(value = "xml文件中的resultMap的id")
 *    List<DemoEntity> selectListByDemoId(Long demoId);
 * }
 * }</pre>
 */
@Slf4j
@MappedJdbcTypes(value = JdbcType.VARCHAR)
public class FastJsonArrayTypeHandler extends AbstractJsonTypeHandler<List<?>> { 
    private Class<?> type; 
    public FastJsonArrayTypeHandler(Class<?> type) {
        if (log.isTraceEnabled()) {
            log.trace("FastjsonTypeHandler(" + type + ")");
        }
        Assert.notNull(type, "Type argument cannot be null");
        this.type = type;
    }
 
    @Override
    protected List<?> parse(String json) {
        return JSON.parseArray(json, type);// 注意不要使用parseObject方法
    }
 
    @Override
    protected String toJson(List<?> obj) {
        return JSON.toJSONString(obj, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty);
    } 
}

xml文件

<?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="a.b.dao.DemoDao">
    <resultMap type="a.b.entity.DemoEntity" id="demoMap">
        <result property="DemoInfo" column="demo_info" javaType="a.b.xx.DiseaseInfo" typeHandler="a.b.handler.FastJsonArrayTypeHandler"/>
    </resultMap>
</mapper>

Dao

@Mapper
public interface DemoDao extends BaseMapper<DemoEntity> {
    @Select("select * from demo where demo_id = #{demoId}")
    @ResultMap(value = "demoMap")
    List<DemoEntity> selectListByPlanId(Long demoId);
}

Entity

@Data
@TableName(value = "demo", resultMap = "demoMap")
public class DemoEntity implements Serializable {
    private static final long serialVersionUID = -1L;
 
    /**
     * 主键
     */
    @TableId
    private Long id;
    private Long demoId
    /**
     * json字段
     */
    private List<DemoInfo> demoInfo;
}

2.手动注册

TypeHandler

@Slf4j
@MappedJdbcTypes(value = JdbcType.VARCHAR)
public class FastJsonArrayTypeHandler<T> extends AbstractJsonTypeHandler<Object> { 
    private TypeReference<List<T>> type; 
    public FastJsonArrayTypeHandler(TypeReference<List<T>> type) {
        this.type = type;
    }
 
    @Override
    protected Object parse(String json) {
        return JSON.parseObject(json, type);
    }
 
    @Override
    protected String toJson(Object obj) {
        return JSON.toJSONString(obj, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty);
    } 
}

初始化,剩下的bean和dao都不需要额外配置

/**
 * 描述:初始化配置
 * 手动注册类型处理器
 */
@Component
public class InitConfig implements CommandLineRunner {
    public static final com.alibaba.fastjson.TypeReference<List<DemoInfo>> F_DEMO_INFO = new com.alibaba.fastjson.TypeReference<List<DemoInfo>>() {
    };
    public static final TypeReference<List<DemoInfo>> M_DEMO_INFO = new TypeReference<List<DemoInfo>>() {
    };
    // mp自动装配时注入的factory,可用于获取mybatis的配置属性,这里用来获取类型处理器的注册器
    private final SqlSessionFactory factory;
 
    public InitConfig(SqlSessionFactory factory) {
        this.factory = factory;
    }
 
    /**
     * 注册类型处理器
     * <pre>{@code
     * 1.List<DemoInfo>类型处理器
     * ...
     * }</pre>
     *
     * @param args incoming main method arguments
     * @throws Exception on error
     */
    @SuppressWarnings("all")
    @Override
    public void run(String... args) throws Exception {
        TypeHandlerRegistry typeHandlerRegistry = factory.getConfiguration().getTypeHandlerRegistry();
        typeHandlerRegistry.register(M_DEMO_INFO, new FastJsonArrayTypeHandler(F_DEMO_INFO));
    }
}

目前方法二存在的缺陷:虽然新增、查询不存在问题,执行MP自带的更新操作时,parameterMap参数类型都是Object,不会经过自定义的TypeHandler处理,最后会把json对象直接set进去(update demo ..., demo_info = JSON对象 ...)导致报错

暂无优雅的解决方案,先做个记录 。以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Mybatis实现自定义类型转换器TypeHandler的方法

    先给大家简单介绍下mybatis MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录. 此文其实是java操作Oracle类型XMLType总结二:使用Mybatis附带的一篇小结. Mybatis实现自定义的转换器,

  • Mybatis自定义类型转换器的使用技巧

    一般情况下,mybatis自带的类型转换器已经足够用了,但是需求永无止境,自定义类型转换器还是学一下吧,指不定哪天会用上. 举个没啥用的示例 javabean如下: public class User { private Integer id; private Message message; } public class Message { private Integer id; private String address; private String job; } 数据库User表如下:

  • mybatis自定义类型处理器TypehHandler示例详解

    前言 当大家使用mybatis作为持久层框架时,在存储和查询数据时,只需要在mapper.xml文件中配置好对应字段的JdbcType和JavaType,mybatis就可以帮我们转化对应的类型.这背后是有mybatis内置的类型转换器做转换(可见源码TypeHandlerRegistry).但是有时候,我们会对某些字段做特殊处理,比如加密和解密.状态转换.类型转换等.这个时候我们需要自定义类型转换器. 类架构 从上面的图中可以看出MyBatis中整个类型处理器实现架构,TypeHandler接

  • mybatis-plus拦截器、字段填充器、类型处理器、表名替换、SqlInjector(联合主键处理)

    目录 组件介绍 表名处理器 字段填充器 类型处理器 补充 最近有个练手的小例子,大概就是配置两个数据源,从一个数据源读取数据写到另一个数据源,虽然最后做了出来,但是不支持事务...就当是对mybatis-plus/mybatis组件使用方式的记录吧,本次例子使用的仍是mybatis-plus 回忆一下mybatis核心对象: Configuration 初始化基础配置,比如MyBatis的别名等,一些重要的类型对象,如,插件,映射器,ObjectFactory和typeHandler对象,MyB

  • Python对象类型及其运算方法(详解)

    基本要点: 程序中储存的所有数据都是对象(可变对象:值可以修改 不可变对象:值不可修改) 每个对象都有一个身份.一个类型.一个值 例: >>> a1 = 'abc' >>> type(a1) str 创建一个字符串对象,其身份是指向它在内存中所处的指针(在内存中的位置) a1就是引用这个具体位置的名称 使用type()函数查看其类型 其值就是'abc' 自定义类型使用class 对象的类型用于描述对象的内部表示及其支持的方法和操作 创建特定类型的对象,也将该对象称为该类

  • 深度思考JDK8中日期类型该如何使用详解

    在JDK8之前,处理日期时间,我们主要使用3个类, Date . SimpleDateFormat 和 Calendar . 这3个类在使用时都或多或少的存在一些问题,比如 SimpleDateFormat 不是线程安全的, 比如 Date 和 Calendar 获取到的月份是0到11,而不是现实生活中的1到12,关于这一点,<阿里巴巴Java开发手册>中也有提及,因为很容易犯错: 不过,JDK8推出了全新的日期时间处理类解决了这些问题,比如 Instant . LocalDate . Loc

  • Java的类型擦除式泛型详解

    Java选择的泛型类型叫做类型擦除式泛型.什么是类型擦除式泛型呢?就是Java语言中的泛型只存在于程序源码之中,在编译后的字节码文件里,则全部泛型都会被替换为原来的原始类型(Raw Type),并且会在相应的地方插入强制转型的代码. 因此,对于运行期间的Java程序来说ArrayList< Integer>和ArrayList< String>其实是同一个类型.这也就是Java选择的泛型类型叫做类型擦除式泛型的原因. ArrayList<String> stringAr

  • TS 类型兼容教程示例详解

    目录 类型兼容 简单类型兼容 普通对象兼容 函数兼容 参数数量不一致 参数类型不一致 返回不同 类型兼容 因为JS语言不慎过于领过, 真实开发场景中往往无法做到严格一致的类型约束,此时TS就不得不做类型兼容 顶类型:unknown -- 任何类型都可以赋值给unknown 底类型:never -- never兼容任何类型(可以赋值给任何类型) any: 其实不是一个类型,它是一个错误关闭器,用了any就等同于放弃了类型约束 简单类型兼容 子集可以赋值给父级 type name = string

  • C/C++ ip地址与int类型的转换实例详解

    C/C++ ip地址与int类型的转换实例详解 前言 最近看道一个面试题目,大体意思就是将ip地址,例如"192.168.1.116"转换成int类型,同时还能在转换回去 思路 ip地址转int类型,例如ip为"192.168.1.116",相当于"."将ip地址分为了4部分,各部分对应的权值为256^3, 256^2, 256, 1,相成即可 int类型转ip地址,思路类似,除以权值即可,但是有部分字符串的操作 实现代码 #include &l

  • Java的静态类型检查示例代码详解

    关于静态类型检查和动态类型检查的解释: 静态类型检查:基于程序的源代码来验证类型安全的过程: 动态类型检查:在程序运行期间验证类型安全的过程: Java使用静态类型检查在编译期间分析程序,确保没有类型错误.基本的思想是不要让类型错误在运行期间发生. 在各色各样的编程语言中,总共存在着两个类型检查机制:静态类型检查和动态类型检查. 静态类型检查是指通过对应用程序的源码进行分析,在编译期间就保证程序的类型安全. 动态类型检查是在程序的运行过程中,验证程序的类型安全.在Java中,编译期间使用静态类型

  • 对python xlrd读取datetime类型数据的方法详解

    使用xlrd读取出来的时间字段是类似41410.5083333的浮点数,在使用时需要转换成对应的datetime类型,下面代码是转换的方法: 首先需要引入xldate_as_tuple函数 from xlrd import xldate_as_tuple 使用方法如下: #d是从excel中读取出来的浮点数 xldate_as_tuple(d,0) xldate_as_tuple第二个参数有两种取值,0或者1,0是以1900-01-01为基准的日期,而1是1904-01-01为基准的日期.该函数

  • java 使用idea将工程打成jar并创建成exe文件类型执行的方法详解

    第一部分: 使用idea 打包工程jar 1.准备好一份 开发好的 可执行的 含有main方法的 工程. 例如:我随便写的main方法 public static void main(String[] args) throws IOException { Properties properties = System.getProperties(); String osName = properties.getProperty("os.name"); System.out.println

  • Java 处理超大数类型之BigInteger案例详解

    一.BigInteger介绍 如果在操作的时候一个整型数据已经超过了整数的最大类型长度 long 的话,则此数据就无法装入,所以,此时要使用 BigInteger 类进行操作.这些大数都会以字符串的形式传入. BigInteger 相比 Integer 的确可以用 big 来形容.它是用于科学计算,Integer 只能容纳一个 int,所以,最大值也就是 2 的 31 次访减去 1,十进制为 2147483647.但是,如果需要计算更大的数,31 位显然是不够用的,那么,此时 BigIntege

  • c#中String类型的存储原理详解

    在我们正式了解c#中的String类型前,先来判断一下下面代码的结果吧~ String str1 = "123"; String str2 = str1; str2 = "321"; Console.WriteLine(str1); 上面代码的最终输出结果是123,如果有浅学过引用类型的同学一定会问:str2不是在存储的是str1的引用么?那么str2不是和str1指向堆中同一块内存空间么?为什么在引用了str2使其改变数据后再打印出str1最终还是打印出来123?

随机推荐