springboot mybatis列舉配置(每次只需新增一個列舉類即可)

Victor劉發表於2020-11-13

配置好的專案是怎麼樣的:
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;
}

相關文章