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&lt;E&gt; 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 -&gt; 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&lt;E&gt; 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&lt;E&gt; 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 -&gt; i.getCode().equals(value))
            .findAny()
            .orElseThrow(() -&gt; <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

Git

https://github.com/lexiaoyao1995/mybatisDemo