首页 > 其他分享 >多数据源下@Transaction失效

多数据源下@Transaction失效

时间:2023-01-03 23:33:59浏览次数:29  
标签:Transaction String 数据源 失效 springframework env org import config

如springboot项目中,手工创建具体数据源,通过@Primary实现多数据源并存。其中遇到两个问题

问题1:@Bean实现时,加上@ConfigurationProperties(prefix = "spring.datasource.datasource1")读取不到具体配置,失效。这个暂时未查到具体原因。

问题2:@Transaction注解不生效,原因是多数据源后,@Transaction触发org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction时,因为没有指定使用哪个事务管理器。源码:

final TransactionManager tm = determineTransactionManager(txAttr);

org.springframework.transaction.interceptor.TransactionAspectSupport#determineTransactionManager
/**
	 * Determine the specific transaction manager to use for the given transaction.
	 */
	@Nullable
	protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
		// Do not attempt to lookup tx manager if no tx attributes are set
		if (txAttr == null || this.beanFactory == null) {
			return getTransactionManager();
		}

		String qualifier = txAttr.getQualifier();
		if (StringUtils.hasText(qualifier)) {
			return determineQualifiedTransactionManager(this.beanFactory, qualifier);
		}
		else if (StringUtils.hasText(this.transactionManagerBeanName)) {
			return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
		}
		else {
			TransactionManager defaultTransactionManager = getTransactionManager();
			if (defaultTransactionManager == null) {
				defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
				if (defaultTransactionManager == null) {
                                  // 均没有指定事务管理器的时候,会从spring factory里拿,这时候会拿到多个,通过@primary返回了默认的主bean
					defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
					this.transactionManagerCache.putIfAbsent(
							DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
				}
			}
			return defaultTransactionManager;
		}
	}

 解决方式:只需@Transaction(value = "datasource1")就可以指定到具体的事物管理器,事务管理器中,是有datasource属性的,所以如果mybatis操作时用的是datasource2,但回滚时用了datasource1的事务管理器,就相当于没回滚。

 

详细的多数据源实现:

package com.shopline.paymentacceptance.merchantservice.config;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

/**
 * @author : liangmingkun
 * @date : 2022/11/21 09:47
 * @Desc :
 */
@Configuration
public class DataSourceConfig {
    @Bean(name = "merchantAccountDatasource")
    // @ConfigurationProperties(prefix = "spring.datasource.merchant-account")
    public HikariDataSource merchantAccountDatasource(Environment env) {
        HikariConfig config= DataSourceConfigUtil.setDataSourceEnvConfig("spring.datasource.merchant-account.","spring.datasource.merchant-account.hikari.",env);
        return new HikariDataSource(config);
    }

    @Bean(name = "merchantKycDatasource")
    // @ConfigurationProperties(prefix = "spring.datasource.merchant-kyc")
    public HikariDataSource merchantKycDatasource(Environment env) {
        HikariConfig config= DataSourceConfigUtil.setDataSourceEnvConfig("spring.datasource.merchant-kyc.","spring.datasource.merchant-kyc.hikari.",env);
        return new HikariDataSource(config);
    }
}

  

package com.shopline.paymentacceptance.merchantservice.config;

import com.zaxxer.hikari.HikariConfig;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.Environment;

/**
 * @author : liangmingkun
 * @date : 2022/11/29 15:31
 * @Desc :
 */
public class DataSourceConfigUtil {

    public static HikariConfig setDataSourceEnvConfig(String prefix, String hikariPrefix, Environment env) {

        HikariConfig config = new HikariConfig();
        String driver = env.getProperty(prefix + "driverClassName");
        String dataSourceUrl = env.getProperty(prefix + "jdbcUrl");
        String user = env.getProperty(prefix + "username");
        String password = env.getProperty(prefix + "password");
        String minimumIdle = env.getProperty(hikariPrefix + "minimumIdle");
        String maximumPoolSize = env.getProperty(hikariPrefix + "maximumPoolSize");
        String autoCommit = env.getProperty(hikariPrefix + "autoCommit");
        String idleTimeout = env.getProperty(hikariPrefix + "idleTimeout");
        String poolName = env.getProperty(hikariPrefix + "poolName");
        String maxLifetime = env.getProperty(hikariPrefix + "maxLifetime");
        String connectionTimeout = env.getProperty(hikariPrefix + "connectionTimeout");
        String dataSourceClassName = env.getProperty(hikariPrefix + "type");
        if (StringUtils.isNotBlank(dataSourceUrl)) {
            config.setJdbcUrl(dataSourceUrl);
        }
        if (StringUtils.isNotBlank(user)) {
            config.setUsername(user);
        }
        if (StringUtils.isNotBlank(password)) {
            config.setPassword(password);
        }
        if (StringUtils.isNotBlank(driver)) {
            config.setDriverClassName(driver);
        }

        if (StringUtils.isNotBlank(minimumIdle)) {
            config.setMinimumIdle(Integer.parseInt(minimumIdle));
        }
        if (StringUtils.isNotBlank(maximumPoolSize)) {
            config.setMaximumPoolSize(Integer.parseInt(maximumPoolSize));
        }
        if (StringUtils.isNotBlank(autoCommit)) {
            config.setAutoCommit(Boolean.parseBoolean(autoCommit));
        }
        if (StringUtils.isNotBlank(idleTimeout)) {
            config.setIdleTimeout(Integer.parseInt(idleTimeout));
        }
        if (StringUtils.isNotBlank(poolName)) {
            config.setPoolName(poolName);
        }
        if (StringUtils.isNotBlank(maxLifetime)) {
            config.setMaxLifetime(Integer.parseInt(maxLifetime));
        }
        if (StringUtils.isNotBlank(connectionTimeout)) {
            config.setConnectionTimeout(Integer.parseInt(connectionTimeout));
        }
        return config;
    }
}

  

package com.shopline.paymentacceptance.merchantservice.config;

import javax.sql.DataSource;

import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.shopline.risk.compliance.persistent.SensitiveDataInterceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;

/**
 * @author : liangmingkun
 * @date : 2022/11/21 10:23
 * @Desc : 多数据源merchantAccount库 mybatis配置
 */
@Configuration
public class MerchantAccountDataSourceMybatisConfig {

    public static final String MERCHANT_ACCOUNT_SESSION_FACTORY = "merchantAccountSqlSessionFactory";

    @Autowired
    private SensitiveDataInterceptor sensitiveDataInterceptor;

    @Primary
    @Bean(name = MERCHANT_ACCOUNT_SESSION_FACTORY)
    public SqlSessionFactory merchantAccountSqlSessionFactory(@Qualifier(value="merchantAccountDatasource") DataSource merchantAccountDataSource) throws Exception {
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        factory.setDataSource(merchantAccountDataSource);
        //指定mapper位置
        // factory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        //分页插件
        PaginationInterceptor pageInterceptor = new PaginationInterceptor();
        //乐观锁插件
        OptimisticLockerInterceptor optimisticLockerInterceptor = new OptimisticLockerInterceptor();
        factory.setPlugins(pageInterceptor,optimisticLockerInterceptor,sensitiveDataInterceptor);
        return factory.getObject();
    }
    @Primary
    @Bean(name = "merchantAccountSqlSessionTemplate")
    public SqlSessionTemplate merchantAccountSqlSessionTemplate(@Qualifier("merchantAccountSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory);
        return template;
    }
    @Primary
    @Bean(name = "merchantAccountTransactionManager")
    public PlatformTransactionManager merchantAccountTransactionManager(@Qualifier(value="merchantAccountDatasource") DataSource merchantAccountDataSource) {
        return new DataSourceTransactionManager(merchantAccountDataSource);
    }
}

  

package com.shopline.paymentacceptance.merchantservice.config;

import javax.sql.DataSource;

import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.shopline.risk.compliance.persistent.SensitiveDataInterceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

/**
 * @author : liangmingkun
 * @date : 2022/11/21 10:23
 * @Desc : 多数据源merchantAccount库 mybatis配置
 */
@Configuration
public class MerchantKycDataSourceMybatisConfig {

    public static final String MERCHANT_KYC_SESSION_FACTORY = "merchantKycSqlSessionFactory";

    @Autowired
    private SensitiveDataInterceptor sensitiveDataInterceptor;


    @Bean(name = MERCHANT_KYC_SESSION_FACTORY)
    public SqlSessionFactory merchantKycSqlSessionFactory(@Qualifier(value="merchantKycDatasource") DataSource merchantKycDatasource) throws Exception {
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        factory.setDataSource(merchantKycDatasource);
        //指定mapper位置
        // factory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:merchantkyc/mapper/*.xml"));
        //分页插件
        PaginationInterceptor pageInterceptor = new PaginationInterceptor();
        //乐观锁插件
        OptimisticLockerInterceptor optimisticLockerInterceptor = new OptimisticLockerInterceptor();
        factory.setPlugins(pageInterceptor,optimisticLockerInterceptor,sensitiveDataInterceptor);
        return factory.getObject();
    }
    @Bean(name = "merchantKycSqlSessionTemplate")
    public SqlSessionTemplate merchantKycSqlSessionTemplate(@Qualifier("merchantKycSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory);
        return template;
    }
    @Bean(name = "merchantKycTransactionManager")
    public PlatformTransactionManager merchantKycTransactionManager(@Qualifier(value="merchantKycDatasource") DataSource merchantKycDatasource) {
        return new DataSourceTransactionManager(merchantKycDatasource);
    }
}

  附一篇@transaction分析的博客:https://blog.csdn.net/weixin_44771989/article/details/123899022

 

标签:Transaction,String,数据源,失效,springframework,env,org,import,config
From: https://www.cnblogs.com/klm-kain/p/17023704.html

相关文章