【原创】获取MybatisPlus注入的mapper的真实类型

18 篇文章 0 订阅
8 篇文章 0 订阅

前言

在之前的博客中我提到了@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的问题。

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值