項目:人人開源 多數據源
<dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>2.5.5</version> </dependency>
問題: 多數據源在事務下不生效
問題原因: spring檢測到如果開啟了事務,會將Connection緩存起來,然后在事務再執行第二條sql時候,繼續使用之前緩存好的Connection
臨時解決方案:
1.不在事務內進行多數據源的切換(廢話)
2.添加注解,當前方法不在事務內運行
//當前方法不會運行在事務內,如果在事務內,則將事務掛起
@Transactional(propagation = Propagation.NOT_SUPPORTED)
3.異步(網上答案,親測失敗)
PS.以上方案在分布式中無效
源頭解決方案:
1、從源頭解決問題,我們在往Spring容器注冊事務管理器的時候,就可以更改其默認的事務管理器,從而Spring在選擇到該數據源的時候就知道用哪一個事務管理器了。下面是相應的配置代碼
@Autowired @Qualifier("ADataSource") private DataSource ADS; @Bean public PlatformTransactionManager uiTransactionManager() { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(ADS); return transactionManager; }
2、在使用 Transactional 注解的時候,我們會發現里面有一個值叫 value,也就是 transactionManager 的別名,在這里指定加載的事務管理器,也可以達到指定的效果。代碼如下:
@Configuration @EnableTransactionManagement @MapperScan(basePackages = "*.*.*.B",sqlSessionTemplateRef = "BSqlSessionTemplate") public class BDataSourceConfig { @Autowired @Qualifier("BDataSource") private DataSource BDS; @Bean public SqlSessionFactory mgSqlSessionFactory() throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); tk.mybatis.mapper.session.Configuration configuration = new tk.mybatis.mapper.session.Configuration(); configuration.setMapUnderscoreToCamelCase(true); bean.setConfiguration(configuration); bean.setDataSource(BDS); bean.setTypeAliasesPackage("*.*.*.B"); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:*.xml")); return bean.getObject(); } // Spring注冊Bean中,方法名即為Bean的名字 @Bean public PlatformTransactionManager BTransactionManager() { return new DataSourceTransactionManager(BDS); } @Bean public SqlSessionTemplate BSqlSessionTemplate(@Qualifier("BSqlSessionFactory") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } } --------配置代碼結束-------- -------業務代碼開始--------- @Transactional(value = "BTransactionManager") public void test(){ } -------業務代碼結束---------
3、手動控制事務管理器,和上面一種方案類似,但是這種方案更具源碼性吧,不用了Spring原生的 @Transactional 而改用自己實現的方式:
// 由於是多數據源,因此會有多個事務管理器,因此用Resource根據名稱的注入方式更為合理 @Resource PlatformTransactionManager ATransactionManager; @Transactional(rollbackFor = Exception.class) public boolean addMenu(MenuRequest request){ TransactionStatus status = ATransactionManager.getTransaction(new DefaultTransactionDefinition()); try { /** 業務代碼 **/ }catch (Exception e){ log.error("addMenu {}",e); ATransactionManager.rollback(status); return false; } ATransactionManager.commit(status); return true; }