foreword
In the use of Mybatis in Spring Boot, I encountered that the field is an enumeration type, and the database stores the value of the enumeration, and found that it cannot be automatically loaded.
solve
Built-in enum converter
MyBatis has two built-in enumeration converters: org.apache.ibatis.type.EnumTypeHandler
and org.apache.ibatis.type.EnumOrdinalTypeHandler
.
EnumTypeHandler
The default enum converter in mybatis is to get the name
properties .
EnumOrdinalTypeHandler
Getting the ordinal
properties is like an index, but incremented from 1.
Therefore, the two converters provided above cannot meet our needs, and we need to customize a converter.
custom enum converter
MyBatis provides org.apache.ibatis.type.BaseTypeHandler
classes for our own extension type converters, and the above EnumTypeHandler和EnumOrdinalTypeHandler
also implement this interface.
Inheritance needs to implement a BaseTypeHandler<T>
total of 4 methods:
1. void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)
When defining the setting parameters, how to convert the parameters of the Java type to the corresponding database types;
2. T getNullableResult(ResultSet rs, String columnName)
When defining the field data obtained by the field name, how to convert the database type to The corresponding Java type;
3. T getNullableResult(ResultSet rs, int columnIndex)
Used to define how to convert the database type to the corresponding Java type when the field data is obtained through the field index;
4. T getNullableResult(CallableStatement cs, int columnIndex)
How to convert the database type to the corresponding Java type after calling the stored procedure with the definition.
Defining an enum generic behavior
Defines the general behavior of an enumeration, and standardizes the implementation of the enumeration.
public interface BaseEnum<E extends Enum<?>, T> {
/**
* 获取枚举的值
* @return 枚举的值
*/
T getValue();
}
Define the enumeration you need:
public class SysConstant {
/**
* 人员状态
*/
public enum SysUserStatus implements BaseEnum<SysUserStatus, String> {
/**
* 账户已经激活(默认)
*/
ACTIVE("1"),
/**
* 账户锁定
*/
LOCK("0");
private String value;
private SysUserStatus(String value) {
this.value = value;
}
@Override
public String getValue() {
return value;
}
}
/**
* 人员类型
*/
public enum SysUserType implements BaseEnum<SysUserType, String> {
/**
* 普通用户
*/
USER("1"),
/**
* 系统管理员
*/
ADMIN("0");
private String value;
private SysUserType(String value) {
this.value = value;
}
@Override
public String getValue() {
return value;
}
}
}
Implement custom converters
Customize a basic enum converter tool, if you have other needs, you can customize this base class.
package com.wuwii.common.util;
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;
/**
* 参考 http://blog.csdn.net/fighterandknight/article/details/51520595
* 进行对本地项目的优化
* <p>
* 解决 Mybatis 中枚举的问题,
* 获取 ResultSet 的值都是获取字符串的,然后比较字符串,以便通用。
*
* @author Zhang Kai
* @version 1.0
* @since <pre>2018/2/9 17:26</pre>
*/
public abstract class BaseEnumTypeHandler<E extends Enum<E> & BaseEnum> extends BaseTypeHandler<E> {
/**
* 枚举的class
*/
private Class<E> type;
/**
* 枚举的每个子类枚
*/
private E[] enums;
/**
* 一定要有默认的构造函数,不然抛出 not found method 异常
*/
public BaseEnumTypeHandler() {
}
/**
* 设置配置文件设置的转换类以及枚举类内容,供其他方法更便捷高效的实现
*
* @param type 配置文件中设置的转换类
*/
public BaseEnumTypeHandler(Class<E> type) {
if (type == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.type = type;
this.enums = type.getEnumConstants();
if (this.enums == null) {
throw new IllegalArgumentException(type.getSimpleName()
+ " does not represent an enum type.");
}
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter,
JdbcType jdbcType) throws SQLException {
/*
* BaseTypeHandler已经帮我们做了parameter的null判断
* 数据库存储的是枚举的值,所以我们这里使用 value , 如果需要存储 name,可以自定义修改
*/
if (jdbcType == null) {
ps.setString(i, Objects.toString(parameter.getValue()));
} else {
ps.setObject(i, parameter.getValue(), jdbcType.TYPE_CODE);
}
}
@Override
public E getNullableResult(ResultSet rs, String columnName)
throws SQLException {
String i = rs.getString(columnName);
if (rs.wasNull()) {
return null;
} else {
return locateEnumStatus(i);
}
}
@Override
public E getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
String i = rs.getString(columnIndex);
if (rs.wasNull()) {
return null;
} else {
return locateEnumStatus(i);
}
}
@Override
public E getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
String i = cs.getString(columnIndex);
if (cs.wasNull()) {
return null;
} else {
return locateEnumStatus(i);
}
}
/**
* 枚举类型转换,由于构造函数获取了枚举的子类 enums,让遍历更加高效快捷,
* <p>
* 我将取出来的值 全部转换成字符串 进行比较,
*
* @param value 数据库中存储的自定义value属性
* @return value 对应的枚举类
*/
private E locateEnumStatus(String value) {
for (E e : enums) {
if (Objects.toString(e.getValue()).equals(value)) {
return e;
}
}
throw new IllegalArgumentException("未知的枚举类型:" + value + ",请核对"
+ type.getSimpleName());
}
}
configuration converter
After writing the enum converter, we need to define which enums it converts.
Can be configured in the Mybatis configuration file
<typeHandlers>
<typeHandler handler="com.example.typeHandler.CodeEnumTypeHandler" javaType="com.example.entity.enums.ComputerState"/>
</typeHandlers>
optimization
Adding typeHandler to MyBatis is used to specify which classes use our custom converter. Once there are more enumeration classes in the system, the configuration file of MyBatis will become very troublesome and error-prone to maintain .
Method 1 :
Define a EnumTypeHandler
to inherit ours BaseEnumTypeHandler
. Then use annotation @MappedTypes
type conversion.
package com.wuwii.module.sys.common.handle;
import com.wuwii.common.handle.BaseEnumTypeHandler;
import com.wuwii.common.util.BaseEnum;
import com.wuwii.module.sys.common.util.SysConstant;
import org.apache.ibatis.type.MappedTypes;
/**
* 枚举转换的公共模块
*
* @author Zhang Kai
* @version 1.0
* @since <pre>2018/2/9 18:12</pre>
*/
@MappedTypes(value = {SysConstant.SysUserStatus.class, SysConstant.SysUserType.class})
public class SysEnumTypeHandler<E extends Enum<E> & BaseEnum> extends BaseEnumTypeHandler<E> {
/**
* 设置配置文件设置的转换类以及枚举类内容,供其他方法更便捷高效的实现
*
* @param type 配置文件中设置的转换类
*/
public SysEnumTypeHandler(Class<E> type) {
super(type);
}
}
It needs to be configured in the system configuration file
# 多个模块的的多个 包配置可以使用逗号分开。
mybatis:
typeHandlersPackage: com.wuwii.module.sys.common.handle,com.wuwii.module.task.common.handle
Method 2 :
If you customize it in your project SqlSessionFactory
, it is recommended to use the following method, and you do not need to configure it multiple times for one use.
How to use enumeration gracefully in MyBatis
In = "Scenario 6. Optimization
Method 3 :
There is also a personal modification of the source code to achieve, if you are interested, you can take a look at:
Modify MyBatis source code to implement scan registration enumeration - specific implementation