甚至不愿听朋友说真话的人,是真正不可救药的人——西塞罗
之前使用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
下,当前的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| 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;
public class StreamPluginAutoConfiguration {
private static final String CURRENT_NAMESPACE = LambdaHelper.getPropertyName(TableInfo::getCurrentNamespace);
@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())) { ReflectHelper.setFieldValue(tableInfo, CURRENT_NAMESPACE, mapperClass.getName()); } if (!Database.isDynamicMapper(mapperClass.getName())) { Database.getEntityMapperClassCache().put(modelClass, mapperClass); } } }; }
}
|
这里重写了inspectInject
方法,判断了当前如果tableInfo
内存入的是动态mapper
,且两个mapper
的类名不一致,则使用反射修改掉tableInfo
的currentNamespace
进而使得优先获取到的是项目中的Mapper
相关的单元测试用例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @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
1
| Assertions.assertFalse(Database.isDynamicMapper(userMapperClass.getName()));
|
是动态Mapper
1
| Assertions.assertTrue(Database.isDynamicMapper(roleMapperClass.getName()));
|