I. Description of the phenomenon
Distributed transaction selection Seata framework. After configuring Seata’s DataSourceProxy data source, it was found that the previously configured MyBatisPlus interceptor (paging, optimistic lock, data permission) was invalid.
Second, the process of solving this problem
The configuration is faulty
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import io.seata.rm.datasource.DataSourceProxy;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/** * Seata proxy data source configuration **@author YeMingXiang
* @date2021/08/26 * /
@Configuration
public class SeataDataSourceProxyConfig {
@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource) throws Exception {
// Mybatis -Plus is introduced in the service, so use the special SqlSessionFactoryBean
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
// Proxy data source
sqlSessionFactoryBean.setDataSource(new DataSourceProxy(dataSource));
/ / generated SqlSessionFactory
returnsqlSessionFactoryBean.getObject(); }}Copy the code
1, try to solve: for MybatisSqlSessionFactoryBean add plug-ins
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import io.seata.rm.datasource.DataSourceProxy;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/** * Seata proxy data source configuration **@author YeMingXiang
* @date2021/08/26 * /
@Configuration
public class SeataDataSourceProxyConfig {
@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource, MybatisPlusInterceptor mybatisPlusInterceptor)
throws Exception {
// Mybatis -Plus is introduced in the service, so use the special SqlSessionFactoryBean
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
/ / for MybatisSqlSessionFactoryBean add plug-ins
sqlSessionFactoryBean.setPlugins(mybatisPlusInterceptor);
// Proxy data source
sqlSessionFactoryBean.setDataSource(new DataSourceProxy(dataSource));
/ / generated SqlSessionFactory
returnsqlSessionFactoryBean.getObject(); }}Copy the code
I started the project to test and found that the paging function of MyBatisPlus was back to normal
But MyBatisPlus’s optimistic lock function is wrong!
2, another way to solve:
Check MybatisPlusAutoConfiguration source code, as follows:
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
/ / TODO use MybatisSqlSessionFactoryBean rather than SqlSessionFactoryBean
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)) {
factory.setPlugins(this.interceptors);
}
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);
}
Resource[] mapperLocations = this.properties.resolveMapperLocations();
if(! ObjectUtils.isEmpty(mapperLocations)) { factory.setMapperLocations(mapperLocations); }// TODO modified source code to support defining TransactionFactory
this.getBeanThen(TransactionFactory.class, factory::setTransactionFactory);
// TODO made some modifications to the source code (because the source code is adapted to the old version of Mybatis, but we don't need to adapt)
Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
if(! ObjectUtils.isEmpty(this.languageDrivers)) {
factory.setScriptingLanguageDrivers(this.languageDrivers);
}
Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver);
// TODO custom enum package
if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {
factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());
}
// TODO must be non-null
GlobalConfig globalConfig = this.properties.getGlobalConfig();
// TODO injection filler
this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);
// TODO injects the primary key generator
this.getBeanThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerator(i));
// TODO injects SQL injectors
this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);
// TODO injects the ID generator
this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);
/ / TODO GlobalConfig set to MybatisSqlSessionFactoryBean
factory.setGlobalConfig(globalConfig);
return factory.getObject();
}
Copy the code
MyBatisPlus does a lot more than just handle interceptors (and may be added in the future), so we’re better off using its default features. So what are we going to do?
See the sqlSessionFactory method in MyBatisPlus auto-configuration class, which also injects a data source?
Put the proxied data source into MyBatisPlus’s sqlSessionFactory.
The modified code is as follows:
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import io.seata.rm.datasource.DataSourceProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
/** * Seata proxy data source configuration **@author YeMingXiang
* @date2021/08/26 * /
@Configuration
public class SeataDataSourceProxyConfig {
@Bean
public DruidDataSource druidDataSource(a) {
return DruidDataSourceBuilder.create().build();
}
@Primary
@Bean
public DataSourceProxy dataSource(DruidDataSource druidDataSource) {
return newDataSourceProxy(druidDataSource); }}Copy the code
Test again, it has been solved
Three, matters needing attention
DruidDataSource creation method
DruidDataSourceWrapper (druiddatasourceDatasource); druiddatasourceDatasource (druiddatasourceDatasource); druiddatasourceDatasource (druiddatasourceDatasource);
@Bean
public DruidDataSource druidDataSource(a) {
return DruidDataSourceBuilder.create().build();
}
Copy the code