如何在MyBatis中优雅的使用枚举

From: https://segmentfault.com/a/1190000010755321

问题

在编码过程中,经常会遇到用某个数值来表示某种状态、类型或者阶段的情况,比如有这样一个枚举:

public enum ComputerState {OPEN(10),         //开启CLOSE(11),         //关闭OFF_LINE(12),     //离线FAULT(200),     //故障UNKNOWN(255);     //未知private int code;ComputerState(int code) { this.code = code; }
}

通常我们希望将表示状态的数值存入数据库,即ComputerState.OPEN存入数据库取值为10

探索

首先,我们先看看MyBatis是否能够满足我们的需求。
MyBatis内置了两个枚举转换器分别是:org.apache.ibatis.type.EnumTypeHandlerorg.apache.ibatis.type.EnumOrdinalTypeHandler

EnumTypeHandler

这是默认的枚举转换器,该转换器将枚举实例转换为实例名称的字符串,即将ComputerState.OPEN转换OPEN

EnumOrdinalTypeHandler

顾名思义这个转换器将枚举实例的ordinal属性作为取值,即ComputerState.OPEN转换为0,ComputerState.CLOSE转换为1
使用它的方式是在MyBatis配置文件中定义:

<typeHandlers><typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.example.entity.enums.ComputerState"/>
</typeHandlers>

以上的两种转换器都不能满足我们的需求,所以看起来要自己编写一个转换器了。

方案

MyBatis提供了org.apache.ibatis.type.BaseTypeHandler类用于我们自己扩展类型转换器,上面的EnumTypeHandlerEnumOrdinalTypeHandler也都实现了这个接口。

1. 定义接口

我们需要一个接口来确定某部分枚举类的行为。如下:

public interface BaseCodeEnum {int getCode();
}

该接口只有一个返回编码的方法,返回值将被存入数据库。

2. 改造枚举

就拿上面的ComputerState来实现BaseCodeEnum接口:

public enum ComputerState implements BaseCodeEnum{OPEN(10),         //开启CLOSE(11),         //关闭OFF_LINE(12),     //离线FAULT(200),     //故障UNKNOWN(255);     //未知private int code;ComputerState(int code) { this.code = code; }@Overridepublic int getCode() { return this.code; }
}

3. 编写一个转换工具类

现在我们能顺利的将枚举转换为某个数值了,还需要一个工具将数值转换为枚举实例。

public class CodeEnumUtil {public static <E extends Enum<?> & BaseCodeEnum> E codeOf(Class<E> enumClass, int code) {E[] enumConstants = enumClass.getEnumConstants();for (E e : enumConstants) {if (e.getCode() == code)return e;}return null;}
}

4. 自定义类型转换器

准备工作做的差不多了,是时候开始编写转换器了。
BaseTypeHandler<T> 一共需要实现4个方法:

  1. void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)

用于定义设置参数时,该如何把Java类型的参数转换为对应的数据库类型

  1. T getNullableResult(ResultSet rs, String columnName)

用于定义通过字段名称获取字段数据时,如何把数据库类型转换为对应的Java类型

  1. T getNullableResult(ResultSet rs, int columnIndex)

用于定义通过字段索引获取字段数据时,如何把数据库类型转换为对应的Java类型

  1. T getNullableResult(CallableStatement cs, int columnIndex)

用定义调用存储过程后,如何把数据库类型转换为对应的Java类型

我是这样实现的:

public class CodeEnumTypeHandler<E extends Enum<?> & BaseCodeEnum> extends BaseTypeHandler<BaseCodeEnum> {private Class<E> type;public CodeEnumTypeHandler(Class<E> type) {if (type == null) {throw new IllegalArgumentException("Type argument cannot be null");}this.type = type;}@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, BaseCodeEnum parameter, JdbcType jdbcType)throws SQLException {ps.setInt(i, parameter.getCode());}@Overridepublic E getNullableResult(ResultSet rs, String columnName) throws SQLException {int code = rs.getInt(columnName);return rs.wasNull() ? null : codeOf(code);}@Overridepublic E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {int code = rs.getInt(columnIndex);return rs.wasNull() ? null : codeOf(code);}@Overridepublic E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {int code = cs.getInt(columnIndex);return cs.wasNull() ? null : codeOf(code);}private E codeOf(int code){try {return CodeEnumUtil.codeOf(type, code);} catch (Exception ex) {throw new IllegalArgumentException("Cannot convert " + code + " to " + type.getSimpleName() + " by code value.", ex);}}
}

5. 使用

接下来需要指定哪个类使用我们自己编写转换器进行转换,在MyBatis配置文件中配置如下:

<typeHandlers><typeHandler handler="com.example.typeHandler.CodeEnumTypeHandler" javaType="com.example.entity.enums.ComputerState"/>
</typeHandlers>

搞定! 经测试ComputerState.OPEN被转换为10,ComputerState.UNKNOWN被转换为255,达到了预期的效果。

6. 优化

在第5步时,我们在MyBatis中添加typeHandler用于指定哪些类使用我们自定义的转换器,一旦系统中的枚举类多了起来,MyBatis的配置文件维护起来会变得非常麻烦,也容易出错。如何解决呢?
Spring中我们可以使用JavaConfig方式来干预SqlSessionFactory的创建过程,来完成转换器的指定。
思路

  1. 再写一个能自动匹配转换行为的转换器
  2. 通过sqlSessionFactory.getConfiguration().getTypeHandlerRegistry()取得类型转换器注册器
  3. 再使用typeHandlerRegistry.setDefaultEnumTypeHandler(Class<? extends TypeHandler> typeHandler)将第一步的转换器注册成为默认的

首先,我们需要一个能确定转换行为的转换器:
AutoEnumTypeHandler.java


public class AutoEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {private BaseTypeHandler typeHandler = null;public AutoEnumTypeHandler(Class<E> type) {if (type == null) {throw new IllegalArgumentException("Type argument cannot be null");}if(BaseCodeEnum.class.isAssignableFrom(type)){// 如果实现了 BaseCodeEnum 则使用我们自定义的转换器typeHandler = new CodeEnumTypeHandler(type);}else {// 默认转换器 也可换成 EnumOrdinalTypeHandlertypeHandler = new EnumTypeHandler<>(type);}}@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {typeHandler.setNonNullParameter(ps,i, parameter,jdbcType);}@Overridepublic E getNullableResult(ResultSet rs, String columnName) throws SQLException {return (E) typeHandler.getNullableResult(rs,columnName);}@Overridepublic E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return (E) typeHandler.getNullableResult(rs,columnIndex);}@Overridepublic E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return (E) typeHandler.getNullableResult(cs,columnIndex);}
}

接下来,我们需要干预SqlSessionFactory的创建过程,将刚刚的转换器指定为默认的:

@Configuration
@ConfigurationProperties(prefix = "mybatis")
public class MyBatisConfig {private String configLocation;private String mapperLocations;@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource,JSONArrayHandler jsonArrayHandler,JSONObjectHandler jsonObjectHandler) throws Exception {SqlSessionFactoryBean factory = new SqlSessionFactoryBean();factory.setDataSource(dataSource);// 设置配置文件及mapper文件地址ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();factory.setConfigLocation(resolver.getResource(configLocation));factory.setMapperLocations(resolver.getResources(mapperLocations));SqlSessionFactory sqlSessionFactory = factory.getObject();// 取得类型转换注册器TypeHandlerRegistry typeHandlerRegistry = sqlSessionFactory.getConfiguration().getTypeHandlerRegistry();// 注册默认枚举转换器typeHandlerRegistry.setDefaultEnumTypeHandler(AutoEnumTypeHandler.class);return sqlSessionFactory;}// ... getter setter
}

搞定! 这样一来,如果枚举实现了BaseCodeEnum接口就使用我们自定义的CodeEnumTypeHandler,如果没有实现BaseCodeEnum接口就使用默认的。再也不用写MyBatis的配置文件了!

结束了

以上就是我对如何在MyBatis中优雅的使用枚举的探索。如果你还有更优的解决方案,请一定在评论中告知,万分感激。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402544.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSS3与页面布局学习笔记(六)——CSS3新特性(阴影、动画、渐变、变形( transform)、透明、伪元素等)...

一、阴影 1.1、文字阴影 text-shadow<length>①&#xff1a; 第1个长度值用来设置对象的阴影水平偏移值。可以为负值 <length>②&#xff1a; 第2个长度值用来设置对象的阴影垂直偏移值。可以为负值 <length>③&#xff1a; 如果提供了第3个长度值则用来设置…

隐藏nginx 版本号信息

为了安全&#xff0c;想将http请求响应头里的nginx版本号信息隐藏掉&#xff1a; 1. nginx配置文件里增加 server_tokens off; server_tokens作用域是http server location语句块 server_tokens默认值是on&#xff0c;表示显示版本信息&#xff0c;设置server_tokens值是off&am…

Linux C编程学习--main()函数简析

提到C语言的函数&#xff0c;有太多内容要讲&#xff0c;今天我们要看的是main()函数。 main()函数时程序的入口点&#xff0c;任何程序都要有main()函数&#xff0c;一般大家都怎么写main()函数啊&#xff1f; main(); void main(); void main(void); int main(); int main(vo…

WAS 报错 Font '宋体' is not available to the JVM

今天把WAS迁移到新服务器上&#xff0c;启动应用程序后&#xff0c;有报错内容如下&#xff1a;创建的异常&#xff1a;net.sf.jasperreports.engine.util.JRFontNotFoundException: Font 宋体 is not available to the JVM. See the Javadoc for more details.环境介绍&#x…

解决表字段使用关键字导致Mybatis Generator生成代码异常的解决方案

From: http://blog.itfsw.com/2017/05/23/jiejue-biao-ziduan-shiyong-guanjianzi-daozhi-mybatis-generator-shengcheng-daima-yichang-de-jiejue-fangan/ 在某个项目中遇到这么一个问题&#xff0c;因为原始表结构中某些字段定义使用了MySQL的关键字如match等&#xff0c;在…

用户(三次)登录--作业小编完成

count 0 while count < 3:user input(请输入用户名>>>)pwd input(请输入密码>>>)if user huang and pwd 123:print(欢迎进入黑客帝国)1print(...................)breakelse:print(用户名或密码错误)count count 1 转载于:https://www.cnblogs.com…

从零开始学C++之模板(三):缺省模板参数(借助标准模板容器实现Stack模板)、成员模板、关键字typename...

一、缺省模板参数 回顾前面的文章&#xff0c;都是自己管理stack的内存&#xff0c;无论是链栈还是数组栈&#xff0c;能否借助标准模板容器管理呢&#xff1f;答案是肯定的&#xff0c;只需要多传一个模板参数即可&#xff0c;而且模板参数还可以是缺省的&#xff0c;如下&…

Linux C编程---指针数组简析(二维数组、多级指针)

讲到指针和数组&#xff0c;先给大家看一道例题&#xff1a; 题目&#xff1a;填空练习&#xff08;指向指针的指针&#xff09; 1.程序分析&#xff1a;      2.程序源代码&#xff1a; main() { char *s[]{"man","woman","girl","bo…

使用@Autowired注解警告Field injection is not recommended

From: https://blog.csdn.net/zhangjingao/article/details/81094529 在使用spring框架中的依赖注入注解Autowired时&#xff0c;idea报了一个警告 大部分被警告的代码都是不严谨的地方&#xff0c;所以我深入了解了一下。 被警告的代码如下&#xff1a; Autowired UserDa…

简单的方式优化mysql

参考博客 自己笔记本上向mysql导入txt数据&#xff0c;有一个table导入了将近4个小时&#xff0c;而且多个table之间都是相互之间存在关系的&#xff0c;所以做联合查询的时候你会发现问题会十分的多&#xff0c;我之前联合查询两个表就死机了&#xff0c;所以优化mysql是迫在眉…

9颜色和背景

选择的类名最好描述其中包含的信息类型&#xff0c;而不是想要达到的视觉效果。 一般来说&#xff0c;前景是元素的文本&#xff0c;不过前景还包括元素周围的边框。color属性可以用来设置前景色。color有很多用法&#xff0c;其中最基本的是替换HTML3.2的BODY属性TEXT、LINK、…

linux C --深入理解字符串处理函数 strlen() strcpy() strcat() strcmp()

在linux C 编程中&#xff0c;我们经常遇到字符串的处理&#xff0c;最多的就是字符串的长度、拷贝字符串、比较字符串等&#xff1b;当然现在的&#xff23;库中为我们提供了很多字符串处理函数。熟练的运用这些函数&#xff0c;可以减少编程工作量&#xff0c;这里介绍几个常…

VSFTP的主动模式和被动模式

关于VSFTP的主动模式和被动模式一&#xff0c;首先我们看两个例子如下&#xff1a;其中192.168.10.7是服务端&#xff0c;172.16.11.11是客户端被动模式# netstat -an |grep 172.16.11.11tcp 0 0 192.168.10.7:52160 172.16.11.11:16091 TIME_WA…

SpringBoot项目利用maven自定义打包结构

From: https://blog.csdn.net/q15858187033/article/details/80742117 SpringBoot官方提供的demo中&#xff0c;pom.xml文件里引用了官方提供的打包插件 <build> <plugin> <groupId>org.springframework.boot</groupId> …

20169210《Linux内核原理与分析》第十二周作业

Return-to-libc 攻击实验 缓冲区溢出的常用攻击方法是用 shellcode 的地址来覆盖漏洞程序的返回地址&#xff0c;使得漏洞程序去执行存放在栈中 shellcode。为了阻止这种类型的攻击&#xff0c;一些操作系统使得系统管理员具有使栈不可执行的能力。这样的话&#xff0c;一旦程序…

判断android图片是否硬解码(方法)

2019独角兽企业重金招聘Python工程师标准>>> 在oncreate方面的setContentView(R.layout.main); 前面&#xff0c;添加如下代码&#xff1a; getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HAR…

如何解决Mybatis里mapper文件中关于不能用大于小于号

From: https://blog.csdn.net/qq_38659629/article/details/80408185 用<![CDATA[ ]]>标识 比如&#xff1a;<![CDATA[ where auctionEndTime < now()]]> 另外一种方法就是使用转义字符 < < > > & " < …

Linux C 编程技巧--利用有限状态机模型编程

我们知道&#xff0c;一般编写程序时都要画出流程图&#xff0c;按照流程图结构来编程&#xff0c;如果编写一个比较繁琐&#xff0c;容易思维混乱的程序时&#xff0c;我们可以利用有限状态机模型画出一个状态转移图&#xff0c;这样便可以利用画出的逻辑图来编写程序&#xf…

JVM的垃圾回收机制

发现一篇好文章,能够快速的帮助我们理清楚思路,以下内容转载 JVM的内部结构 先说下jvm运行时数据的划分&#xff0c;粗暴的分可以分为堆区(Heap)和栈区(Stack)&#xff0c;但jvm的分法实际上比这复杂得多&#xff0c;大概分为下面几块&#xff1a; 1、程序计数器(Program Conut…

centos配置ssh免密码登录后仍要输入密码的解决方法

From: https://blog.csdn.net/zwbill/article/details/80448939 前言 在搭建Linux集群服务的时候&#xff0c;主服务器需要启动从服务器的服务&#xff0c;如果通过手动启动&#xff0c;集群内服务器几台还好&#xff0c;要是像阿里1000台的云梯Hadoop集群的话&#xff0c;轨…