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("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

posted @ 2020-08-30 13:55  刃牙  阅读(5185)  评论(0编辑  收藏  举报