本文主要是介绍SpringBoot 使用多SqlSessionFactory下的事务问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
如下配置了两个数据源:
spring:datasource:ds1:jdbc-url: jdbc:mysql://localhost:3307/spring-boot-demos?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: passwordds2:jdbc-url: jdbc:mysql://127.0.0.1:3308/spring-boot-demos?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: password
同时,配置了响应的数据源配置:
@Configuration
public class DataSourceConfigurations {@Configuration@MapperScan(basePackages = "com.example.mapper.ds1",sqlSessionFactoryRef = "ds1SqlSessionFactory")public static class Ds1Configuration {public static final String DS1_MAPPER_LOCATION = "classpath*:mapper/ds1/*.xml";@Primary@Bean("ds1DataSource")@ConfigurationProperties(prefix = "spring.datasource.ds1")public DataSource ds1DataSource() {return DataSourceBuilder.create().build();}@Primary@Bean("ds1TransactionManager")public PlatformTransactionManager ds1TransactionManager(@Qualifier("ds1DataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}@Primary@Bean(name = "ds1SqlSessionFactory")public SqlSessionFactory ds1SqlSessionFactory(@Qualifier("ds1DataSource") DataSource dataSource) throws Exception {MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean();sessionFactoryBean.setDataSource(dataSource);sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(DS1_MAPPER_LOCATION));return sessionFactoryBean.getObject();}}@Configuration@MapperScan(basePackages = "com.example.ds2",sqlSessionFactoryRef = "ds2SqlSessionFactory")public static class Ds2Configuration {public static final String DS2_MAPPER_LOCATION = "classpath*:mapper/ds2/*.xml";@Bean@ConfigurationProperties(prefix = "spring.datasource.ds2")public DataSource ds2DataSource() {return DataSourceBuilder.create().build();}@Beanpublic PlatformTransactionManager ds2TransactionManager(@Qualifier("ds2DataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}@Bean(name = "ds2SqlSessionFactory")public SqlSessionFactory ds2SqlSessionFactory(@Qualifier("ds2DataSource") DataSource dataSource) throws Exception {MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean();sessionFactoryBean.setDataSource(dataSource);sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(DS2_MAPPER_LOCATION));return sessionFactoryBean.getObject();}}
}
相对应的,有两个Mapper:
@Data
@TableName("tb_test1")
public class Test1DO {@TableIdprivate Integer id;private LocalDateTime createTime;private boolean deleted;private String name;private Short status;
}@Mapper
public interface Ds1Test1Mapper extends BaseMapper<Test1DO> {}@Mapper
public interface Ds2Test1Mapper extends BaseMapper<Test1DO> {}
首先,先向ds2数据库的tb_test1表中插入id=1的数据。然后做如下操作:
@RequiredArgsConstructor
@Service
public class Test1ServiceImpl implements ITest1Service {private final Ds1Test1Mapper ds1Test1Mapper;private final Ds2Test1Mapper ds2Test1Mapper;@Override@Transactional(rollbackFor = RuntimeException.class)public void save() {Test1DO test1DO = new Test1DO();test1DO.setId(1);test1DO.setName("test ds2");test1DO.setStatus((short) 1);ds1Test1Mapper.insert(test1DO);ds2Test1Mapper.insert(test1DO);}@Overridepublic void save1() {Test1DO test1DO = new Test1DO();test1DO.setId(1);test1DO.setName("test ds2");test1DO.setStatus((short) 1);ds1Test1Mapper.insert(test1DO);ds2Test1Mapper.insert(test1DO);}
}
当调用save1()时,ds1中插入了一条数据,而ds2中由于id冲突而失败。
如果调用save()时,ds1和ds2中都插入失败(ds1的数要清掉).
这个时候对save()进行断点:
//TransactionAspectSupport.java 377行 (springboot版本2.7.17)
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
发现ptm是上面配置的Primary TransactionManager。
如果将两个数据库表数据都清空,然后执行save(),对 commit 进行断点:
// DataSourceTransactionManager.doCommit()
protected void doCommit(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();Connection con = txObject.getConnectionHolder().getConnection();if (status.isDebug()) {logger.debug("Committing JDBC transaction on Connection [" + con + "]");}try {con.commit();}catch (SQLException ex) {throw translateException("JDBC commit", ex);}}
对 con 进行断点发现,其为3307的数据库的连接,然后整个过程没有别的连接了。并且3308的数据也能正常保存。
以上也就是两点:
1、当使用多个SqlSessionFactory配置数据源时,公用同一个TransactionManager,和同一个Connection(3307),这个时候,3308数据正常提交。
2、当提交3308的数据发生异常时,3307的数据也不会插入,即有事务属性。
目前对于上述两点不是很理解,为何只有3307的Connection,3308的数据也能提交,而且为何会有事务性。
可能是作者对SpringBoot的事务不是理解的很深入,希望有大佬能解惑。
这篇关于SpringBoot 使用多SqlSessionFactory下的事务问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!