• 【原创】获取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的真实类型

    获取真实类型并没有想象中的那么难,看了很多博客写得异常复杂,其实解决方法相当简单:

    1. Class[] classes = AopProxyUtils.proxiedUserInterfaces(mapper);
    2. if (classes.length > 0) {
    3. mapperClass = (Class) classes[0];
    4. }

    不好意思,核心代码就4行,真的是4行,其中AopProxyUtils是org.springframework.aop.framework里的,也就是Spring框架自带的类。

    我试过了里面的ultimateTargetClass方法,并没有作用,反而是completeProxiedInterfaces获取第0个类就是我们要的结果。

    看一下completeProxiedInterfaces的源码:

    1. public static Class[] proxiedUserInterfaces(Object proxy) {
    2. Class[] proxyInterfaces = proxy.getClass().getInterfaces();
    3. int nonUserIfcCount = 0;
    4. if (proxy instanceof SpringProxy) {
    5. ++nonUserIfcCount;
    6. }
    7. if (proxy instanceof Advised) {
    8. ++nonUserIfcCount;
    9. }
    10. if (proxy instanceof DecoratingProxy) {
    11. ++nonUserIfcCount;
    12. }
    13. Class[] userInterfaces = (Class[])Arrays.copyOf(proxyInterfaces, proxyInterfaces.length - nonUserIfcCount);
    14. Assert.notEmpty(userInterfaces, "JDK proxy must implement one or more interfaces");
    15. return userInterfaces;
    16. }

    其实就是通过这个类获取其实现的接口列表,然后去除几个包装的Proxy接口,最后封装成一个数组返回出来,就这么简单。按照上面的getClass().getInterfaces(),能够解决获取很多Spring AOP注入的类的原本类型。

    优化一下获取方法

    1. public static extends BaseEntity, R extends BaseMapper> Class getNoProxyMapperClass(R mapper) {
    2. Class mapperClass = null;
    3. boolean cglibProxy = AopUtils.isCglibProxy(mapper);
    4. boolean aopProxy = AopUtils.isAopProxy(mapper);
    5. boolean jdkDynamicProxy = AopUtils.isJdkDynamicProxy(mapper);
    6. if (cglibProxy || aopProxy || jdkDynamicProxy) {
    7. Class[] classes = AopProxyUtils.proxiedUserInterfaces(mapper);
    8. if (classes.length > 0) {
    9. mapperClass = (Class) classes[0];
    10. } else {
    11. mapperClass = (Class) AopUtils.getTargetClass(mapper);
    12. }
    13. } else {
    14. mapperClass = (Class) mapper.getClass();
    15. }
    16. return mapperClass;
    17. }

    其中AopUtils也是Spring框架自己的方法,用于判断这个实例是否是代理出来的,如果是代理出来的,则使用AopProxyUtils.proxiedUserInterfaces()方法获取其真实类。如果非代理类,则直接getClass()即可。

    最后放出优化后的SqlUtil中的saveBatch方法

    关联博客:

    【原创】辟谣,实测MyBatisPlus批量新增/更新方法确实有效,且可单独使用无需跟随IService_DCTANT的博客-CSDN博客_mybatisplus批量新增

    1. public static extends BaseEntity, R extends BaseMapper> void saveBatch(R mapper, List entityList, Long userId, int batchSize, boolean keepInsertId) {
    2. if (entityList.size() == 0) {
    3. return;
    4. }
    5. T t = entityList.get(0);
    6. Class entityClass = (Class) t.getClass();
    7. Class mapperClass = getNoProxyMapperClass(mapper);
    8. SqlHelper.saveOrUpdateBatch(entityClass, mapperClass, log, entityList, batchSize, (sqlSession, entity) -> {
    9. // INFO: DCTANT: 2021/12/27 insert判断,返回true则是走insert代码,返回false则会走后面的update代码
    10. if (entity == null) {
    11. return false;
    12. }
    13. Long id = entity.getId();
    14. if (id == null) {
    15. // INFO: DCTANT: 2021/12/27 insert前加一些自己必要的业务逻辑,如setCreateTime、setDel、setVersion等等
    16. insertNecessaryField(entity, userId, keepInsertId);
    17. return true;
    18. } else {
    19. // INFO: DCTANT: 2021/12/27 去执行update的代码
    20. return false;
    21. }
    22. }, (sqlSession, entity) -> {
    23. // INFO: DCTANT: 2021/12/27 判断为update,然后执行必要操作
    24. if (entity == null) {
    25. return;
    26. }
    27. // INFO: DCTANT: 2021/12/27 update前加一些自己的业务逻辑,如setUpdateTime等等
    28. updateNecessaryField(entity, userId);
    29. MapperMethod.ParamMap param = new MapperMethod.ParamMap<>();
    30. // INFO: DCTANT: 2022/8/22 参数需要为Constants.ENTITY,也就是里面的et,否则会报et这个参数无法找到
    31. param.put(Constants.ENTITY, entity);
    32. String sqlStatement = SqlHelper.getSqlStatement(mapperClass, SqlMethod.UPDATE_BY_ID);
    33. sqlSession.update(sqlStatement, param);
    34. });
    35. }

    其中:

    SqlHelper是MybatisPlus自带的方法。

    insertNecessaryField、updateNecessaryField是给entity赋一些默认值的方法,比如设置创建时间、更新时间、删除标志的方法,不加也无所谓。

    主要解决了第一个入参为Class的问题。

  • 相关阅读:
    电子科技大学编译原理复习笔记(四):程序语言的设计
    Springboot整合分页插件pagehelper
    SAP ABAP 千分位字符串转换成金额
    AWS DynamoDB浅析
    软件安装教程1——Neo4j下载与安装
    python 定时器
    动态加载布局的技巧
    IDEFICS 简介: 最先进视觉语言模型的开源复现
    【CSS如何实现双飞翼布局】
    链表合并 分数 25
  • 原文地址:https://blog.csdn.net/DCTANT/article/details/127638744