mybatis 以及plus 枚举处理

发布于:2024-04-09 ⋅ 阅读:(129) ⋅ 点赞:(0)
java.lang.IllegalArgumentException: No enum constant XXX	at java.base/java.lang.Enum.valueOf(Enum.java:273)
	at org.apache.ibatis.type.EnumTypeHandler.getNullableResult(EnumTypeHandler.java:49)
	at org.apache.ibatis.type.EnumTypeHandler.getNullableResult(EnumTypeHandler.java:26)
	at org.apache.ibatis.type.BaseTypeHandler.getResult(BaseTypeHandler.java:86)
	at com.baomidou.mybatisplus.core.handlers.CompositeEnumTypeHandler.getResult(CompositeEnumTypeHandler.java:62)
	at com.baomidou.mybatisplus.core.handlers.CompositeEnumTypeHandler.getResult(CompositeEnumTypeHandler.java:37)
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.applyAutomaticMappings(DefaultResultSetHandler.java

mybatis 如何处理枚举?有一个自带处理oridal 就是按位置来 这个枚举声明容易修改位置
 

org.apache.ibatis.type.EnumOrdinalTypeHandler<E> :该类实现了枚举类型和Integer类型的相互转换。
但是给转换仅仅是将对应的枚举转换为其索引位置,也就是"ordinal()"方法获取到的值。对应自定义的int值,该类无能为力。


mybatis-plus 如何处理枚举?他是要写他自定义的注解 @EnumValue 或者实现接口IEnum接口

@Getter
public enum GenderEnum{

    WOMAN(0,"女"),
    MAN(1, "男");


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

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


import com.baomidou.mybatisplus.annotation.IEnum;

/**
 * 性别枚举
 */
public enum GenderEnum implements IEnum<Integer> {

    UNKNOWN(0, "未知"),
    MALE(1, "男"),
    FEMALE(2, "女");

    @JsonValue // 序列化枚举值为 接口出参;接口入参(RequestBody),反序列化为枚举值
    private final Integer value;

    private final String description;


    @Override
    public Integer getValue() {
        return value; // 标记数据库存的值是 value
    }

}

很明显我们一般只关心序列化的注解,那么又增加一套注解麻烦
平时也就使用jackson 注解 或者gson 注解
这里我给出一个全自动处理的方案

package com.nbxxf.kpower.spring.mybatis.typeHandler;

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.gson.annotations.SerializedName;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;

import java.io.IOException;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.security.PrivilegedAction;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 全局自动化处理枚举
 * 业务只需要
 * 对于jackson 只需要在枚举常量添加@JsonProperty注解
 * 对于gson 只需要在枚举常量添加需要@SerializedName
 *
 * @version v1.0
 * @authpr youxuan
 * @date 2024-03-26 10:16
 */
@MappedTypes(Enum.class)
public class AutoEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
    @SuppressWarnings("ALL")
    private static class EnumTypeAdapter<T extends Enum<T>> {
        final Map<String, T> nameToConstant = new HashMap<>();
        final Map<T, String> constantToName = new HashMap<>();

        public Field[] parse(final Class<T> classOfT) {
            try {
                Field[] constantFields = java.security.AccessController.doPrivileged(new PrivilegedAction<Field[]>() {
                    @Override
                    public Field[] run() {
                        Field[] fields = classOfT.getDeclaredFields();
                        ArrayList<Field> constantFieldsList = new ArrayList<>(fields.length);
                        for (Field f : fields) {
                            if (f.isEnumConstant()) {
                                constantFieldsList.add(f);
                            }
                        }

                        Field[] constantFields = constantFieldsList.toArray(new Field[0]);
                        AccessibleObject.setAccessible(constantFields, true);
                        return constantFields;
                    }
                });
                return constantFields;
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }

            Field[] fields = classOfT.getDeclaredFields();
            ArrayList<Field> constantFieldsList = new ArrayList<>(fields.length);
            for (Field f : fields) {
                if (f.isEnumConstant()) {
                    constantFieldsList.add(f);
                }
            }

            Field[] constantFields = constantFieldsList.toArray(new Field[0]);
            AccessibleObject.setAccessible(constantFields, true);
            return constantFields;
        }

        public EnumTypeAdapter(final Class<T> classOfT) {
            try {
                // Uses reflection to find enum constants to work around name mismatches for obfuscated classes
                // Reflection access might throw SecurityException, therefore run this in privileged context;
                // should be acceptable because this only retrieves enum constants, but does not expose anything else
                Field[] constantFields = parse(classOfT);
                for (Field constantField : constantFields) {
                    @SuppressWarnings("unchecked")
                    T constant = (T) (constantField.get(null));
                    String name = constant.name();
                    String toStringVal = constant.toString();

                    //jackson 方式
                    JsonProperty jsonProperty = constantField.getAnnotation(JsonProperty.class);
                    if (jsonProperty != null) {
                        name = jsonProperty.value();
                    }
                    JsonAlias jsonAlias = constantField.getAnnotation(JsonAlias.class);
                    if (jsonAlias != null) {
                        for (String alternate : jsonAlias.value()) {
                            nameToConstant.put(alternate, constant);
                        }
                    }

                    //gson
                    SerializedName annotation = constantField.getAnnotation(SerializedName.class);
                    if (annotation != null) {
                        name = annotation.value();
                        for (String alternate : annotation.alternate()) {
                            nameToConstant.put(alternate, constant);
                        }
                    }

                    nameToConstant.put(name, constant);
                    nameToConstant.put(toStringVal, constant);
                    constantToName.put(constant, name);
                }
            } catch (IllegalAccessException e) {
                throw new AssertionError(e);
            }
        }

        public T read(Object in) throws IOException {
            String key = in.toString();
            T constant = nameToConstant.get(key);
            return constant;
        }

        public T getValue(T in) throws IOException {
            String key = in.toString();
            T constant = nameToConstant.get(key);
            return constant;
        }
    }

    EnumTypeAdapter<E> enumTypeAdapter;

    public AutoEnumTypeHandler(Class<E> type) {
        if (type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        enumTypeAdapter = new EnumTypeAdapter(type);
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType)
            throws SQLException {
        if (jdbcType == null)
            ps.setString(i, enumTypeAdapter.constantToName.getOrDefault(parameter, parameter.name()));
        else
            ps.setObject(i, enumTypeAdapter.constantToName.getOrDefault(parameter, parameter.name()), jdbcType.TYPE_CODE);
    }

    @Override
    public E getNullableResult(ResultSet rs, String columnName)
            throws SQLException {
        return enumTypeAdapter.nameToConstant.get(rs.getString(columnName));
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex)
            throws SQLException {
        return enumTypeAdapter.nameToConstant.get(rs.getString(columnIndex));
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return enumTypeAdapter.nameToConstant.get(cs.getString(columnIndex));
    }
}


然后在application.properties 注册一下

mybatis-plus.configuration.default-enum-type-handler=com.nbxxf.kpower.spring.mybatis.typeHandler.AutoEnumTypeHandler

当然也可以通过代码来注册(找一个初始化bean的组件来调用下面的代码)
package com.nbxxf.kpower.spring.mybatis;

import com.baomidou.mybatisplus.core.handlers.CompositeEnumTypeHandler;
import com.nbxxf.kpower.spring.mybatis.typeHandler.AutoEnumTypeHandler;
import org.apache.ibatis.type.TypeHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;

public class MyBatisHelper {
    public static void setDefaultEnumTypeHandlerIfAbsent(ApplicationContext context) {
        if (!hasDefaultEnumTypeHandler(context)) {
            CompositeEnumTypeHandler.setDefaultEnumTypeHandler(AutoEnumTypeHandler.class);
        }
    }

    public static void setDefaultEnumTypeHandlerIfAbsent(ApplicationContext context, Class<? extends TypeHandler> defaultEnumTypeHandler) {
        if (!hasDefaultEnumTypeHandler(context)) {
            CompositeEnumTypeHandler.setDefaultEnumTypeHandler(defaultEnumTypeHandler);
        }
    }

    public static boolean hasDefaultEnumTypeHandler(ApplicationContext context) {
        Environment environment = context.getEnvironment();
        String defaultEnumTypeHandler = environment.getProperty("mybatis-plus.configuration.default-enum-type-handler");
        if (!StringUtils.hasLength(defaultEnumTypeHandler)) {
            defaultEnumTypeHandler = environment.getProperty("mybatis.configuration.default-enum-type-handler");
        }
        return StringUtils.hasLength(defaultEnumTypeHandler);
    }
}

这样我们写枚举 就不用关心其他第三方注解

* 全局自动化处理枚举
* 业务只需要
* 对于jackson 只需要在枚举常量添加@JsonProperty注解
* 对于gson 只需要在枚举常量添加需要@SerializedName

或者都不写序列化注解,那么会自动序列化枚举声明的字段名字 而不会导致报错

且两者jackson和gson共存,如果没有引入gson 那么将gson对应的报错删除就行

网站公告

今日签到

点亮在社区的每一天
去签到