Mybatis枚举类型转换

Mybatis枚举类型转换

类型转换器源码分析

在Mybatis的TypeHandlerRegistry中,添加了常用的类转换器,其中默认的枚举类型转换器是EnumTypeHandler。

cpp 复制代码
public final class TypeHandlerRegistry {
  ....
  
  public TypeHandlerRegistry(Configuration configuration) {
    this.unknownTypeHandler = new UnknownTypeHandler(configuration);

    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());
    ...

EnumTypeHandler.java,默认使用的是枚举的名称设置参数和转换枚举类型。

cpp 复制代码
public EnumTypeHandler(Class<E> type) {
    if (type == null) {
      throw new IllegalArgumentException("Type argument cannot be null");
    }
    this.type = type;
  }

  @Override
  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); // see r3589
    }
  }

  @Override
  public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
    String s = rs.getString(columnName);
    return s == null ? null : Enum.valueOf(type, s);
    ...

以下代码展示了如何为确定枚举类型的类型转换器。

  1. 首先直接获取对应类型的类型型转换器,包括原始类型,包括raw type(原始类型,对应Class),parameterized types(参数化类型), array types(数组类型),这是最精确的匹配。
  2. 如果是Class类型且枚举类型是其接口或父类,如果是匿名类,寻找其父类的类型转换器,否则再不断递归寻找该枚举类的接口类型转换器,如果没有找到,直接利用反射获defaultEnumHandler的的对象,专门用于处理该枚举类型,在图中是GroupStatusEnum。
  3. 如果不是枚举类型,则再尝试其父类。

getInstance方法如下

cpp 复制代码
public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
    if (javaTypeClass != null) {
      try {
        Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
        return (TypeHandler<T>) c.newInstance(javaTypeClass);
      } catch (NoSuchMethodException ignored) {
        // ignored
      } catch (Exception e) {
        throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
      }
    }
    try {
      Constructor<?> c = typeHandlerClass.getConstructor();
      return (TypeHandler<T>) c.newInstance();
    } catch (Exception e) {
      throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
    }
  }

自定义通用枚举类型

  1. 为项目中所有枚举类型定一个接口

    java 复制代码
    public interface BaseEnum {
        Integer getValue();
    }
  2. 枚举类实现该接口

    java 复制代码
    @AllArgsConstructor
    @Getter
    public enum GroupStatusEnum implements BaseEnum {
        DISBANDED("已解散", 0),
        NORMAL("正常", 1);
        private final String desc;
        private final Integer value;
    }
  3. 定义通用枚举类型转换器

    cpp 复制代码
    public class GenericEnumHandler<E extends BaseEnum> implements TypeHandler<E> {
    
        private final Map<Integer, E> map = new HashMap<>();
    
        public GenericEnumHandler(Class<E> clazz) {
            E[] constants = clazz.getEnumConstants();
            for (E constant : constants) {
                map.put(constant.getValue(), constant);
            }
        }
    
        @Override
        public void setParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
            ps.setInt(i, parameter.getValue());
        }
    
        @Override
        public E getResult(ResultSet rs, String columnName) throws SQLException {
            return map.get(rs.getInt(columnName));
        }
    
        @Override
        public E getResult(ResultSet rs, int columnIndex) throws SQLException {
            return map.get(rs.getInt(columnIndex));
        }
    
        @Override
        public E getResult(CallableStatement cs, int columnIndex) throws SQLException {
            return map.get(cs.getInt(columnIndex));
        }
    }

按照上述源码分析流程,当GroupStatusEnum第一次需要转化数据库的int时,mybatis去寻找类型转换器。

  • 我们没有为这种类型定义专门的类型转换器(TypeHandler<GroupStatusEnum>)。
  • 该类不是内部类。
  • 该类实现了BaseEnum接口,但是我们没有为BaseEnum定义类型转换器。
  • 使用该类和默认的枚举类利用反射构造一个对象处理该枚举,即new GenericEnumTypeHandler(GroupStatusEnum.class)。

使用方法

yaml 复制代码
mybatis:
  configuration:
    local-cache-scope: statement
    jdbc-type-for-null: null
    use-generated-keys: true
    cache-enabled: false
    map-underscore-to-camel-case: true
    default-enum-type-handler: com.windcf.easychatjava.typehandler.GenericEnumHandler
  mapper-locations: classpath:/mappers/**/*.xml
#  type-handlers-package: com.windcf.easychatjava.typehandler
相关推荐
公贵买其鹿2 分钟前
List深拷贝后,数据还是被串改
java
PieroPc4 分钟前
Python 写的 智慧记 进销存 辅助 程序 导入导出 excel 可打印
开发语言·python·excel
2401_857439693 小时前
SSM 架构下 Vue 电脑测评系统:为电脑性能评估赋能
开发语言·php
SoraLuna3 小时前
「Mac畅玩鸿蒙与硬件47」UI互动应用篇24 - 虚拟音乐控制台
开发语言·macos·ui·华为·harmonyos
xlsw_3 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹4 小时前
基于java的改良版超级玛丽小游戏
java
Dream_Snowar4 小时前
速通Python 第三节
开发语言·python
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭4 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫5 小时前
泛型(2)
java
超爱吃士力架5 小时前
邀请逻辑
java·linux·后端