类型处理器(TypeHandler)

无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成 Java 类型。

通过类型处理器(TypeHandler),可以实现javaBean以某种方式存入数据库中,抑或是从数据库取出的数据如何映射为javaBean。

通过继承BaseTypeHandler类,我们可以定制这个类型处理器,已实现枚举类或是一个javaBean的存取和写入。

内置的枚举处理器

mybatis内置了两个枚举类型处理器,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");
}

EnumTypeHandler源码

public class EnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
private final Class<E> type; //采用父类的构造方法,会获取到注解MappedTypes中的value,作为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);
}
}

自定义枚举类处理

1、实现BaseTypeHandler接口,重写方法


public class GenderHandler extends BaseTypeHandler<Gender> { @Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Gender gender, JdbcType jdbcType) throws SQLException {
preparedStatement.setString(i, gender.getCode());
} @Override
public Gender getNullableResult(ResultSet resultSet, String s) throws SQLException {
return Gender.getGender(resultSet.getString(s));
} @Override
public Gender getNullableResult(ResultSet resultSet, int i) throws SQLException {
return Gender.getGender(resultSet.getString(i));
} @Override
public Gender getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
return Gender.getGender(callableStatement.getString(i));
} }

2、在application.yml中加入配置,扫描handler组件

mybatis:
# 配置mybatis的resultType别名,默认是别名为小写
type-aliases-package: com.lexiaoyao.mybatisdemo.domain
# 配置扫描的xml文件位置
mapper-locations: classpath:mybatis/mapper/*.xml
# mybatis详细配置文件
config-location: classpath:mybatis/mybatis-config.xml
# 扫描handler组件
type-handlers-package: com.lexiaoyao.mybatisdemo.handler

通用枚举处理器

note 这里将mysql数据库里字段定义为char(1)

1、定义枚举类的接口,统一格式


public interface BaseEnum<E extends Enum<?>, T> { T getCode();//code为存入数据库的值 String getInfo();//info为实际意义 }

2、定义枚举类


public enum Gender implements BaseEnum<Gender, String> {
Male("1", "男"),
Female("0", "女"),
Unknown("3", "保密"); private String code;
private String info; Gender(String code, String info) {
this.code = code;
this.info = info;
} public static Gender getGender(String code) {
return Arrays.stream(Gender.values()).filter(i -> i.getCode().equals(code)).findAny().orElse(null); } public String getCode() {
return code;
} public void setCode(String code) {
this.code = code;
} public String getInfo() {
return info;
} public void setInfo(String info) {
this.info = info;
}
}

3、编写统一的枚举映射handler


public class NormalEnumHandler<E extends BaseEnum> extends BaseTypeHandler<E> { private Class<E> enumType;
private E[] enums; public NormalEnumHandler(Class<E> type) {
if (type == null)
throw new IllegalArgumentException("Type argument cannot be null");
this.enumType = type;
this.enums = type.getEnumConstants();//获取所有枚举数组
if (this.enums == null)
throw new IllegalArgumentException(type.getSimpleName()
+ " does not represent an enum type.");
} public NormalEnumHandler() {
} @Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, E e, JdbcType jdbcType) throws SQLException {
if (jdbcType == null) {
preparedStatement.setString(i, (String) e.getCode());
} else {
preparedStatement.setObject(i, e.getCode(), jdbcType.TYPE_CODE);
} } @Override
public E getNullableResult(ResultSet resultSet, String s) throws SQLException {
return resultSet.wasNull() ? null : locateEnumStatus(resultSet.getString(s));
} @Override
public E getNullableResult(ResultSet resultSet, int i) throws SQLException {
return resultSet.wasNull() ? null : locateEnumStatus(resultSet.getString(i));
} @Override
public E getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
return callableStatement.wasNull() ? null : locateEnumStatus(callableStatement.getString(i));
} private E locateEnumStatus(String value) {
return Arrays.stream(enums)
.filter(i -> i.getCode().equals(value))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("未知的枚举类型:" + value + ",请核对" + enumType.getSimpleName()));
} }

4、相应的扫描包下只需写一个Handler,并继承NormalEnumHandler


@MappedTypes(value = {Gender.class})
public class NormalHandler<E extends BaseEnum> extends NormalEnumHandler<E> {
public NormalHandler(Class<E> type) {
super(type);
}
}

要新加入枚举时候,只需要在MappedTypes的数据里添加一个即可。

5、配置扫描包

mybatis:
# 配置mybatis的resultType别名,默认是别名为小写
type-aliases-package: com.lexiaoyao.mybatisdemo.domain
# 配置扫描的xml文件位置
mapper-locations: classpath:mybatis/mapper/*.xml
# mybatis详细配置文件
config-location: classpath:mybatis/mybatis-config.xml
# 扫描handler组件
type-handlers-package: com.lexiaoyao.mybatisdemo.handler

Git

https://github.com/lexiaoyao1995/mybatisDemo

Mybatis 枚举类处理的更多相关文章

  1. 使用内部枚举类作为外部类的参数的Mybatis的参数该如何判断

    新写了一个接口,期望根据不同的参数来给数据库中不同的字段进行传值.这里使用了内部静态枚举类的方式进行传值,在写mybatis动态sql时,如果是普通对象,一般使用,那么使用枚举类,如何判断枚举类的值呢 ...

  2. mybatis枚举映射成tinyint

    第一步:定义顶级枚举接口 public interface BaseEnum<E extends Enum<?>, T> { public T getCode(); publi ...

  3. mybatis枚举自动转换(通用转换处理器实现)

    https://blog.csdn.net/fighterandknight/article/details/51520595 https://blog.csdn.net/fighterandknig ...

  4. mybatis 枚举类型使用

    一.首先定义接口,提供获取数据库存取的值得方法,如下: public interface BaseEnum { int getCode(); } 二.定义mybatis的typeHandler扩展类, ...

  5. mybatis枚举类型处理器

    1. 定义枚举值的接口 public abstract interface ValuedEnum { int getValue(); } 所有要被mybatis处理的枚举类继承该接口 2. 定义枚举类 ...

  6. Mybatis枚举转换

    自定义mybatis枚举转换,原理是如果用户没有定义自己的枚举转换工具,mybatis在解析枚举类时会自动获取mybatis的BaseTypeHandler,来转换枚举类,我们只需要重写这个枚举转换器 ...

  7. Linq专题之提高编码效率—— 第三篇 你需要知道的枚举类

     众所周知,如果一个类可以被枚举,那么这个类必须要实现IEnumerable接口,而恰恰我们所有的linq都是一个继承自IEnumerable接口的匿名类, 那么问题就来了,IEnumerable使了 ...

  8. 0029 Java学习笔记-面向对象-枚举类

    可以创建几个对象? n多个:大部分的类,都可以随意创建对象,只要内存不爆掉 1个:比如单例类 有限的几个:采用单例类的设计思路,可以只允许创建少数的几个特定的对象:还有就是枚举类. 创建少数几个对象, ...

  9. FastJson转换自定义枚举类

    在项目中有些状态需要采用枚举类型,在数据库中保存的是name(英文),而前台需要显示的是text(中文). 所以这就需要自己去实现序列. 例如对象: import java.util.Date; im ...

随机推荐

  1. Android 布局的一些控件的补充和布局的补充(今儿没课)

    前面写的博客可能会有点乱: 1,是不太会排版. 2,就是我一边看书,一边听学长讲课,所以有的知识就融入进去了,我写的都是自己的意见和理解,大家取我精华,弃我糟粕哈. 今天是书上的内容,主要讲布局的,一 ...

  2. GitHub/Git配置与简单的使用

    今天我开始了初步的学习,首先从陌生的开始下手,GitHub,自己通过查询网络上的资料有了初步的理解与认识.进行了Git与GitHub的配置. 一.前期准备 首先下载Git,Git官网->http ...

  3. Netty(二):如何处理io请求?

    文接上一篇.上篇讲到netty暴露一个端口出来,acceptor, handler, pipeline, eventloop 都已准备好.但是并没体现其如何处理接入新的网络请求,今天我们就一起来看看吧 ...

  4. 金题大战Vol.0 C、树上的等差数列

    金题大战Vol.0 C.树上的等差数列 题目描述 给定一棵包含\(N\)个节点的无根树,节点编号\(1-N\).其中每个节点都具有一个权值,第\(i\)个节点的权值是\(A_i\). 小\(Hi\)希 ...

  5. Spring - RestTemplate执行原理分析

    什么是RestTemplate Synchronous client to perform HTTP requests, exposing a simple, template method API ...

  6. Lambda表达式的几种实现过程

    1.无参数+语句(代码块):适用于匿名内部类中方法无参数的情况 private void threadTest(){ //普通写法 new Thread(new Runnable(){ @Overri ...

  7. YApi——手摸手,带你在Win10环境下安装YApi可视化接口管理平台

    手摸手,带你在Win10环境下安装YApi可视化接口管理平台 YApi YApi 是高效.易用.功能强大的 api 管理平台,旨在为开发.产品.测试人员提供更优雅的接口管理服务.可以帮助开发者轻松创建 ...

  8. Jenkins配置总结

    1.配置全局 系统管理->全局工具配置 2.配置 自己安装安装jdk,git,以及maven 3.系统管理->系统配置 3.1配置Jenkins URL 3.2 配置SSH Servers ...

  9. guzzlehttp中的坑之带子目录的域名

    1.问题: 线上开发环境配的是带子目录的域名,例如:https://aa.com/bb.使用nginx的location代理到项目的根目录.在代码中使用guzzlehttp访问这域名下的API时,一直 ...

  10. mycli工具mysql命令自动补全

    简介 MyCli 是一个 MySQL 的命令行客户端,可以实现自动补全和语法高亮.MyCli 也可用于 MariaDB 和Percona. 项目地址:http://mycli.net/ 安装 pip安 ...