配置好的项目是怎么样的:
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>