原创获取MybatisPlus注入的mapper的真实类型
Posted DCTANT
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原创获取MybatisPlus注入的mapper的真实类型相关的知识,希望对你有一定的参考价值。
前言
在之前的博客中我提到了@Autowired出来的mapper是AOP注入出来的代理类,如果直接使用其getClass获取到的是代理类型,而不是mapper的真实类型,这就会导致批量编辑报错,报:
Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.sun.proxy.$Proxy129.updateById
如我上篇博客所示:
【报错记录】MybatisPlus报Mapped Statements collection does not contain value for..._DCTANT的博客-CSDN博客
但是这篇博客做的还是不够优雅,不够优雅的地方在于我明明已经Autowired过mapper示例了,传入的值却是一个Class,感觉非常糟糕!而且文中说“且无解”!这是完全不对滴!
获取mapper的真实类型
获取真实类型并没有想象中的那么难,看了很多博客写得异常复杂,其实解决方法相当简单:
Class<?>[] classes = AopProxyUtils.proxiedUserInterfaces(mapper);
if (classes.length > 0)
mapperClass = (Class<R>) classes[0];
不好意思,核心代码就4行,真的是4行,其中AopProxyUtils是org.springframework.aop.framework里的,也就是Spring框架自带的类。
我试过了里面的ultimateTargetClass方法,并没有作用,反而是completeProxiedInterfaces获取第0个类就是我们要的结果。
看一下completeProxiedInterfaces的源码:
public static Class<?>[] proxiedUserInterfaces(Object proxy)
Class<?>[] proxyInterfaces = proxy.getClass().getInterfaces();
int nonUserIfcCount = 0;
if (proxy instanceof SpringProxy)
++nonUserIfcCount;
if (proxy instanceof Advised)
++nonUserIfcCount;
if (proxy instanceof DecoratingProxy)
++nonUserIfcCount;
Class<?>[] userInterfaces = (Class[])Arrays.copyOf(proxyInterfaces, proxyInterfaces.length - nonUserIfcCount);
Assert.notEmpty(userInterfaces, "JDK proxy must implement one or more interfaces");
return userInterfaces;
其实就是通过这个类获取其实现的接口列表,然后去除几个包装的Proxy接口,最后封装成一个数组返回出来,就这么简单。按照上面的getClass().getInterfaces(),能够解决获取很多Spring AOP注入的类的原本类型。
优化一下获取方法
public static <T extends BaseEntity, R extends BaseMapper<T>> Class<R> getNoProxyMapperClass(R mapper)
Class<R> mapperClass = null;
boolean cglibProxy = AopUtils.isCglibProxy(mapper);
boolean aopProxy = AopUtils.isAopProxy(mapper);
boolean jdkDynamicProxy = AopUtils.isJdkDynamicProxy(mapper);
if (cglibProxy || aopProxy || jdkDynamicProxy)
Class<?>[] classes = AopProxyUtils.proxiedUserInterfaces(mapper);
if (classes.length > 0)
mapperClass = (Class<R>) classes[0];
else
mapperClass = (Class<R>) AopUtils.getTargetClass(mapper);
else
mapperClass = (Class<R>) mapper.getClass();
return mapperClass;
其中AopUtils也是Spring框架自己的方法,用于判断这个实例是否是代理出来的,如果是代理出来的,则使用AopProxyUtils.proxiedUserInterfaces()方法获取其真实类。如果非代理类,则直接getClass()即可。
最后放出优化后的SqlUtil中的saveBatch方法
关联博客:
【原创】辟谣,实测MyBatisPlus批量新增/更新方法确实有效,且可单独使用无需跟随IService_DCTANT的博客-CSDN博客_mybatisplus批量新增
public static <T extends BaseEntity, R extends BaseMapper<T>> void saveBatch(R mapper, List<T> entityList, Long userId, int batchSize, boolean keepInsertId)
if (entityList.size() == 0)
return;
T t = entityList.get(0);
Class<T> entityClass = (Class<T>) t.getClass();
Class<R> mapperClass = getNoProxyMapperClass(mapper);
SqlHelper.saveOrUpdateBatch(entityClass, mapperClass, log, entityList, batchSize, (sqlSession, entity) ->
// INFO: DCTANT: 2021/12/27 insert判断,返回true则是走insert代码,返回false则会走后面的update代码
if (entity == null)
return false;
Long id = entity.getId();
if (id == null)
// INFO: DCTANT: 2021/12/27 insert前加一些自己必要的业务逻辑,如setCreateTime、setDel、setVersion等等
insertNecessaryField(entity, userId, keepInsertId);
return true;
else
// INFO: DCTANT: 2021/12/27 去执行update的代码
return false;
, (sqlSession, entity) ->
// INFO: DCTANT: 2021/12/27 判断为update,然后执行必要操作
if (entity == null)
return;
// INFO: DCTANT: 2021/12/27 update前加一些自己的业务逻辑,如setUpdateTime等等
updateNecessaryField(entity, userId);
MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
// INFO: DCTANT: 2022/8/22 参数需要为Constants.ENTITY,也就是里面的et,否则会报et这个参数无法找到
param.put(Constants.ENTITY, entity);
String sqlStatement = SqlHelper.getSqlStatement(mapperClass, SqlMethod.UPDATE_BY_ID);
sqlSession.update(sqlStatement, param);
);
其中:
SqlHelper是MybatisPlus自带的方法。
insertNecessaryField、updateNecessaryField是给entity赋一些默认值的方法,比如设置创建时间、更新时间、删除标志的方法,不加也无所谓。
主要解决了第一个入参为Class的问题。
以上是关于原创获取MybatisPlus注入的mapper的真实类型的主要内容,如果未能解决你的问题,请参考以下文章
定义了Mapper接口,但是没有写任何SQL,MybatisPlus是如何知道该查询哪张表呢?