MyBatis枚举映射类讨论

2023-11-17

前言

本篇需要对于MyBatis有一定的认识,而且只是针对于TypeHandler接口来讨论,暂不讨论其他方面的问题

TypeHandler概叙

      TypeHandler是MyBatis设计的一个用于参数的接口,你们会不会很好奇MyBatis是如何把整形,时间,字符串等映射到数据表字段的,实际上就是这个接口来做的,在TypeHandlerRegistry的构造器中初始化了很多处理器,如下

public TypeHandlerRegistry(Configuration configuration) {
    this.unknownTypeHandler = new UnknownTypeHandler(configuration);

    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());

    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());

    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());

    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(JdbcType.INTEGER, new IntegerTypeHandler());

    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());

    register(Float.class, new FloatTypeHandler());
    register(float.class, new FloatTypeHandler());
    register(JdbcType.FLOAT, new FloatTypeHandler());

    register(Double.class, new DoubleTypeHandler());
    register(double.class, new DoubleTypeHandler());
    register(JdbcType.DOUBLE, new DoubleTypeHandler());

    register(Reader.class, new ClobReaderTypeHandler());
    register(String.class, new StringTypeHandler());
    register(String.class, JdbcType.CHAR, new StringTypeHandler());
    register(String.class, JdbcType.CLOB, new ClobTypeHandler());
    register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
    register(JdbcType.CHAR, new StringTypeHandler());
    register(JdbcType.VARCHAR, new StringTypeHandler());
    register(JdbcType.CLOB, new ClobTypeHandler());
    register(JdbcType.LONGVARCHAR, new StringTypeHandler());
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    register(JdbcType.NCHAR, new NStringTypeHandler());
    register(JdbcType.NCLOB, new NClobTypeHandler());

    register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
    register(JdbcType.ARRAY, new ArrayTypeHandler());

    register(BigInteger.class, new BigIntegerTypeHandler());
    register(JdbcType.BIGINT, new LongTypeHandler());

    register(BigDecimal.class, new BigDecimalTypeHandler());
    register(JdbcType.REAL, new BigDecimalTypeHandler());
    register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
    register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

    register(InputStream.class, new BlobInputStreamTypeHandler());
    register(Byte[].class, new ByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
    register(byte[].class, new ByteArrayTypeHandler());
    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.BLOB, new BlobTypeHandler());

    register(Object.class, unknownTypeHandler);
    register(Object.class, JdbcType.OTHER, unknownTypeHandler);
    register(JdbcType.OTHER, unknownTypeHandler);

    register(Date.class, new DateTypeHandler());
    register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
    register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
    register(JdbcType.TIMESTAMP, new DateTypeHandler());
    register(JdbcType.DATE, new DateOnlyTypeHandler());
    register(JdbcType.TIME, new TimeOnlyTypeHandler());

    register(java.sql.Date.class, new SqlDateTypeHandler());
    register(java.sql.Time.class, new SqlTimeTypeHandler());
    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

    register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());

    register(Instant.class, new InstantTypeHandler());
    register(LocalDateTime.class, new LocalDateTimeTypeHandler());
    register(LocalDate.class, new LocalDateTypeHandler());
    register(LocalTime.class, new LocalTimeTypeHandler());
    register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
    register(OffsetTime.class, new OffsetTimeTypeHandler());
    register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
    register(Month.class, new MonthTypeHandler());
    register(Year.class, new YearTypeHandler());
    register(YearMonth.class, new YearMonthTypeHandler());
    register(JapaneseDate.class, new JapaneseDateTypeHandler());

    // issue #273
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler());
  }

所以我们平时不用设计什么就可以进行自动转换,而一种特殊情况下,我们需要自己定义,也就是枚举,这也是我们经常用的技术,比如有以下的对象

public class SysUser {

    /**
     * 用户名
     */
    private String name;

    /**
     * 密码-加密模式
     */
    private String password;
    /**
     * 数据状态,0-正常,1-删除
     */
    private DataStateEnum deleted = DataStateEnum.NORMAL;
}


package com.zxc.movie.common.enums;

/**
 * 数据状态
 */
public enum DataStateEnum implements BaseEnum<DataStateEnum>{

    NORMAL(0, "正常"),
    DELETE(1, "删除"),

    ;

    private final Integer code;
    private final String desc;

    DataStateEnum(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public String getDesc() {
        return desc;
    }
}

            那么我们如何进行转换呢,其实有很多种方式,下面一个个说下

常规方式

  直接继承BaseTypeHandler,然后实现就行,看下面的描述就很简单了

package com.zxc.movie.common.typehandler.common;

import com.zxc.movie.common.enums.BaseEnumUtils;
import com.zxc.movie.common.enums.DataStateEnum;
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;
import java.util.Objects;

/**
* @author zxc_user
* @date 2023/8/7 19:15
* @version 1.0
* @description 数据状态返回
**/
public class DataStateEnumHandler extends BaseTypeHandler<DataStateEnum> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, DataStateEnum parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter.getCode());
    }

    @Override
    public DataStateEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return getByCode(rs.getInt(columnName));
    }

    @Override
    public DataStateEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return getByCode(rs.getInt(columnIndex));
    }

    @Override
    public DataStateEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return getByCode(cs.getInt(columnIndex));
    }

    private DataStateEnum getByCode(Integer code) {
        DataStateEnum[] dataStateEnums = DataStateEnum.values();
        for (DataStateEnum baseEnum : dataStateEnums) {
            if(Objects.equals(baseEnum.getCode(), code)) {
                return baseEnum;
            }
        }
    }
}

这种方式很简单,但是如果有多个枚举类会存在大量的重复代码,所以我们可以把公用逻辑进行抽取,其实只要让枚举有统一的类型,并且获取到所有枚举就行了,那么怎么做呢,需要有以下的步骤

进阶方式      

  首先,要提供一个接口,然后所有枚举都实现这个接口,如下

/**
* @author zxc_user
* @date 2023/8/7 19:01
* @version 1.0
* @description 枚举基础类
**/
public interface BaseEnum<T>{

    Integer getCode();

    String getDesc();
}


/**
 * 数据状态
 */
public enum DataStateEnum implements BaseEnum<DataStateEnum>{

    NORMAL(0, "正常"),
    DELETE(1, "删除"),

    ;

    private final Integer code;
    private final String desc;

    DataStateEnum(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public String getDesc() {
        return desc;
    }
}

这样一来,所有枚举都属于BaseEnum类型

        其次提供了一个公用的TypeHandler处理器BaseEnumHandler,定义如下

package com.zxc.movie.common.typehandler;

import com.zxc.movie.common.enums.BaseEnum;
import com.zxc.movie.common.enums.BaseEnumUtils;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author zxc_user
* @date 2023/8/7 18:52
* @version 1.0
* @description 通用的枚举类解释器
**/
public abstract class BaseEnumHandler<T extends BaseEnum<T>> extends BaseTypeHandler<T> {

    //进一步优化
    protected abstract BaseEnum<T>[] getBaseEnumArray();

      //根据code获取对应的数据
    private BaseEnum<T> getBaseEnum(Integer code) {
        BaseEnum<T>[] baseEnumArray = getBaseEnumArray();
        for (BaseEnum<T> baseEnum : baseEnumArray) {
            if(Objects.equals(baseEnum.getCode(), code)) {
                return baseEnum;
            }
        }
        return null;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter.getCode());
    }

    @Override
    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return (T) getBaseEnum(rs.getInt(columnName));
    }

    @Override
    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return (T) getBaseEnum(rs.getInt(columnIndex));
    }

    @Override
    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return (T) getBaseEnum(cs.getInt(columnIndex));
    }
}

原来的DataStateEnumHandler只需要继承上面的处理器,然后实现方法即可,如下

package com.zxc.movie.common.typehandler.common;

import com.zxc.movie.common.enums.BaseEnum;
import com.zxc.movie.common.enums.DataStateEnum;
import com.zxc.movie.common.typehandler.BaseEnumHandler;

/**
* @author zxc_user
* @date 2023/8/7 19:15
* @version 1.0
* @description 数据状态返回
**/
public class DataStateEnumHandler extends BaseEnumHandler<DataStateEnum> {

    @Override
    protected BaseEnum<DataStateEnum>[] getBaseEnumArray() {
        return DataStateEnum.values();
    }
}

是不是很简单,到这里其实就已经很容易了,但是其实还能进一步优化,回看逻辑,其实我们只要有办法拿到所有枚举的对象就可以处理,那么有没有办法呢,其实是有的,可以通过反射,如下

最终方式

package com.zxc.movie.common.typehandler;

import com.zxc.movie.common.enums.BaseEnum;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Objects;

/**
* @author zxc_user
* @date 2023/8/7 18:52
* @version 1.0
* @description 通用的枚举类解释器
**/
public abstract class BaseEnumHandler<T extends BaseEnum<T>> extends BaseTypeHandler<T> {

    private BaseEnum<T>[] baseArrayEnum;

    public BaseEnumHandler(){
        //获取泛型的类型
        Type genericSuperclass = this.getClass().getGenericSuperclass();
        if (genericSuperclass instanceof ParameterizedType){
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
               //获取枚举的所有对象,因为都实现了BaseEnum接口
                this.baseArrayEnum = (BaseEnum[]) ((Class)actualTypeArgument).getEnumConstants();
            }
        }
    }


    //根据code获取对应的数据
    private BaseEnum<T> getBaseEnum(Integer code) {
        for (BaseEnum<T> baseEnum : baseArrayEnum) {
            if(Objects.equals(baseEnum.getCode(), code)) {
                return baseEnum;
            }
        }
        return null;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter.getCode());
    }

    @Override
    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return (T) getBaseEnum(rs.getInt(columnName));
    }

    @Override
    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return (T) getBaseEnum(rs.getInt(columnIndex));
    }

    @Override
    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return (T) getBaseEnum(cs.getInt(columnIndex));
    }
}

最重要的地方就是构造器里面通过反射获取到枚举所有类型,然后原来的处理器只需要直接继承就行了,如下

package com.zxc.movie.common.typehandler.common;

import com.zxc.movie.common.enums.DataStateEnum;
import com.zxc.movie.common.typehandler.BaseEnumHandler;

/**
* @author zxc_user
* @date 2023/8/7 19:15
* @version 1.0
* @description 数据状态返回
**/
public class DataStateEnumHandler extends BaseEnumHandler<DataStateEnum> {

}

是不是变的很简单?唯一的缺点就是还是需要创建一个转换器,但是转换器已经不需要实现任何内容了,只需要提供一个类就行了

TypeHandler生效

老版本的MyBatis是通过xml文件注册的,这里就不说了,因为现在都是需要和Spring进行集成的,而且还是SpringBoot,所以这里只说SpringBoot的方式

第一种

在application.yml文件中配置如下内容,type-handlers-package为处理类所在包

mybatis-plus:
  type-handlers-package: com.zxc.movie.common.typehandler

第二种

往IOC容器中注入一个Bean,如下,这是MyBatis-PLUS提供的扩展点,是不是很方便?

/**
* @author zxc_user
* @date 2023/8/7 19:17
* @version 1.0
* @description mybatis-plus配置类
**/
@Configuration
public class MyBatisPlusConfig {

    /**
     * 自定义配置,看源码发现的
     * @return
     */
    @Bean
    public ConfigurationCustomizer mybatisConfiguration() {
        return configuration -> {
            //注册解析器
            configuration.getTypeHandlerRegistry().register(DataStateEnumHandler.class);
        };
    }
}

这里顺便说一下,第二种方式的优先度比第一种的要高,这个逻辑需要在源码中查看或者官网介绍,源码逻辑在MybatisPlusAutoConfiguration.applyConfiguration中有所体现

总结

到这里本篇文章就结束了,其实并不复杂,主要是为了统一TypeHandler的处理逻辑,最妙的是通过反射获取的枚举列表方法,如下,虽然不难,不过也可以记录一下

    public BaseEnumHandler(){
        //获取枚举类型
        Type genericSuperclass = this.getClass().getGenericSuperclass();
        if (genericSuperclass instanceof ParameterizedType){
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                this.baseArrayEnum = (BaseEnum[]) ((Class)actualTypeArgument).getEnumConstants();
            }
        }
    }

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MyBatis枚举映射类讨论 的相关文章

随机推荐

  • 浅谈H5的理解

    1 h5广义的概念 描述的是网页技术一次重要的更新迭代 更新的特征包括HTML语言的新特性 CSS的新特性 JavaScript 的新特新 浏览器新特新标准等等 html中新增了语义化标签 表单新增特性 更加简洁的文档声明 媒体标签 vid
  • 【Vue】实现无限滚动加载

    文章目录 法一 ElemntUI的InfiniteScroll 法二 直接操作window窗口的滚轮事件 先提前预告一下 如果需要的是单个确定高度的容器组里进行无限滚动刷新 则使用InfiniteScroll最方便 而如果无限滚动依赖的是整
  • 一目了然的node.js

    node js基础知识入门 1 模块化 导出命令 导入命令 2 文件系统 异步读文件 同步读文件 异步写文件 同步写文件 在fs模块中 提供同步方法是为了方便使用 那我们到底是应该用异步方法还是同步方法呢 3 HTTP模块 模块导入 参数含
  • 团体程序设计天梯赛 -- 练习集 (L1合集)

    文章目录 L1 001 Hello World 5 分 L1 002 打印沙漏 20 分 L1 003 个位数统计 15 分 L1 004 计算摄氏温度 5 分 L1 005 考试座位号 15 分 L1 006 连续因子 20 分 L1 0
  • 免费的 XShell 替代品,我推荐这5款软件,一个比一个香!

    点击上方 Java基基 选择 设为星标 做积极的人 而不是积极废人 每天 14 00 更新文章 每天掉亿点点头发 源码精品专栏 原创 Java 2021 超神之路 很肝 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析 网络应用框
  • C/C++:06. 模板

    文章目录 前言 一 函数模板 二 类模板 三 函数模板重载 总结 前言 C 的模板是被迫推出的 最直接的动力来源于对数据结构的封装 数据结构关乎的是数据的存储 以及存储后如何进行增加 删除 修改和查询操作 在实际开发中有着非常广泛的应用 C
  • UWSGI学习笔记

    uwsgi spooler可以用来实现Cron Task调度和非阻塞Task django相关安装包 uwsgidecorators 1 1 0 uwsgi tasks 0 6 4
  • c语言冒泡法对10个整数由大到小排序,用冒泡法对10个整数排序

    公告 为响应国家净网行动 部分内容已经删除 感谢读者理解 话题 用冒泡法对10个整数排序 10个整数用scanf函数输入回答 举了例 一个数组 3 2 5 1 4从小到大排序从左侧开始 逐对比较32 3 2的位置 数组变为2 3 5 1 4
  • git修改仓库名次之后,本地仓库重定向问题

    在github网页中更改了项目的名次 再次推送的时候报这样的错误fatal repository https xxx git not founds 使用下面的命令将推送的远程仓库重定向 git remote set url origin u
  • 数据压缩与管理:掌握Linux VDO和LVM的力量

    1 逻辑卷 LVM Logical Volume Management 动态的为服务器磁盘添加空间 而不会影响原磁盘的数据 也不需要对原始磁盘重新分区 1 1 LVM介绍 以下是LVM的示意图 我们拿到一块硬盘后首先对齐进行划分分区 也就得
  • [免签约]微信+支付宝个人收款解决方案

    方案原理 使用一台闲置的安卓手机专门用来做收款 收到付款时手机会有通知提示 对该通知进行监控 监控到后发送数据到服务器 服务器根据订单情况支付情况判断是否成功完成一轮下单支付操作 如果成功则自动发货 具体实现流程 网页前端展示商品 用户浏览
  • 华容道html源码,华容道(项目源代码)

    实例简介 Java华容道游戏完整代码 添加了图片与音效 设置了三个关卡 有注释 实例截图 核心代码 华容道 项目源代码 华容道 bin HuaRong About class BackgroundPanel class HuaRong 1
  • 学习PySOT避坑指南

    PySOT是商汤 SenseTime视频智能研究团队 开源的目标跟踪库 实现了最新的单目标跟踪算法 主要包含 SiamRPN SiamMask 使用Python编写的 基于Pytorch深度学习框架 该软件系统还包含了评估跟踪算法的Pyth
  • Java生成6位随机码(大小写+数字)

    char sources new char a b c d e f g h i j k l m n
  • 【Taro】微信小程序隐私协议改造

    微信要求小程序开发者在2023 9 15日前将小程序中调用获取用户隐私api的接口时 都必须要先让用户授权 如果用户拒绝授权 那么小程序的对应接口或组件将直接禁用 那么首先 请将微信小程序开发者工具 详情 本地设置 基础调试库 切换至2 3
  • QT实现用户登录功能

    功能 1 提供登录界面 客户端 2 服务器端用数据库来存储用户名和密码 3 注册时客户端将注册信息发送给服务器端 并进行验证 如果注册名可用 添加进数据库并返回客户端添加成功信息 4 登录时客户端将登录信息发送给服务器端进行验证 服务端返回
  • Ubuntu14.04下配置CGAL+boost+QT+Suitesparse

    这两天突然间想把以前在linux在没有调通的程序给调通 这个程序需要用到CGAL和Suitesparse 稀疏矩阵计算 大家上网查哈 而CGAL又依赖于boost 和QT 所以总共需要安装boost QT suitesparse和CGAL
  • java二维数组的创建,java二维数组创建方法

    java动态创建二维数组 从零学java笔录 第31篇 图解二位数组在内存中存储 java二维数组动态赋值 java二维数组创建方法 二维数组的定义 type arrayName type arrayName Java 二维数组的声明 初始
  • 数据可视化入门学习——Jupyter Notebook 和绘图有关的几个魔术指令

    数据可视化 Jupyter Notebook 和绘图有关的几个魔术指令 matplotlib inline 这是默认的模式 输出的图片是静态的 matplotlib auto 在这个模式下会弹出一个单独 的绘图窗口 和在pycharm中一样
  • MyBatis枚举映射类讨论

    前言 本篇需要对于MyBatis有一定的认识 而且只是针对于TypeHandler接口来讨论 暂不讨论其他方面的问题 TypeHandler概叙 TypeHandler是MyBatis设计的一个用于参数的接口 你们会不会很好奇MyBatis