Mybatis枚举类型处理器(自带实现两种)+如何自定义枚举类型处理器

详细带图版解释,csdn不允许直接复制图片,下面都是文字描述,理解起来可能会有点难度

final关键字的赋值有以下几种方式:
显式赋值:private final Integer code = 1
静态代码块/代码块赋值
构造器赋值

当直接用枚举类存入数据库使用Mybatis时,是怎么处理的?
在java和数据库之间,Mybatis承担了中间人的角色,存入时会自动将枚举对象转为字符串,取出时又把字符串转为枚举对象。Mybatis是怎么操作的呢?
存入:
Mybatis默认的枚举转换器是EnumTypeHandler,取枚举的name作为值存入数据库。Enum.name()
Mybatis中还有另一个枚举转换器EnumOrdinalTypeHandler,它取的是枚举的ordinal的值存入数据库。如需切换:

显式声明Mybatis枚举转换器,默认是EnumTypeHandler

default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler

name和ordinal被定义在Enum抽象类中,而所有枚举类实际上都会继承Enum,所以每一个枚举对象都有name和ordinal。且枚举Enum父类重写了toString()方法。实际返回的是name。
取出:
如何将存入的值变为枚举的呢?
同样的,肯定还是EnumTypeHandler和EnumOrdinalTypeHandler帮我们做的。
1.ordinal怎么转的呢?EnumOrdinalTypeHandler怎么处理的?
EnumOrdinalTypeHandler继承了BaseTypeHandler类。枚举转换类,需实现MyBatis提供的TypeHandler接口才能进行类型转换。

从数据库取出数据时会调用getNullableResult()方法中的toOrdinalEnum()方法,该方法会直接从enums对象中取出ordianl对应的Enum对象。enums为枚举对象数组。
enums是什么时候来的呢?

在Mybatis启动时,枚举处理器会被初始化。type.getEnumConstants()会调用getEnumConstantsShared()方法,在此方法中如果是枚举的话,通过反射获取到values()方法,最终获取到enum对象数组。

通过反编译会发现枚举的values数组是通过静态代码块就加载进去了。

2.name怎么转的呢?EnumTypeHandler怎么处理的?
EnumTypeHandler也是继承BaseTypeHandler的。
里面也实现了getNullableResult()方法,当从数据库中取出数据时会通过此方法对name进行转换。在此方法中通过Enum.valueOf(type, name)方法,此方法为根据枚举的名称获取枚举实例。最终还是会调用Class中的enumConstantDirectory()方法

在此通过以枚举的name()作键,枚举对象本身做值,把枚举对象数据存入Map中,然后直接通过Map.get(name)获取到枚举对象。
但不论EnumTypeHandler还是EnumOrdinalTypeHandler处理器都不好用,实际开发中我们往往使用的不是ordinal或name,而是自己定义的字段,如code,desc等。
自定义枚举转换器:
核心思想是 抄! 我们可以通过模范EnumOrdinalTypeHandler抄一份。然后让Mybatis按我们的转换器转换。
自定义枚举转换器分为3步:
1.编写枚举转换器类,实现Mybatis提供的TypeHandler接口,但此接口中方法很少,我们可以想EnumOrdinalTypeHandler一样继承BaseTypeHandler。
2.指定type-handlers-package,告诉Mybatis在哪里可以找到我们自定义的转换器。
3.在转换器上使用MappedTypes{Enum.class},指定处理那个枚举类。

我们复制过来后,我们会发现设置值时调用的是枚举自带的ordianl方法,我们怎么才能设置自定义的字段,如何获取呢?对了。反射。为了更加具有通用性,可以通过使用注解对要存入数据库和取出数据库的字段进行注解标记。

ok,为了Mybatis当处理枚举时,使用我们的枚举类型处理器时,处理时怎么才能让他知道要操作枚举的哪个字段的值呢,通过使用注解标记字段,然后使用反射获取到哪个字段上包含此注解。所以我们先定义注解类。

接下来写我们自定义的MyEnumTypeHandler怎么处理存入和取出呢。
存入:

取出:

是不是有疑问?这个enum和type是哪来的呀?我们既然是‘抄’的所以跟EnumOrdianlTypeHandler一样,在其构造器中未enums和type进行了初始化。

小结:

自定义枚举处理器文件:
MyEnumTypeHandler.java

package com.jt.mybatisenum.handler;

import com.jt.mybatisenum.annotation.EnumValue;
import com.jt.mybatisenum.enumj.WeekDayEnum;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;

import java.lang.reflect.Field;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @author jiatai.hu
 * @version 1.0
 * @date 2021/2/4 10:59
 * 处理WeekDayEnum枚举
 */
@MappedTypes(WeekDayEnum.class)
public class MyEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {

    private final Class<E> type;
    private final E[] enums;

    public MyEnumTypeHandler(Class<E> type) {
        if (type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.type = type;
        this.enums = type.getEnumConstants();
        if (this.enums == null) {
            throw new IllegalArgumentException(type.getSimpleName() + " does not represent an enum type.");
        }
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
        Field[] declaredFields = parameter.getClass().getDeclaredFields();
        for (Field declaredField : declaredFields) {
            EnumValue annotation = declaredField.getAnnotation(EnumValue.class);
            if (annotation == null){
                continue;
            }
            declaredField.setAccessible(true);
            Object paramValue = null;
            try {
                // 获取到设置了EnumValue注解字段的value值
                paramValue = declaredField.get(parameter);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            ps.setObject(i, paramValue);
            return;
        }
    }

    @Override
    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Field[] declaredFields = type.getDeclaredFields();
        Field enumField = null;
        Object value4Db = null;
        for (Field declaredField : declaredFields) {
            EnumValue annotation = declaredField.getAnnotation(EnumValue.class);
            if (annotation == null){
                continue;
            }
            enumField = declaredField;
            value4Db = rs.getObject(columnName,enumField.getType());
            break;
        }
        if (enumField == null){
            return getResultByOrdinal(rs.getInt(columnName), rs.wasNull());
        }
        enumField.setAccessible(true);
        for (E anEnum : enums) {
            try {
                Object value = enumField.get(anEnum);
                if (value.equals(value4Db)){
                    return anEnum;
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return getResultByOrdinal(rs.getInt(columnName), rs.wasNull());
    }

    private E getResultByOrdinal(int anInt, boolean b) {
        int ordinal = anInt;
        if (ordinal == 0 && b) {
            return null;
        }
        return toOrdinalEnum(ordinal);
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return getResultByOrdinal(rs.getInt(columnIndex), rs.wasNull());
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return getResultByOrdinal(cs.getInt(columnIndex), cs.wasNull());
    }

    private E toOrdinalEnum(int ordinal) {
        try {
            return enums[ordinal];
        } catch (Exception ex) {
            throw new IllegalArgumentException("Cannot convert " + ordinal + " to " + type.getSimpleName() + " by ordinal value.", ex);
        }
    }
}

EnumValue.java

package com.jt.mybatisenum.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author jiatai.hu
 * @version 1.0
 * @date 2021/2/4 11:19
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnumValue {
}

WeekDayEnum.java

package com.jt.mybatisenum.enumj;

import com.jt.mybatisenum.annotation.EnumValue;
import lombok.Getter;

@Getter
public enum WeekDayEnum {
    MONDAY(1,"星期一"),
    TUESDAY(2,"星期二"),
    WEDNESDAY(3,"星期三"),
    THURSDAY(4,"星期四"),
    FRIDAY(5,"星期五"),
    SATURDAY(6,"星期六"),
    SUNDAY(7,"星期日");

    WeekDayEnum(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    private final Integer code;
    @EnumValue
    private final String desc;
}

数据库:

CREATE TABLE `t_user` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `name` varchar(50) DEFAULT '' COMMENT '姓名',
  `age` tinyint(3) unsigned DEFAULT NULL COMMENT '年龄',
  `rest_day` varchar(50)  COMMENT '休息日',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  `deleted` tinyint(1) unsigned DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

application.yml

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mysql?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
    username: root
    password: xpar
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  mapper-locations: classpath:mapper/**/*.xml
  configuration:
    map-underscore-to-camel-case: on
      # 显式声明Mybatis枚举转换器,默认是EnumTypeHandler
    default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler
  # 指定自定义的枚举转换器路径
  type-handlers-package: com.jt.mybatisenum.handler

logging:
  level:
    com.jt: debug
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis中,你可以使用自定义的类型处理器(TypeHandler)来处理自定义的Map类型。类型处理器用于在Java对象和数据库字段之间进行转换。 以下是一种实现自定义类型处理器处理自定义的Map的示例: 1. 定义一个自定义的Map类型,例如`CustomMap`: ```java public class CustomMap extends HashMap<String, Object> { // 添加自定义的方法或属性 } ``` 2. 实现一个自定义的类型处理器,继承自`org.apache.ibatis.type.BaseTypeHandler`类,并实现对应的方法。 ```java import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class CustomMapTypeHandler extends BaseTypeHandler<CustomMap> { @Override public void setNonNullParameter(PreparedStatement ps, int i, CustomMap parameter, JdbcType jdbcType) throws SQLException { // 将CustomMap转换为需要的数据类型,并设置到PreparedStatement中 // ps.setXXX(i, convertedValue); } @Override public CustomMap getNullableResult(ResultSet rs, String columnName) throws SQLException { // 从ResultSet中获取指定列名的值,并将其转换为CustomMap类型 // Object columnValue = rs.getXXX(columnName); // CustomMap map = convertToCustomMap(columnValue); // return map; return null; } @Override public CustomMap getNullableResult(ResultSet rs, int columnIndex) throws SQLException { // 与上面类似,只是根据列索引获取值 return null; } @Override public CustomMap getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { // 与上面类似,只是在CallableStatement中获取值 return null; } } ``` 在上述示例中,我们继承了`BaseTypeHandler`类,并重写了父类的方法,在这些方法中进行了自定义类型的转换逻辑。 3. 在MyBatis的配置文件中,注册自定义的类型处理器。 ```xml <typeHandlers> <typeHandler handler="com.example.CustomMapTypeHandler"/> </typeHandlers> ``` 通过以上步骤,你就可以使用自定义的Map类型,并通过自定义的类型处理器处理该类型的转换逻辑。在数据库操作时,MyBatis会自动调用类型处理器来进行转换。你可以根据实际需求,在类型处理器中编写相应的转换逻辑,将自定义的Map类型与数据库字段进行转换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值