如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