Spring Boot在异常情况下不会回滚事务

4

我有一段简单的代码,将一些东西保存到数据库,然后抛出异常,以查看更改是否被回滚。

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
}

执行此操作并检查数据库时,对象名称仍为新名称。我本来期望它会回滚到原始状态。
为了理解发生的情况,我尝试查看可以启用的日志记录,并且唯一成功启用的是 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实际上告诉我它正在"回滚"事务..但是数据库中没有任何更改?

有一件奇怪的事情我刚刚注意到,就是在dao.save(objectA)之后,我已经可以看到名称在数据库中被更改了..所以不知怎么回事,MyBatis自动提交并且似乎没有使用与日志中看到的连接相同的连接。

以下是我设置Databasource和配置MyBatis的方式:

(我有两个datasource,所以我使用这个方法而不是仅使用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;
  }


1
updateSomething也是事务性的吗?ServiceException是否继承Exception? - Simon Martinelli
1
从更新日志中我看到交易已经被回滚。 - Simon Martinelli
1
是的,但是数据库中的数据不是这样的。这意味着什么? DAO是否可能会创建一个新连接而不是使用Spring提供的连接?我这么想是因为如果我在DB保存后立即放置断点并手动检查DB,名称会直接更改。这不应该对吧?只有在事务提交时才应该更改,对吧? 我的意思是,如果我已经看到数据在DB中被更改... Spring可以回滚任何它想要的...数据已经被提交,所以我不确定它到底回滚了什么。 - Alexandre
你把自动提交关掉了吗? - Simon Martinelli
1
我应该这样做吗?难道不是交给事务管理器来处理吗? 就像日志中所示:DEBUG o.s.j.s.JdbcTransactionManager - 将 JDBC 连接 [1091371323, URL=jdbc:mysql://localhost:3306/table?allowMultiQueries=true, UserName=root@localhost, MySQL Connector/J] 切换到手动提交 - Alexandre
显示剩余2条评论
1个回答

4
你最新的更新使内容更加清晰了... 我认为正在发生的是,在实例化DataSourceTransactionManager时,Spring没有使用你手动定义的DataSource。它可能在使用一个类似于BasicDataSource的东西(使用你的application.properties中的属性创建的)。 MyBatis文档明确指出,为了使事务正常工作,DataSourceTransactionManager需要具有与应用程序的其余部分完全相同的数据源,否则它将无法工作。 将此添加到你的配置中,事务应该开始工作。
@Bean
public DataSourceTransactionManager transactionManager(@Qualifier(MYBATIS_DATASOURCE_A) DataSource dataSource) {
  DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
  transactionManager.setDataSource(dataSource);
  return transactionManager;
}

通常情况下,您不需要手动定义此Bean,因为Spring会自动为您完成。但是,由于您正在使用自己的DataSource,因此需要这样做。
如果您想要验证这一点,请不要声明此bean并在 DataSourceTransactionManager 构造函数中设置断点。您应该注意到,DataSource与您自己声明的实例不同。

哦,哇...确实,回滚现在已经可行。 - Alexandre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接