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

Return the proxied datasource Bean with @primary