类型处理器(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> extends BaseTypeHandler {

private final Class type;

//采用父类的构造方法,会获取到注解MappedTypes中的value,作为type

public EnumTypeHandler(Class 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 {

@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, T> {

T getCode();//code为存入数据库的值

String getInfo();//info为实际意义

}

2、定义枚举类

public enum Gender implements BaseEnum {

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 extends BaseTypeHandler {

private Class enumType;

private E[] enums;

public NormalEnumHandler(Class 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 extends NormalEnumHandler {

public NormalHandler(Class 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

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐