Atomikos Forum |
|
I wrote a similar question about this in the Spring forum, but perhaps my problem is something related to Atomikos, so I'm posting this here.
I have a small app that reads a few databases, and writes a small table in one database. I have a @Service-annotated class with a @Transactional method. That method calls a method in a DAO class (which is not @Repository-annotated) which first deletes some rows from a table and then inserts rows to the same table. This gets deployed to WebLogic. Under normal operation this app is working perfectly fine. I tried an experiment of deliberately mucking the SQL for the "insert", and deployed this to my local box, and then executed the JMX operation that executes this service operation. After it failed I checked the database, and I confirmed that the table was intact, so it correctly rolled back the "delete" when the "insert" failed. My problem is that my integration test (using Atomikos) that tries to simulate a similar scenario is NOT behaving transactionally. I mocked the JdbcTemplate so it performed the delete, but forced it to throw a DataAccessException on the insert. Afterwards, I checked the database, and the rows were gone, so it didn't rollback the delete as I hoped. The following is an excerpt from the context I'm using in test to define the "catalogTransactionManager", which is what is referenced in the rest of the context. ----------- <!-- Construct Atomikos UserTransactionManager, needed to configure Spring --> <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close"> <!-- when close is called, should we force transactions to terminate or not? --> <property name="forceShutdown"> <value>true</value> </property> </bean> <!-- Also use Atomikos UserTransactionImp, needed to configure Spring --> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> <property name="transactionTimeout"> <value>300</value> </property> </bean> <!-- Configure the Spring framework to use JTA transactions from Atomikos --> <bean id="catalogTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager"> <ref bean="atomikosTransactionManager" /> </property> <property name="userTransaction"> <ref bean="atomikosUserTransaction" /> </property> </bean> ----------- It probably doesn't matter, but here's my test method (with some things obfuscated): ----------- @Test public void testInsertFailsAfterDelete() { List<ErrorMessageInfo> commonErrorMessagesBefore = myService. getMyDAO().getCommonErrorMessages(MyService.CHANNEL_NAME); JdbcTemplate template = mock(JdbcTemplate.class); myService.getMyDAO().setJdbcTemplate(template); when(template.update(eq(MyDAO.SQL_DELETE_CHANNEL), any())). thenReturn(getOrigTemplate().update(MyDAO.SQL_DELETE_CHANNEL, MyService.CHANNEL_NAME)); DataAccessException exception = new DataAccessException("insert failed") {}; when(template.update(eq(MyDAO.SQL_INSERT_ERROR_TO_CHANNEL), anyString(), anyString(), anyInt(), any(), anyInt())). thenThrow(exception); try { myService.updateCommonErrorMessages(); fail(); } catch (Exception ex) { assertThat(ex).isEqualTo(exception); } finally { restoreTemplate(); } List<ErrorMessageInfo> commonErrorMessagesAfter = myService. getMyDAO().getCommonErrorMessages(MyService.CHANNEL_NAME); assertThat(commonErrorMessagesBefore).isEqualTo(commonErrorMessagesAfter); } -------------- What might I be missing?
You mocked the template - but the template is using DataAccessUtils in the back, which participates in handling connection and transaction for the DAO.
If not, no transaction boundaries get applied (or are you using weaved classed via aspectj instead of cglib/java proxies)? Either way - i guess your template does not participate in the spring managed transaction and thats the cause why it does fail; you can confirm this by looking at the database connection used to do your queries - should be different. Solution would be to create a custom impl of your DAO for your test, annotate it with @Primary if using it as @Autowire dep, and implement your scenario in this custom impl, e.g. throwing an exception. Created proxy should take care of rolling back the TX. Should work fine. |