前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >动态mapper优先级问题

动态mapper优先级问题

作者头像
阿超
发布2023-03-19 09:45:30
3030
发布2023-03-19 09:45:30
举报
文章被收录于专栏:快乐阿超快乐阿超

之前使用byte-buddy实现mybatis-plus动态mapper

但是使用过程中发现一个问题,相关的issue链接:

https://gitee.com/VampireAchao/stream-query/issues/I6EJ27

在项目中已经定义了Mapper,如果在动态mapper已经注入的情况下,没法再通过Database.execute方法拿到,而是拿到的动态Mapper

进而导致大部分只要是基于execute方法的函数都是这样

于是为了解决这个问题,在DefaultSqlInjector处进行了处理

io.github.vampireachao.stream.plugin.mybatisplus.engine.configuration.StreamPluginAutoConfiguration下,当前的代码如下:

代码语言:javascript
复制
package io.github.vampireachao.stream.plugin.mybatisplus.engine.configuration;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import io.github.vampireachao.stream.core.lambda.LambdaHelper;
import io.github.vampireachao.stream.core.reflect.ReflectHelper;
import io.github.vampireachao.stream.plugin.mybatisplus.Database;
import io.github.vampireachao.stream.plugin.mybatisplus.engine.enumration.SqlMethodEnum;
import io.github.vampireachao.stream.plugin.mybatisplus.engine.methods.SaveOneSql;
import io.github.vampireachao.stream.plugin.mybatisplus.engine.methods.UpdateOneSql;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;

import java.util.List;

/**
 * MPSql注入
 *
 * @author VampireAchao Cizai_
 */
public class StreamPluginAutoConfiguration {

    private static final String CURRENT_NAMESPACE = LambdaHelper.getPropertyName(TableInfo::getCurrentNamespace);

    /**
     * <p>defaultSqlInjector.</p>
     *
     * @return a {@link com.baomidou.mybatisplus.core.injector.DefaultSqlInjector} object
     */
    @Bean
    @Order
    @ConditionalOnMissingBean(DefaultSqlInjector.class)
    public DefaultSqlInjector defaultSqlInjector() {
        return new DefaultSqlInjector() {
            @Override
            public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
                List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
                methodList.add(new SaveOneSql(SqlMethodEnum.SAVE_ONE_SQL.getMethod()));
                methodList.add(new UpdateOneSql(SqlMethodEnum.UPDATE_ONE_SQL.getMethod()));
                return methodList;
            }

            @Override
            public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
                super.inspectInject(builderAssistant, mapperClass);
                Class<?> modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0);
                if (modelClass == null) {
                    return;
                }
                TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
                if (Database.isDynamicMapper(tableInfo.getCurrentNamespace()) &&
                        !mapperClass.getName().equals(tableInfo.getCurrentNamespace())) {
                    // 降低动态mapper优先级
                    ReflectHelper.setFieldValue(tableInfo, CURRENT_NAMESPACE, mapperClass.getName());
                    Database.getEntityMapperClassCache().put(modelClass, mapperClass);
                }
            }
        };
    }

}

这里重写了inspectInject方法,判断了当前如果tableInfo内存入的是动态mapper,且两个mapper的类名不一致,则使用反射修改掉tableInfocurrentNamespace

进而使得优先获取到的是项目中的Mapper

相关的单元测试用例如下:

代码语言:javascript
复制
@Test
@SuppressWarnings("unchecked")
void testMapperPriority() {
    try (SqlSession sqlSession = SqlHelper.sqlSession(UserInfo.class)) {
        IMapper<UserInfo> userMapper = Database.getMapper(UserInfo.class, sqlSession);
        MybatisMapperProxy<UserInfoMapper> userMapperProxy =
                (MybatisMapperProxy<UserInfoMapper>) Proxy.getInvocationHandler(userMapper);
        Class<UserInfoMapper> userMapperClass = ReflectHelper.getFieldValue(userMapperProxy, "mapperInterface");
        Assertions.assertEquals(UserInfoMapper.class, userMapperClass);
        Assertions.assertFalse(Database.isDynamicMapper(userMapperClass.getName()));

        IMapper<RoleInfo> roleMapper = Database.getMapper(RoleInfo.class, sqlSession);
        MybatisMapperProxy<? super IMapper<RoleInfo>> roleMapperProxy =
                (MybatisMapperProxy<? super IMapper<RoleInfo>>) Proxy.getInvocationHandler(roleMapper);
        Class<? super IMapper<RoleInfo>> roleMapperClass = ReflectHelper.getFieldValue(roleMapperProxy, "mapperInterface");
        Assertions.assertTrue(Database.isDynamicMapper(roleMapperClass.getName()));
    }

}

此时的UserInfoMapper是项目中存在的

因为直接使用Database.getMapper获取到的是一个代理,所以使用了Proxy.getInvocationHandler(userMapper)以及反射获取mapperInterface属性来得到代理到的目标类型

两个mapper通过isDynamicMapper方法的对比:

不是动态Mapper

代码语言:javascript
复制
Assertions.assertFalse(Database.isDynamicMapper(userMapperClass.getName()));

是动态Mapper

代码语言:javascript
复制
Assertions.assertTrue(Database.isDynamicMapper(roleMapperClass.getName()));
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-03-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档