• 需求
    mybatis目前已经内嵌入了springboot中了,这说明其目前在数据访问层的绝对优势。而我们在开发的过程中,往往会在程序中使用枚举(enum) 来表示一些状态或选项,而在数据库中使用数字来存储。这样做的好处是在程序中使用enum更直观的可以知道每个值代表的状态及含义,还可以做国际化的功能。那么这样会带来一个问题那就是:程序中的枚举 与 数据库中的数字 转换问题。
  • 介绍
    抱歉,最近因为实在太忙,所以写一半就停了。等有空继续。不将就哈。停了大概一周的时间,在周一的早上继续来完成这篇文章。

    无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成 Java 类型。Mybatis默认为我们实现了许多TypeHandler, 当我们没有配置指定TypeHandler时,Mybatis会根据参数或者返回结果的不同,默认为我们选择合适的TypeHandler处理。

    对于enum而言,在mybatis中已经存在了EnumTypeHandler和EnumOrdinalTypeHandler两大处理器,他们都是继承自BaseTypeHandler处理器,那为何不能使用mybatis自定义的enum处理器而是要自己再定义枚举处理器呢?下面为您一步一步剖析。

  • 分析
    首先说说EnumTypeHandler与EnumOrdinalTypeHandler两者之间的区别吧:
    EnumTypeHandler存入数据库的是枚举的name,EnumOrdinalTypeHandler存入数据库的是枚举的位置。例如下方的枚举,当我们有一个枚举值是EStatus.init时,这时我们使用mybatis EnumTypeHandler存入数据库的是"init"字符串;而EnumOrdinalTypeHandler存入的是3,因为init是第四个值,第一个值disable的index是0。

    public enum EStatus {disable("0"), enable("1"), deleted("2"),init("10"), start("11"), wait("12"), end("13");}

    上面是一个简单的枚举。它包含了7个状态值,当我们没有设置枚举处理器时,mybatis默认使用EnumTypeHandler作为缺省处理器,当需要将枚举存入数据库时它调用的是枚举的name()方法从而获取的是像:disable,enable这样的值存入了数据库,并不是我们想要的(value)value存入数据库,可以看看源码:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //package org.apache.ibatis.type;import java.sql.CallableStatement;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;public class EnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {private Class<E> type;public EnumTypeHandler(Class<E> type) {if(type == null) {throw new IllegalArgumentException("Type argument cannot be null");} else {this.type = type;}}public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {if(jdbcType == null) {ps.setString(i, parameter.name());} else {ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE);}}public E getNullableResult(ResultSet rs, String columnName) throws SQLException {String s = rs.getString(columnName);return s == null?null:Enum.valueOf(this.type, s);}public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {String s = rs.getString(columnIndex);return s == null?null:Enum.valueOf(this.type, s);}public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {String s = cs.getString(columnIndex);return s == null?null:Enum.valueOf(this.type, s);}
    }

    大家可以看到setNonNullParameter方法中ps.setString(i, parameter.name());调用的确实是name()方法。
    而在将数据库字段转java Bean字段的时候使用的方法getNullableResult中都是调用Enum.valueOf(this.type, s),而这个方法底层调用的是get(name)方法。
    所以EnumTypeHandler对我们来说并不适用,因为它在java Bean转数据库数据时获取的是枚举的name而不是value。
    而EnumOrdinalTypeHandler存入数据库的是参数的位置,所以这两个处理器都不是我们想要的。但是我们可以根据EnumTypeHandler的设计思想转变一下,变成符合我们想要的统一处理器。

  • 教程
    首先我们定义一个BaseEnumTypeHandler继承BaseTypeHandler,并实现抽象方法。

    package com.cmc.base.handler;import com.cmc.base.enums.EStatus;
    import com.cmc.base.utils.EnumUtil;
    import org.apache.ibatis.type.BaseTypeHandler;
    import org.apache.ibatis.type.JdbcType;
    import org.apache.ibatis.type.MappedTypes;import java.sql.CallableStatement;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;public class BaseEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {private Class<E> type;public BaseEnumTypeHandler() {}public BaseEnumTypeHandler(Class<E> type) {this.type = type;}@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {if (jdbcType == null) {ps.setString(i, parameter.toString());} else {ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE);}}@Overridepublic E getNullableResult(ResultSet rs, String columnName) throws SQLException {return get(rs.getString(columnName));}@Overridepublic E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return get(rs.getString(columnIndex));}@Overridepublic E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return get(cs.getString(columnIndex));}private <E extends Enum<E>> E get(String v) {if (v == null) return null;if (StringUtils.isNumeric(v)) {return get(type, Integer.parseInt(v));} else {return Enum.valueOf(type, v);}}private <E extends Enum<E>> E get(Class<E> type, int v) {Method method = null;E result = null;try {method = type.getMethod("get", int.class);result = (E)method.invoke(type, v);} catch (NoSuchMethodException e) {result = Enum.valueOf(type, String.valueOf(v));e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return result;}
    }

    在上面的方法setNonNullParameter中大家可以看到我使用parameter.toString()替换了原来的parameter.name()方法,其实做到能统一处理枚举的关键就是这里,因为toString方法是Object的方法,所以不管传入的类型是什么都适用。而从数据库中取出的数据转Bean我使用的是和默认的处理器一致的方法get()。

    所以我们需要在枚举中定义toString和get方法

    package com.cmc.base.enums;/*** 状态枚举* @author chenmc**/
    public enum EStatus {disable("0"), enable("1"), deleted("2"),init("10"), start("11"), wait("12"), end("13");private final String value;private EStatus(String v) {this.value = v;}public String toString() {return this.value;}public static EStatus get(int v) {String str = String.valueOf(v);return get(str);}public static EStatus get(String str) {for (EStatus e : values()) {if(e.toString().equals(str)) {return e;}}return null;}/*public static String getName(EStatus e, Locale locale) {return I18N.getEnumName(e, locale);}*/
    }

    大家可看到toString方法返回的是当前对象的value值,这样就是我们想要的了。
    既然方法都写好了,那下面给大家讲讲如何在springboot中使用。
    1.在application.properties或者application.yml中添加

    mybatis:mapperLocations: classpath:mapper/*.xmltypeAliasesPackage: com.cmc.schedule.model.entitytypeHandlersPackage: com.cmc.schedule.model.handler

    typeHandlersPackage就是你自定义handler的包名,上面我们定义了一个BaseEnumTypeHandler,往往我们不太会在基类中修改或添加,因为基类可能是多个模块公用的,这时在我们的项目中定义一个EnumTypeHandler去继承我们的BaseEnumTypeHandler。

    package com.cmc.schedule.model.handler;import com.cmc.base.enums.EStatus;
    import com.cmc.base.handler.BaseEnumTypeHandler;
    import com.cmc.schedule.model.enums.EFeedback;
    import org.apache.ibatis.type.MappedTypes;/*** @author chenmc*/@MappedTypes(value = {  EFeedback.class, EStatus.class})
    public class EnumTypeHandler<E extends Enum<E>> extends BaseEnumTypeHandler<E> {public EnumTypeHandler(Class<E> type) {super(type);}
    }

    这样有多少个enum需要转换就可以在@MappedTypes注解中添加。是不是很方便了。

  • 结语
    希望可以帮助到大家,如果你有任何疑问都可以在下面留言,我会每天回复你。

SpringBoot Mybatis EnumTypeHandler自定义统一处理器相关推荐

  1. springboot+mybatis集成自定义缓存ehcache用法笔记

    今天小编给大家整理了springboot+mybatis集成自定义缓存ehcache用法笔记,希望对大家能有所办帮助! 一.ehcache介绍 EhCache 是一个纯Java的进程内缓存管理框架,属 ...

  2. 学习Spring Boot:(十二)Mybatis 中自定义枚举转换器

    前言 在 Spring Boot 中使用 Mybatis 中遇到了字段为枚举类型,数据库存储的是枚举的值,发现它不能自动装载. 解决 内置枚举转换器 MyBatis内置了两个枚举转换器分别是:org. ...

  3. MyBatis自定义类型处理器 TypeHandler

    在项目开发中经常会遇到一个问题: 当我们在javabean中自定义了枚举类型或者其它某个类型,但是在数据库中存储时往往需要转换成数据库对应的类型,并且在从数据库中取出来时也需要将数据库类型转换为jav ...

  4. MyBatis核心源码剖析(SqlSession XML解析 Mapper executor SQL执行过程 自定义类型处理器 缓存 日志)

    MyBatis核心源码剖析 MyBatis核心源码剖析 1 MyBatis源码概述 1.1 为什么要看MyBatis框架的源码 1.2 如何深入学习MyBatis源码 1.3 源码分析的5大原则 2 ...

  5. springboot项目mybatis日志自定义设置无法生效

    springboot项目mybatis日志自定义设置无法生效,就是无法设置日志级别,无法对java.sql.PreparedStatement.java.sql.Connection等进行设置. 翻了 ...

  6. springboot自定义参数处理器和返回值处理器

    参数处理器(ArgumentResolvers)和返回参数处理器(ReturnValueHandlers) 在我们调用controller层组件时,Springboot实际上是使用代理模式进行调用,s ...

  7. MyBatis自定义类型处理器(typeHandler)

    MyBatis自定义类型处理器(typeHandler) 我们执行sql语句通过PreparedStatement语句实现,PreparedStatement会设置?值,类型处理器帮PreparedS ...

  8. 在mybatis里如何自定义类型处理器

    类型处理器(typeHandlers) MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Jav ...

  9. 自定义类型处理器的应用

    问题描述: 一个JSON字符串在转对象的时候报JSON解析异常的错误,我仔细看了一下错误堆栈,是枚举导致的数组越界问题. [{"fee":0,"amount": ...

最新文章

  1. python格式化输出print()
  2. 二十、深入Python迭代器和生成器
  3. 【ES】ES 拼音 Pinyin 分词器
  4. [OpenS-CAD]屏幕坐标转换分析
  5. 数字图像处理复习记录(二)邻接、连通和形态学处理
  6. 雷锋科普:小米M2之芯高通APQ8064芯片组解析
  7. win10安装secureCRT8.1.4破解
  8. 苹果中国官网新增蚂蚁花呗 24 期分期免息服务
  9. php调用微信公众号支付接口,Thinkphp实现微信公众号支付接口
  10. 系统架构师—软件架构设计(二)CS/BS/SOA/DSSA/ABSD
  11. python maketrans函数_maketrans函数
  12. 跨境爬虫:跨境电商可以利用爬虫做什么
  13. URAL 1671 Anansi's Cobweb (并查集)
  14. libpqxx 库安装configure: error:Linking a call to libpq failed in C++, even though it succeeded in C.
  15. HDU多校第三场6608 Fansblog(米勒罗宾+威尔逊定理)
  16. 黑苹果传统BIOS引导安装
  17. 《数学建模算法与应用第二版》——chapter3. 非线性规划
  18. 有哪些你看过五遍以上的电影?
  19. 最小系统板 STM32入门,点亮 LED 灯(STM32F103C6T6)
  20. 第二期:百度AI开发者大会;蚂蚁金服投资雪球|互联网行业公会

热门文章

  1. leetcode刷题 60 61
  2. 从2019年-2021年的各大顶会论文,看动态神经网络的发展
  3. Python 和 C/C++ 拓展程序的性能优化
  4. POJ1185 炮兵阵地 状压DP
  5. 【教程】Jupyter notebook基本使用教程
  6. 【多线程高并发】jcstress并发测试工具使用教程详解
  7. LeetCode 两数之和
  8. redis的基础命令操作
  9. 功能测试——Selenium自动化功能测试
  10. 牛客题霸 SQL1 查找最晚入职员工的所有信息