当前位置:   article > 正文

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

mybatis自定义枚举处理

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

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

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

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

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

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);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115

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 {
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

  • 1

数据库:

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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/173097
推荐阅读
相关标签
  

闽ICP备14008679号