背景:
目前公司的一个SpringBoot项目,有一张表的一个字段需要存储枚举,之前保存的一直是枚举的code值,导致实体类的该属性是int类型,而不是枚举类型。技术领导希望我能解决这个问题,思路很简单,从网上找一下 “mybatis枚举自动转换”,应该不难。
结果:
但是看过大部分文章后,发现在 通用的自动转换 时,有点麻烦,大部分都建议枚举实现一个接口或在转换类中表明转换哪些枚举。经常仔细查看代码,根据我们项目的实际情况。编写出 枚举通用转换类,仅供参考。
废话不多说,直接上代码:
AutoEnumTypeHandler.class
package com.founderbn.common.mybatis.typeHandler;
import com.founderbn.common.mybatis.constants.EnumTransfer;
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;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
public class AutoEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
private Class<E> type;
public AutoEnumTypeHandler(Class<E> type) {
if (type == null) {
throw new IllegalArgumentException("Type argument cannot be null");
} else {
EnumTransfer transfer = (EnumTransfer)type.getAnnotation(EnumTransfer.class);
if (transfer == null) {
throw new RuntimeException(type.getSimpleName() + " 没有使用@EnumTransfer ,不明确转换方式");
} else {
this.type = type;
}
}
}
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
EnumTransfer transfer = (EnumTransfer)this.type.getAnnotation(EnumTransfer.class);
String value = transfer.value();
value = value.substring(0, 1).toUpperCase() + value.substring(1);
try {
Method method = this.type.getMethod("get" + value);
ps.setObject(i, method.invoke(parameter));
} catch (Exception var8) {
throw new RuntimeException("AutoEnumTypeHandler 转换枚举时,异常,请检查 EnumTransfer的value ", var8);
}
}
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
int code = rs.getInt(columnName);
return rs.wasNull() ? null : this.codeOf(code);
}
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int code = rs.getInt(columnIndex);
return rs.wasNull() ? null : this.codeOf(code);
}
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int code = cs.getInt(columnIndex);
return cs.wasNull() ? null : this.codeOf(code);
}
private E codeOf(int code) {
try {
return this.codeOf(this.type, code);
} catch (Exception var3) {
throw new IllegalArgumentException("Cannot convert " + code + " to " + this.type.getSimpleName() + " by code value.", var3);
}
}
public <E extends Enum<?>> E codeOf(Class<E> enumClass, int code) {
EnumTransfer transfer = (EnumTransfer)this.type.getAnnotation(EnumTransfer.class);
String value = transfer.value();
value = value.substring(0, 1).toUpperCase() + value.substring(1);
E[] enumConstants = (Enum[])enumClass.getEnumConstants();
Enum[] arr$ = enumConstants;
int len$ = enumConstants.length;
for(int i$ = 0; i$ < len$; ++i$) {
Enum e = arr$[i$];
try {
Method method = e.getClass().getMethod("get" + value);
try {
if (method.invoke(e).equals(code)) {
return e;
}
} catch (IllegalAccessException var12) {
var12.printStackTrace();
} catch (InvocationTargetException var13) {
var13.printStackTrace();
}
} catch (NoSuchMethodException var14) {
throw new RuntimeException(this.type.getSimpleName() + " 方法没有get" + value + "() 方法");
}
}
return null;
}
}
@EnumTransfer 注解
@Retention(RetentionPolicy.RUNTIME)
public @interface EnumTransfer {
String value() default "id";
}
原理:
1.AutoEnumTypeHandler.class 继承mybatis的 BaseTypeHandler类,重写方法。
2.在"写入"setNonNullParameter 方法内,通过注解的方式,获取到枚举想要入库的值
3.在 "查询"getNullableResult方法内,通过数据库值,反射枚举方法得到枚举值
整理思路还是很简单的,如果项目在定义枚举时,有一定的规范,可以更加简单些,不过我写的这个反射获取方法更加灵活,可以不同的枚举,选择入库id,code,name...自由选择。
ps:如果数据库对枚举入库没有要求,也可以不使用枚举转换方法,默认入库保存枚举的name,灵活变通。
springboot在使用该枚举工具栏时,需显示说明:
枚举:
@EnumTransfer
public enum Status {
STAUTS_ENABLE(1, "启用", "enable"),
STAUTS_DISABLE(2, "停用", "disable");
private int id;
private String name;
private String enname;
public int getId() {
return this.id;
}
public String getName() {
return this.name;
}
public String getEnname() {
return this.enname;
}
private Status(int id, String name, String enname) {
this.id = id;
this.name = name;
this.enname = enname;
}
public static Status getById(int id) {
switch(id) {
case 1:
return STAUTS_ENABLE;
case 2:
return STAUTS_DISABLE;
default:
return null;
}
}
}
显示说明引用:
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactoryBean() throws Exception{
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
//配置信息在application.yml
bean.setTypeAliasesPackage(environment.getProperty("mybatis.typeAliasesPackage"));
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// 添加XML目录
bean.setMapperLocations(resolver.getResources(environment.getProperty("mybatis.mapperLocations")));
SqlSessionFactory sqlSessionFactory = bean.getObject();
// 取得类型转换注册器
TypeHandlerRegistry typeHandlerRegistry = sqlSessionFactory.getConfiguration().getTypeHandlerRegistry();
// 注册默认枚举转换器
typeHandlerRegistry.register(Status.class,AutoEnumTypeHandler.class);
return sqlSessionFactory;
}
参考资料: mybatis枚举自动转换,主要是在他的基础上,改动的。