如下配置了两个数据源:
spring:
  datasource:
    ds1:
      jdbc-url: jdbc:mysql://localhost:3307/spring-boot-demos?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
      username: root
      password: password
    ds2:
      jdbc-url: jdbc:mysql://127.0.0.1:3308/spring-boot-demos?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
      username: root
      password: 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();
        }
        @Bean
        public 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 {
    @TableId
    private 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);
    }
    @Override
    public 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的事务不是理解的很深入,希望有大佬能解惑。










