Mybatis Plus 与 Pagehelper 拦截器执行顺序问题
在使用Mybatis Plus中提供的数据权限拦截器相关处理时,使用Pagehelper进分页会出现查询count的sql中没有增加权限查询的sql拼接。
问题分析
:由于mybatis plus 与 pagehelper的实现都是通过mybatis的拦截器进行处理基本就能想到在拦截器执行的时候先走的Pagehelper的分页拦截器,导致权限没有拼接上去;
解决方式一
自行配置SqlSessionFactory
的创建,在代码factory.setPlugins
这部分内容里面修改自己的逻辑,将拦截器顺序修改一下即可
配置代码如下,下方配置参考的是MybatisPlusAutoConfiguration
中的配置
@EnableConfigurationProperties({PageHelperProperties.class, PageHelperStandardProperties.class,MybatisPlusProperties.class})
@Configuration
public class MybatisPlusConfig implements InitializingBean {
private final PageHelperProperties standardProperties;
private final MybatisPlusProperties properties;
private final Interceptor[] interceptors;
private final TypeHandler[] typeHandlers;
private final LanguageDriver[] languageDrivers;
private final ResourceLoader resourceLoader;
private final DatabaseIdProvider databaseIdProvider;
private final List<ConfigurationCustomizer> configurationCustomizers;
private final List<SqlSessionFactoryBeanCustomizer> sqlSessionFactoryBeanCustomizers;
private final List<MybatisPlusPropertiesCustomizer> mybatisPlusPropertiesCustomizers;
private final ApplicationContext applicationContext;
public MybatisPlusConfig(MybatisPlusProperties properties,
ObjectProvider<Interceptor[]> interceptorsProvider,
ObjectProvider<TypeHandler[]> typeHandlersProvider,
ObjectProvider<LanguageDriver[]> languageDriversProvider,
ResourceLoader resourceLoader,
ObjectProvider<DatabaseIdProvider> databaseIdProvider,
ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,
ObjectProvider<List<SqlSessionFactoryBeanCustomizer>> sqlSessionFactoryBeanCustomizers,
ObjectProvider<List<MybatisPlusPropertiesCustomizer>> mybatisPlusPropertiesCustomizerProvider,
ApplicationContext applicationContext,
PageHelperStandardProperties standardProperties) {
this.properties = properties;
this.interceptors = interceptorsProvider.getIfAvailable();
this.typeHandlers = typeHandlersProvider.getIfAvailable();
this.languageDrivers = languageDriversProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
this.sqlSessionFactoryBeanCustomizers = sqlSessionFactoryBeanCustomizers.getIfAvailable();
this.mybatisPlusPropertiesCustomizers = mybatisPlusPropertiesCustomizerProvider.getIfAvailable();
this.applicationContext = applicationContext;
this.standardProperties = standardProperties.getProperties();
}
@Override
public void afterPropertiesSet() {
if (!CollectionUtils.isEmpty(mybatisPlusPropertiesCustomizers)) {
mybatisPlusPropertiesCustomizers.forEach(i -> i.customize(properties));
}
checkConfigFileExists();
}
private void checkConfigFileExists() {
if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
Assert.state(resource.exists(),
"Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");
}
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
applyConfiguration(factory);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {
PageInterceptor interceptor = new PageInterceptor();
interceptor.setProperties(this.standardProperties);
LinkedList<Interceptor> list = new LinkedList<>(Arrays.stream(this.interceptors).toList());
list.addFirst(interceptor);
factory.setPlugins(list.toArray(new Interceptor[]{}));
}
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (this.properties.getTypeAliasesSuperType() != null) {
factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.typeHandlers)) {
factory.setTypeHandlers(this.typeHandlers);
}
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}
this.getBeanThen(TransactionFactory.class, factory::setTransactionFactory);
Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
if (!ObjectUtils.isEmpty(this.languageDrivers)) {
factory.setScriptingLanguageDrivers(this.languageDrivers);
}
Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver);
applySqlSessionFactoryBeanCustomizers(factory);
GlobalConfig globalConfig = this.properties.getGlobalConfig();
this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);
this.getBeanThen(AnnotationHandler.class, globalConfig::setAnnotationHandler);
this.getBeanThen(PostInitTableInfoHandler.class, globalConfig::setPostInitTableInfoHandler);
this.getBeansThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerators(i));
this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);
this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);
factory.setGlobalConfig(globalConfig);
return factory.getObject();
}
/**
* 检查spring容器里是否有对应的bean,有则进行消费
*
* @param clazz class
* @param consumer 消费
* @param <T> 泛型
*/
private <T> void getBeanThen(Class<T> clazz, Consumer<T> consumer) {
if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) {
consumer.accept(this.applicationContext.getBean(clazz));
}
}
/**
* 检查spring容器里是否有对应的bean,有则进行消费
*
* @param clazz class
* @param consumer 消费
* @param <T> 泛型
*/
private <T> void getBeansThen(Class<T> clazz, Consumer<List<T>> consumer) {
if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) {
final Map<String, T> beansOfType = this.applicationContext.getBeansOfType(clazz);
List<T> clazzList = new ArrayList<>();
beansOfType.forEach((k, v) -> clazzList.add(v));
consumer.accept(clazzList);
}
}
private void applyConfiguration(MybatisSqlSessionFactoryBean factory) {
MybatisPlusProperties.CoreConfiguration coreConfiguration = this.properties.getConfiguration();
MybatisConfiguration configuration = null;
if (coreConfiguration != null || !StringUtils.hasText(this.properties.getConfigLocation())) {
configuration = new MybatisConfiguration();
}
if (configuration != null && coreConfiguration != null) {
coreConfiguration.applyTo(configuration);
}
if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
customizer.customize(configuration);
}
}
factory.setConfiguration(configuration);
}
private void applySqlSessionFactoryBeanCustomizers(MybatisSqlSessionFactoryBean factory) {
if (!CollectionUtils.isEmpty(this.sqlSessionFactoryBeanCustomizers)) {
for (SqlSessionFactoryBeanCustomizer customizer : this.sqlSessionFactoryBeanCustomizers) {
customizer.customize(factory);
}
}
}
}
解决方式二
通过反射机制对SqlSessionFactory
中的拦截器进行排序
配置代码如下:
@Configuration
public class MybatisInterceptorConfig implements ApplicationListener<ContextRefreshedEvent> {
private final List<SqlSessionFactory> sqlSessionFactoryList;
// 这里设置拦截器顺序,要把PageInterceptor拦截器放在MybatisPlusInterceptor之前
// mybatis拦截器执行链路是通过(Plugin.wrap)动态代理方式创建的执行链路
// 靠前创建的代理对象执行时会越靠后,看懂Plugin.wrap中的代码就能理解
private final Map<Class<?>,Integer> sortNum = new HashMap<>(){
put(PageInterceptor.class,1);
put(MybatisPlusInterceptor.class,2);
}};
public MybatisInterceptorConfig(List<SqlSessionFactory> sqlSessionFactoryList) {
this.sqlSessionFactoryList = sqlSessionFactoryList;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (sqlSessionFactoryList != null && !sqlSessionFactoryList.isEmpty()) {
for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();
InterceptorChain interceptorChain = (InterceptorChain) geFieldValue(configuration, "interceptorChain");
List<Interceptor> interceptors = (List<Interceptor>) geFieldValue(interceptorChain, "interceptors");
interceptors.sort((o1, o2) -> {
Integer num1 = sortNum.get(o1.getClass());
Integer num2 = sortNum.get(o2.getClass());
if(num1 != null && num2 != null){
return num1.compareTo(num2);
}
return 0;
});
}
}
}
private Object geFieldValue(Object obj, String fieldName) {
Field field = ReflectionUtils.findField(obj.getClass(), fieldName);
field.setAccessible(true);
return ReflectionUtils.getField(field, obj);
}
}