当前位置: 首页 > news >正文

mybatis-plus使用@EnumValue搭配shardingsphere报错“getObject with type”

目录

  • 一、背景
  • 二、修改方案
  • 三、如何让修改的TypeHandler生效
    • 1、在@TableField中配置TypeHandler
    • 2、考虑直接在TypeHandlerRegistry注册该枚举的handler为自定义的handler处理类。
    • 3、不止重写MybatisEnumTypeHandler,还重写CompositeEnumTypeHandler类
      • 3.1、修改CompositeEnumTypeHandler类的方法并新建ShardingMybatisEnumTypeHandler类
      • 3.2、使ShardingCompositeEnumTypeHandler生效
  • 四、总结

一、背景

最近做项目时使用到了mybatis-plus和shardingsphere。只要在PO中使用了EnumValue注解,加载该PO时就会报“getObject with type”,不使用shardingsphere就不会出现这样的问题。查看报错的堆栈过后发现报错最后在shardingsphere的AbstractUnsupportedOperationResultSet抽象类。实际调用的是该抽象类的子类ShardingResultSet ,但是该子类没有重写父类的 getObject(final int columnIndex, final Class type) 方法,只重写了**getObject(final int columnIndex)**方法,但是在mybatis-plus的枚举类型转换时,调用的是 getObject(final int columnIndex, final Class type) 方法,所以在类型转换时会报错。

二、修改方案

先看下原来的调用层级:类型转换处理类调用CompositeEnumTypeHandler,CompositeEnumTypeHandler再根据当前枚举类是否使用了@EnumValue注解,如果使用了就调用MybatisEnumTypeHandler,如果没使用,就调用的EnumTypeHandler。
流程图

这里其实知道原因过后就简单了,因为知道getObject这个方法需要调用只有一个参数的方法。那么这里我们只需要重新定义一个TypeHandler就可以了。所以我们这里重新定义了一个自己的ShardingMybatisEnumTypeHandler,该handler在getNullableResult的方法时,调用的是**getObject(final int columnIndex)方法。但是这里还需要判断当前是否使用了shardingSphere,如果没使用,还是调用getObject(final int columnIndex, final Class type)**方法。ShardingMybatisEnumTypeHandler类相对于MybatisEnumTypeHandler,修改了getNullableResult方法,具体如下

    @Overridepublic E getNullableResult(ResultSet rs, String columnName) throws SQLException {Object value = rs.getObject(columnName);//原来为 rs.getObject(columnName, this.propertyType);if (null == value || rs.wasNull()) {return null;}return this.valueOf(value);}

三、如何让修改的TypeHandler生效

1、在@TableField中配置TypeHandler

这个方案失败了。枚举类型的处理类依然是CompositeEnumTypeHandler。

2、考虑直接在TypeHandlerRegistry注册该枚举的handler为自定义的handler处理类。

但是这个有个弊端,只要涉及使用@EnumValue注解的枚举都需要手动指定使用这个Handler,不太通用

3、不止重写MybatisEnumTypeHandler,还重写CompositeEnumTypeHandler类

3.1、修改CompositeEnumTypeHandler类的方法并新建ShardingMybatisEnumTypeHandler类

先看下CompositeEnumTypeHandler类的实现

public class CompositeEnumTypeHandler<E extends Enum<E>> implements TypeHandler<E> {private static final Map<Class<?>, Boolean> MP_ENUM_CACHE = new ConcurrentHashMap<>();private static Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;private final TypeHandler<E> delegate;public CompositeEnumTypeHandler(Class<E> enumClassType) {if (enumClassType == null) {throw new IllegalArgumentException("Type argument cannot be null");}if (CollectionUtils.computeIfAbsent(MP_ENUM_CACHE, enumClassType, MybatisEnumTypeHandler::isMpEnums)) {delegate = new MybatisEnumTypeHandler<>(enumClassType);} else {delegate = getInstance(enumClassType, defaultEnumTypeHandler);}}
......
}

从上面代码我们也看到了CompositeEnumTypeHandler在调用构造函数时,将MybatisEnumTypeHandler写死在了代码中。那么我们重新写一个ShardingCompositeEnumTypeHandler类,将上面的代码的MybatisEnumTypeHandler改为自己的ShardingMybatisEnumTypeHandler。

public class ShardingCompositeEnumTypeHandler<E extends Enum<E>> implements TypeHandler<E> {private static final Map<Class<?>, Boolean> MP_ENUM_CACHE = new ConcurrentHashMap<>();private static Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;private final TypeHandler<E> delegate;public ShardingCompositeEnumTypeHandler(Class<E> enumClassType) {if (enumClassType == null) {throw new IllegalArgumentException("Type argument cannot be null");}if (CollectionUtils.computeIfAbsent(MP_ENUM_CACHE, enumClassType, MybatisEnumTypeHandler::isMpEnums)) {delegate = new ShardingMybatisEnumTypeHandler<>(enumClassType);//原来为new MybatisEnumTypeHandler<>(enumClassType);} else {delegate = getInstance(enumClassType, defaultEnumTypeHandler);}}
......
}

3.2、使ShardingCompositeEnumTypeHandler生效

为了让我们的ShardingCompositeEnumTypeHandler在代码中生效,我们还需要在项目启动时,做一些其他操作。
我们可以通过代码发现,CompositeEnumTypeHandler是在MybatisConfiguration初始化的时候指定

    public MybatisConfiguration() {super();this.mapUnderscoreToCamelCase = true;typeHandlerRegistry.setDefaultEnumTypeHandler(CompositeEnumTypeHandler.class);languageRegistry.setDefaultDriverClass(MybatisXMLLanguageDriver.class);}

MybatisConfiguration还提供了setDefaultEnumTypeHandler方法,和getTypeHandlerRegistry。这里直接在项目启动时定义一个configurantion类。

//
@ConditionalOnProperty(prefix = "spring.shardingsphere", name = "enabled", havingValue = "true", matchIfMissing = true) //这个还是非常重要的,只在使用了sharding sphere的时候使用ShardingCompositeEnumTypeHandler
@Configuration
public class ShardingConfig implements ApplicationContextAware{private ApplicationContext applicationContext;void setApplicationContext(ApplicationContext applicationContext){this.applicationContext = applicationContext;}@PostConstructpublic void init(){//获取sqlSessionFactorySqlSessionFactory sqlSessionFactory = applicationContext.getBean(SqlSessionFactory.class);//获取typeHandlerRegistryTypeHandlerRegistry typeHandlerRegistry = sqlSessionFactory.getConfiguration().getTypeHandlerRegistry();//设置DefaultEnumTypeHandlertypeHandlerRegistry.setDefaultEnumTypeHandler(ShardingCompositeEnumTypeHandler.class);}
}

四、总结

1、需要自定义一个TypeHandler
2、需要将该TypeHandler生效。直观的体现就是在TypeHandlerRegistry中查看allTypeHandlersMap对应的类的handler。如果当前的typeHandler没生效,那么就看下生效的handler在什么时候创建的。


http://www.mrgr.cn/news/22354.html

相关文章:

  • C++17: 用折叠表达式实现一个IsAllTrue函数
  • 同样数据源走RTMP播放延迟低还是RTSP低?
  • 一文搞定postgreSQL
  • 记一次学习--内网穿透
  • 抖音素材网站有哪些?这几个高质量的抖音无水印素材网站分享
  • pytyon之raise
  • VMware安装Ubuntu虚拟机
  • Adobe Sensei——自动化视频编辑、特效应用和素材增强,通过AI技术快速优化视频内容,自动修复视频质量、自动添加背景音乐或字幕
  • Linux 开机自动启动服务
  • 缓存穿透问题
  • ASIO网络调试助手之二:网络通信原理
  • 梧桐数据库(WuTongDB):非聚簇索引的原理、实现方法及应用场景
  • 如何选择SDR无线图传方案
  • JavaScript 实用技巧
  • (一)【react-router v6】react-router v6基本用法,带你彻底搞懂前端路由原理、react-router V6带来了什么
  • AI编程工具合集
  • OPCUA-PLC
  • CCF刷题计划——因子化简
  • 每天五分钟玩转深度学习框架PyTorch:将nn的神经网络层连接起来
  • PHP-FPM 远程代码执行漏洞(CVE-2019-11043)复现