[如何提高代码优雅性——枚举] Mybatis枚举全自动配置

配置好的项目是怎么样的:
1、新建表、MGB生成Mapper.xml
2、新增枚举类 class XXXEnum implements DbEnum{…}
3、修改POJO实体属性类型为XXXEnum
4、完成。

注:
这样配置后,新增枚举是很简单的,
且无需修改Mapper.xml,
resultMap|resultType都有效,
插入、查询都自动转换枚举类型。

实现方式:
1、myBatis的处理器typeHandler、自定义的枚举类接口DbEnum
2、springBoot配置handler(applicaiton.yml)

先来简单的application.yml配置:

mybatis:
  type-handlers-package: com.macro.mall.enums.config

然后是DbEnum:

public interface DbEnum<E extends Enum<?>, T> {
    String getCode();
    String getName();
}

最后是重点的TypeHandler:
TypeHandler,类型转换器,在mybatis中用于实现java类型和JDBC类型的相互转换。
我们自定义一个枚举类型转换器,继承TypeHandler

重点代码

// 1、此注解告诉mybatis遇到此PayWayEnum.class类型时,使用此处理器处理
@MappedTypes(value = {PayWayEnum.class})
public class UniversalEnumHandler<E extends Enum<E> & DbEnum> extends BaseTypeHandler<E> {

    /**
     * 2、value接收的Class[]类型,我们想让实现DbEnum接口的枚举都自动转换,不要麻烦的一一配置。
     */
    static {
        try {
        	// 3、MapperTypes是使用代理模式的,static块中我们使用Proxy获取并修改@MappedTypes(value = {})的value值
            MappedTypes annotation = UniversalEnumHandler.class.getAnnotation(MappedTypes.class);
            InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
            Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
            memberValues.setAccessible(true);
            Map values = (Map) memberValues.get(invocationHandler);

			// 4、使用hutool工具包,获取xxx包下,DbEnum的实现类。
            Set<Class<?>> classes = ClassUtil.scanPackageBySuper("com.xxx.enums", DbEnum.class);
            Class[] allDbEnums = classes.toArray(new Class[classes.size()]);

            values.put("value", allDbEnums);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

详细代码

package com.macro.mall.enums.config;

import cn.hutool.core.util.ClassUtil;
import com.macro.mall.enums.*;
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.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.Set;

@MappedTypes(value = {})
public class UniversalEnumHandler<E extends Enum<E> & DbEnum> extends BaseTypeHandler<E> {

    /**
     * 自动加载枚举类型
     * 动态修改@MappedTypes(value = {})的value值
     */
    static {
        try {
            MappedTypes annotation = UniversalEnumHandler.class.getAnnotation(MappedTypes.class);
            InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
            Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
            memberValues.setAccessible(true);
            Map values = (Map) memberValues.get(invocationHandler);

            Set<Class<?>> classes = ClassUtil.scanPackageBySuper("com.xxx.enums", DbEnum.class);
            Class[] allDbEnums = classes.toArray(new Class[classes.size()]);

            values.put("value", allDbEnums);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private final Class<E> type;

    /**
     * construct with parameter.
     */
    public UniversalEnumHandler(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 {
        ps.setString(i, parameter.getCode());
    }

    @Override
    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String code = rs.getString(columnName);
        return rs.wasNull() ? null : this.codeOf(this.type, code);
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String code = rs.getString(columnIndex);
        return rs.wasNull() ? null : this.codeOf(this.type, code);
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String code = cs.getString(columnIndex);
        return cs.wasNull() ? null : this.codeOf(this.type, code);
    }

    public <T extends Enum<?> & DbEnum> T codeOf(Class<T> enumClass, String code) {
        T[] enumConstants = enumClass.getEnumConstants();
        for (T t : enumConstants) {
            if (t.getCode().equals(code)) {
                return t;
            }
        }
        return null;
    }
}

最后放一个新增枚举的例子

package com.macro.mall.enums;

import com.macro.mall.enums.config.DbEnum;
import com.macro.mall.enums.config.Localisable;

public enum PayWayEnum implements DbEnum, Localisable {

    ALI("ali", "支付宝"),
    WX("wx", "微信支付"),
    BANK("bank", "银联"),
    TANSFER("tansfer", "转账"),
    CASH("cash", "现金");

    private String code;
    private String name;

    private PayWayEnum(String dbConstant, String messageKey) {
        this.code = dbConstant;
        this.name = messageKey;
    }

    @Override
    public String getCode() {
        return this.code;
    }

    @Override
    public String getName() {
        return this.name;
    }
}

POJO实体

public class OrderPay {
	private PayWayEnum payWay;
}

2021-01-27日更新
我将此功能,封装成了SpringBoot的Starter,并发布到Maven中心库了。原文点我
maven地址:

<!-- https://mvnrepository.com/artifact/com.github.haerxiong/autoenum-spring-boot-starter -->
<dependency>
    <groupId>com.github.haerxiong</groupId>
    <artifactId>autoenum-spring-boot-starter</artifactId>
    <version>1.0.1</version>
</dependency>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值