spring mybatis 自定义枚举类型处理器,并返回枚举字段全部属性。数据库如何存枚举类型? 如何存除name, oridinal 之外的值?

388 阅读2分钟

你是不是也有这样的需求:

PENDING(4, "审核中"),
APPROVED(5, "通过"),
REJECTED(6, "未通过");

如上三个枚举值,那么数据库中该如何存枚举值类型呢?

mybatis 有两个枚举类型处理器: (默认的是2)

  1. EnumOrdinalTypeHandler // Enum.ordinal() 存枚举的下标,如: 0
  2. EnumTypeHandler // Enum.name() 存枚举字符串,如: "PENDING"

那我如果想要存 PENDING(4, "审核中") 中的 4 为值该如何处理了?

如果你也有这样的需求,那么可以继续阅读。

自定义枚举类型处理器

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class CustomEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {

    private final Logger logger = LoggerFactory.getLogger(CustomEnumTypeHandler.class);

    private final Class<E> type;

    public CustomEnumTypeHandler(Class<E> type) {
        if (type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.type = type;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int index, E parameter, JdbcType jdbcType) throws SQLException {
        try {
            Method method = type.getMethod("getCode");
            int code = (int) method.invoke(Enum.valueOf(type, parameter.name()));
            ps.setInt(index, code);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        int code = rs.getInt(columnName);
        if (code == 0 && rs.wasNull()) {
            return null;
        }
        return toCodeEnum(ordinal);
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        int code = rs.getInt(columnIndex);
        if (code == 0 && rs.wasNull()) {
            return null;
        }
        return toCodeEnum(ordinal);
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        int code = cs.getInt(columnIndex);
        if (code == 0 && cs.wasNull()) {
            return null;
        }
        return toCodeEnum(ordinal);
    }

    private E toCodeEnum(int code) {
        try {
            Method method = type.getMethod("getEnum", int.class);
            return (E) method.invoke(type, code);
        } catch (Exception ex) {
            throw new IllegalArgumentException("Cannot convert " + ordinal + " to " + type.getSimpleName() + " by ordinal value.", ex);
        }
    }
}

枚举类代码

import com.fasterxml.jackson.annotation.JsonFormat;

import java.util.HashMap;
import java.util.Map;

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum AppointmentStatus {
    PENDING(4, "审核中"),
    APPROVED(5, "通过"),
    REJECTED(6, "未通过");

    static Map<Integer, AppointmentStatus> enumMap = new HashMap<Integer, AppointmentStatus>();
    static{
        for(AppointmentStatus appointmentStatus : AppointmentStatus.values()){
            enumMap.put(appointmentStatus.getCode(), appointmentStatus);
        }
    }

    private int code;

    private String name;

    private AppointmentStatus(int code, String name) {
        this.code = code;
        this.name = name;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public static AppointmentStatus getEnum(int code) {
        return enumMap.get(code);
    }
}

@JsonFormat(shape = JsonFormat.Shape.OBJECT)当字段属性是枚举类型时, 加上这条注解即可返回枚举的全部属性。如:

appointmentStatus: {
    code: 4,
    name: "审核中"
}

如果不加此注解,则返回的是: appointmentStatus: "PENDING"

最后 替换默认的枚举处理类型

@Bean
public SqlSessionFactory sqlSessionFactory(DriverManagerDataSource driverManagerDataSource) throws Exception {
    Resource resource[] = new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml");
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(driverManagerDataSource);
    sqlSessionFactoryBean.setMapperLocations(resource);
    sqlSessionFactoryBean.setPlugins(new PageInterceptor());
    Class<? extends TypeHandler> customEnumTypeHandler = CustomEnumTypeHandler.class;
    sqlSessionFactoryBean.setDefaultEnumTypeHandler(customEnumTypeHandler); // 替换默认的枚举处理类型
    return sqlSessionFactoryBean.getObject();
}