I have a straightforward code that saves something to the DB and then throws an exception to see if the changes get rollback.
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
}
When executing this and I check the DB the name of the object is still New Name. I would have excepted it to be rolled back to what it was originally.
To understand what is happening I have tried looking at what log I can enable and the only one that I managed to enable is
logging.level.org.springframework.transaction.interceptor=TRACE
which gives:
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
So the transaction manager does see the exception but nothing gets rollback.
Is there something I need to enable somewhere to have rollback actually working? What other log can I enable that could show me exactly what is happening?
UPDATE:
I have managed to show ore logs with 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
Looking at these logs.. spring is actually telling me it 'is' rolling back the transaction.. but nothing gets changed in the DB ?
Something odd that I just noticed is that right after dao.save(objectA) I can already see the name being changed in the DB.. so somehow MyBatis is auto committing and doesn't look like it is using the same connection has the one I see in the logs
This is how I setup the Databasource and configure MyBatis
(I have 2 datasource this is why I'm using this instead of just using application.properties 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;
}