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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM