Mybatis 枚举类处理
类型处理器(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(<span class="hljs-string">"0"</span>), enable(<span class="hljs-string">"1"</span>), deleted(<span class="hljs-string">"2"</span>),
init(<span class="hljs-string">"10"</span>), start(<span class="hljs-string">"11"</span>), wait(<span class="hljs-string">"12"</span>), end(<span class="hljs-string">"13"</span>);
}
EnumTypeHandler 源码
public class EnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
private final Class<E> type;
<span class="hljs-comment">//采用父类的构造方法,会获取到注解MappedTypes中的value,作为type</span>
<span class="hljs-keyword">public</span> <span class="hljs-title function_">EnumTypeHandler</span><span class="hljs-params">(Class<E> type)</span> {
<span class="hljs-keyword">if</span> (type == <span class="hljs-literal">null</span>) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">IllegalArgumentException</span>(<span class="hljs-string">"Type argument cannot be null"</span>);
} <span class="hljs-keyword">else</span> {
<span class="hljs-built_in">this</span>.type = type;
}
}
<span class="hljs-comment">//写入数据库时调用</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setNonNullParameter</span><span class="hljs-params">(PreparedStatement ps, <span class="hljs-type">int</span> i, E parameter, JdbcType jdbcType)</span> <span class="hljs-keyword">throws</span> SQLException {
<span class="hljs-keyword">if</span> (jdbcType == <span class="hljs-literal">null</span>) {
ps.setString(i, parameter.name());
} <span class="hljs-keyword">else</span> {
ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE);
}
}
<span class="hljs-comment">//以下三个方法是在存入时候调用,一个是根据列名获取值,一个是根据列索引位置获取值,最后一个是存储过程。</span>
<span class="hljs-keyword">public</span> E <span class="hljs-title function_">getNullableResult</span><span class="hljs-params">(ResultSet rs, String columnName)</span> <span class="hljs-keyword">throws</span> SQLException {
<span class="hljs-type">String</span> <span class="hljs-variable">s</span> <span class="hljs-operator">=</span> rs.getString(columnName);
<span class="hljs-type">return</span> <span class="hljs-variable">s</span> <span class="hljs-operator">=</span>= <span class="hljs-literal">null</span> ? <span class="hljs-literal">null</span> : Enum.valueOf(<span class="hljs-built_in">this</span>.type, s);
}
<span class="hljs-keyword">public</span> E <span class="hljs-title function_">getNullableResult</span><span class="hljs-params">(ResultSet rs, <span class="hljs-type">int</span> columnIndex)</span> <span class="hljs-keyword">throws</span> SQLException {
<span class="hljs-type">String</span> <span class="hljs-variable">s</span> <span class="hljs-operator">=</span> rs.getString(columnIndex);
<span class="hljs-type">return</span> <span class="hljs-variable">s</span> <span class="hljs-operator">=</span>= <span class="hljs-literal">null</span> ? <span class="hljs-literal">null</span> : Enum.valueOf(<span class="hljs-built_in">this</span>.type, s);
}
<span class="hljs-keyword">public</span> E <span class="hljs-title function_">getNullableResult</span><span class="hljs-params">(CallableStatement cs, <span class="hljs-type">int</span> columnIndex)</span> <span class="hljs-keyword">throws</span> SQLException {
<span class="hljs-type">String</span> <span class="hljs-variable">s</span> <span class="hljs-operator">=</span> cs.getString(columnIndex);
<span class="hljs-type">return</span> <span class="hljs-variable">s</span> <span class="hljs-operator">=</span>= <span class="hljs-literal">null</span> ? <span class="hljs-literal">null</span> : Enum.valueOf(<span class="hljs-built_in">this</span>.type, s);
}
}
自定义枚举类处理
1、实现 BaseTypeHandler 接口,重写方法
public class GenderHandler extends BaseTypeHandler<Gender> {
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setNonNullParameter</span><span class="hljs-params">(PreparedStatement preparedStatement, <span class="hljs-type">int</span> i, Gender gender, JdbcType jdbcType)</span> <span class="hljs-keyword">throws</span> SQLException {
preparedStatement.setString(i, gender.getCode());
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> Gender <span class="hljs-title function_">getNullableResult</span><span class="hljs-params">(ResultSet resultSet, String s)</span> <span class="hljs-keyword">throws</span> SQLException {
<span class="hljs-keyword">return</span> Gender.getGender(resultSet.getString(s));
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> Gender <span class="hljs-title function_">getNullableResult</span><span class="hljs-params">(ResultSet resultSet, <span class="hljs-type">int</span> i)</span> <span class="hljs-keyword">throws</span> SQLException {
<span class="hljs-keyword">return</span> Gender.getGender(resultSet.getString(i));
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> Gender <span class="hljs-title function_">getNullableResult</span><span class="hljs-params">(CallableStatement callableStatement, <span class="hljs-type">int</span> i)</span> <span class="hljs-keyword">throws</span> SQLException {
<span class="hljs-keyword">return</span> 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 <span class="hljs-title function_">getCode</span><span class="hljs-params">()</span>;<span class="hljs-comment">//code为存入数据库的值</span>
String <span class="hljs-title function_">getInfo</span><span class="hljs-params">()</span>;<span class="hljs-comment">//info为实际意义</span>
}
2、定义枚举类
public enum Gender implements BaseEnum<Gender, String> {
Male("1", "男"),
Female("0", "女"),
Unknown("3", "保密");
<span class="hljs-keyword">private</span> String code;
<span class="hljs-keyword">private</span> String info;
Gender(String code, String info) {
<span class="hljs-built_in">this</span>.code = code;
<span class="hljs-built_in">this</span>.info = info;
}
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Gender <span class="hljs-title function_">getGender</span><span class="hljs-params">(String code)</span> {
<span class="hljs-keyword">return</span> Arrays.stream(Gender.values()).filter(i -> i.getCode().equals(code)).findAny().orElse(<span class="hljs-literal">null</span>);
}
<span class="hljs-keyword">public</span> String <span class="hljs-title function_">getCode</span><span class="hljs-params">()</span> {
<span class="hljs-keyword">return</span> code;
}
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setCode</span><span class="hljs-params">(String code)</span> {
<span class="hljs-built_in">this</span>.code = code;
}
<span class="hljs-keyword">public</span> String <span class="hljs-title function_">getInfo</span><span class="hljs-params">()</span> {
<span class="hljs-keyword">return</span> info;
}
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setInfo</span><span class="hljs-params">(String info)</span> {
<span class="hljs-built_in">this</span>.info = info;
}
}
3、编写统一的枚举映射 handler
public class NormalEnumHandler<E extends BaseEnum> extends BaseTypeHandler<E> {
<span class="hljs-keyword">private</span> Class<E> enumType;
<span class="hljs-keyword">private</span> E[] enums;
<span class="hljs-keyword">public</span> <span class="hljs-title function_">NormalEnumHandler</span><span class="hljs-params">(Class<E> type)</span> {
<span class="hljs-keyword">if</span> (type == <span class="hljs-literal">null</span>)
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">IllegalArgumentException</span>(<span class="hljs-string">"Type argument cannot be null"</span>);
<span class="hljs-built_in">this</span>.enumType = type;
<span class="hljs-built_in">this</span>.enums = type.getEnumConstants();<span class="hljs-comment">//获取所有枚举数组</span>
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.enums == <span class="hljs-literal">null</span>)
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">IllegalArgumentException</span>(type.getSimpleName()
+ <span class="hljs-string">" does not represent an enum type."</span>);
}
<span class="hljs-keyword">public</span> <span class="hljs-title function_">NormalEnumHandler</span><span class="hljs-params">()</span> {
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setNonNullParameter</span><span class="hljs-params">(PreparedStatement preparedStatement, <span class="hljs-type">int</span> i, E e, JdbcType jdbcType)</span> <span class="hljs-keyword">throws</span> SQLException {
<span class="hljs-keyword">if</span> (jdbcType == <span class="hljs-literal">null</span>) {
preparedStatement.setString(i, (String) e.getCode());
} <span class="hljs-keyword">else</span> {
preparedStatement.setObject(i, e.getCode(), jdbcType.TYPE_CODE);
}
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> E <span class="hljs-title function_">getNullableResult</span><span class="hljs-params">(ResultSet resultSet, String s)</span> <span class="hljs-keyword">throws</span> SQLException {
<span class="hljs-keyword">return</span> resultSet.wasNull() ? <span class="hljs-literal">null</span> : locateEnumStatus(resultSet.getString(s));
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> E <span class="hljs-title function_">getNullableResult</span><span class="hljs-params">(ResultSet resultSet, <span class="hljs-type">int</span> i)</span> <span class="hljs-keyword">throws</span> SQLException {
<span class="hljs-keyword">return</span> resultSet.wasNull() ? <span class="hljs-literal">null</span> : locateEnumStatus(resultSet.getString(i));
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> E <span class="hljs-title function_">getNullableResult</span><span class="hljs-params">(CallableStatement callableStatement, <span class="hljs-type">int</span> i)</span> <span class="hljs-keyword">throws</span> SQLException {
<span class="hljs-keyword">return</span> callableStatement.wasNull() ? <span class="hljs-literal">null</span> : locateEnumStatus(callableStatement.getString(i));
}
<span class="hljs-keyword">private</span> E <span class="hljs-title function_">locateEnumStatus</span><span class="hljs-params">(String value)</span> {
<span class="hljs-keyword">return</span> Arrays.stream(enums)
.filter(i -> i.getCode().equals(value))
.findAny()
.orElseThrow(() -> <span class="hljs-keyword">new</span> <span class="hljs-title class_">IllegalArgumentException</span>(<span class="hljs-string">"未知的枚举类型:"</span> + value + <span class="hljs-string">",请核对"</span> + 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