Spring 引导不在异常时回滚事务

Spring Boot not rolling back transaction on exception

我有一个简单的代码,可以将某些内容保存到数据库,然后抛出异常以查看更改是否回滚。

ResourceA.java

@Autowire ServiceA serviceA;

@PUT
@Path("/{Id:[0-9]+}")
public ObjectA updateSomethingResource(..) {
   return serviceA.upateSomethingService(..);
}

ServiceA.java

@Transactional(rollbackFor=Exception.class)
public ObjectA upateSomethingService(EntitlementRequest entitlementRequest) throws ServiceException {

    ObjectA objectA = getObjectFromDB(..);
    objectA.setName("New Name");
    dao.save(objectA)
    throw new ServiceException("error"); //ServiceException extends Exception
}

执行此操作时,我检查了数据库,对象的名称仍然是 New Name。我会排除它回滚到原来的样子。

为了了解发生了什么,我尝试查看我可以启用的日志,而我设法启用的唯一日志是 logging.level.org.springframework.transaction.interceptor=TRACE

给出:

TRACE o.s.t.i.TransactionInterceptor - Getting transaction for [xxxxx.upateSomethingService] 
TRACE o.s.t.i.TransactionInterceptor - Completing transaction for [xxxxx.upateSomethingService] after exception: xxx.ServiceException 

因此事务管理器确实看到了异常,但没有任何回滚。

是否需要在某处启用某些功能才能使回滚真正起作用? 我可以启用哪些其他日志可以准确显示正在发生的事情?

更新: 我已经设法用 logging.level.org.springframework=DEBUG

显示矿石日志
DEBUG o.s.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource 
DEBUG o.s.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource 
DEBUG o.s.j.s.JdbcTransactionManager - Creating new transaction with name [xxx.service.upateSomethingService]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception 
DEBUG o.s.j.s.JdbcTransactionManager - Acquired Connection [1091371323, URL=jdbc:mysql://localhost:3306/table?allowMultiQueries=true, UserName=root@localhost, MySQL Connector/J] for JDBC transaction 
DEBUG o.s.j.s.JdbcTransactionManager - Switching JDBC Connection [1091371323, URL=jdbc:mysql://localhost:3306/table?allowMultiQueries=true, UserName=root@localhost, MySQL Connector/J] to manual commit 
DEBUG o.s.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource 
DEBUG o.s.j.s.JdbcTransactionManager - Initiating transaction rollback 
DEBUG o.s.j.s.JdbcTransactionManager - Rolling back JDBC transaction on Connection [1091371323, URL=jdbc:mysql://localhost:3306/table?allowMultiQueries=true, UserName=root@localhost, MySQL Connector/J] 
DEBUG o.s.j.s.JdbcTransactionManager - Releasing JDBC Connection [1091371323, URL=jdbc:mysql://localhost:3306/table?allowMultiQueries=true, UserName=root@localhost, MySQL Connector/J] after transaction 

查看这些日志.. spring 实际上告诉我它 'is' 回滚事务.. 但数据库中没有任何更改?

我刚刚注意到的奇怪的是,在 dao.save(objectA) 之后我已经可以看到数据库中的名称正在更改..所以 MyBatis 以某种方式自动提交并且看起来不像使用相同的名称连接有我在日志中看到的那个

这就是我设置数据库源和配置 MyBatis 的方式

(我有 2 个数据源,这就是我使用它而不是仅仅使用 application.properties 属性的原因。)

@Bean(name = MYBATIS_DATASOURCE_ONE, destroyMethod = "")
  public DataSource dataSource(....) throws SQLException {

    PooledDataSource dataSource = new PooledDataSource();
    dataSource.setDriver(driverclassname);
    dataSource.setUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(password);
    [...]
    return dataSource;
  }

@Bean(name = A_SESSION_FACTORY, destroyMethod = "")
  @Primary
  public SqlSessionFactory sqlSessionFactory(@Qualifier(MYBATIS_DATASOURCE_A) DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource);
    factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mybatis/mapper/*.xml"));
    final SqlSessionFactory sqlSessionFactory = factoryBean.getObject();
    return sqlSessionFactory;
  }

你最近的更新让它更清楚了......我认为正在发生的事情是 Spring 在实例化 DataSourceTransactionManager 时没有使用你手动定义的数据源。它可能正在使用 BasicDataSource 或类似的东西(使用 application.properties

中的属性创建的东西

MyBatis documentation clearly specifies that for Transaction to work the DataSourceTransactionManager needs to have the exact same Datasource the rest of your app is using otherwise it won't work.

将此添加到您的配置中,事务应该开始工作

@Bean
public DataSourceTransactionManager transactionManager(@Qualifier(MYBATIS_DATASOURCE_A) DataSource dataSource) {
  DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
  transactionManager.setDataSource(dataSource);
  return transactionManager;
}

通常,您不需要手动定义此 Bean,因为 Spring 会自动为您完成。但是由于您使用的是自己的数据源,因此您需要这样做。

如果你想验证这一点。不要声明此 bean 并在 DataSourceTransactionManager 构造函数中放置断点。 您应该注意到 DataSource 与您自己声明的实例不同。