Spring Transactions - UnexpectedRollbackException in live, TransactionSystemException in tests - spring

We identified a transaction nesting issues after seeing UnexpectedRollbackExceptions in a production logs. This was because we handle specific database errors fairly close to the #Transactional persistence code, but further up at the service level was another #Transactional annotation that errored because the transaction was set to rollback only (as explained here: http://codetreasury.com/2014/01/25/springs-transactional-important-points/)
Our tests were initially too low level, so I added some further up the stack to confirm our fix, however the transaction exception thrown is different:
In live we see: UnexpectedRollbackException
In JUnit/Spring integration tests we see: TransactionSystemException
I'm concerned our tests are inconsistent with live code - can you explain why the exceptions are different?

Related

Why roll back transactions in a Spring test environment?

from spring doc:
One common issue in tests that access a real database is their effect
on the state of the persistence store. Even when you use a development
database, changes to the state may affect future tests. Also, many
operations — such as inserting or modifying persistent data — cannot
be performed (or verified) outside of a transaction.
The TestContext framework addresses this issue. By default, the
framework creates and rolls back a transaction for each test. You can
write code that can assume the existence of a transaction. If you call
transactionally proxied objects in your tests, they behave correctly,
according to their configured transactional semantics. In addition, if
a test method deletes the contents of selected tables while running
within the transaction managed for the test, the transaction rolls
back by default, and the database returns to its state prior to
execution of the test. Transactional support is provided to a test by
using a PlatformTransactionManager bean defined in the test’s
application context.
If you want a transaction to commit (unusual, but occasionally useful
when you want a particular test to populate or modify the database),
you can tell the TestContext framework to cause the transaction to
commit instead of roll back by using the #Commit annotation.
How can we be certain that a transactional test was successful if the transaction is rolled back after it? Perhaps the test will be failed as a result of the transaction failing upon commit, for instance because of a violation of a SQL constraint (in relational databases transactions). or, am I missing something?
As per my understanding, by default transaction got roll-back so that state of database remains unchanged.
For cases like unique constraint violation, ideally you should be verifying exception message/code which your application is throwing other than verifying state of transaction in unit tests.
Please note, you don't require to verify if rollback actually rolling back transaction or not, but you need to verify if error is thrown from your application after constraint violation occurred.
So your success criteria in this case is to check error is thrown after trying to insert duplicate record, and to check if error message which you have thrown from your method is correct.
For cases like update/insert to table, you can mark test case with explicit commit & verify it by executing select query within test; that will be your success criteria.

Envers audit table does not rollback in spring-boot integration tests annotated with #Transactional

I am trying to figure out why #Transactional does not rollback data in envers audit table after each test and how to fix it.
How can I make that happen in spring integration tests?
I have tried with #DirtiesContext and that makes it work but it's rather workaround and makes tests run much longer which I don't like.
Does any of you have any idea how to make it work?
Hibernate Envers acts as a transaction-commit-time audit solution. What that ultimately means is that all the persistence changes that happen during a transaction are examined and a set of work unit operations are cached in memory. In short, the only time Envers flushes operations to the audit schema is immediately before the commit of a successful transaction.
So how does this all work from a Hibernate point of view?
Hibernate Envers registers 2 very critical callback operations with Hibernate ORM when a transaction that operates on an audited entity is detected, a before and after transaction completion callback. The before callback is what actually performs the flushing of the audit changes to the audit tables and the after is responsible for cleaning up any resource allocations that are related to the transaction.
The only time that the before callback actually happens is when the hibernate transaction coordinator is asked to commit the transaction. If the transaction has been marked for rollback when the commit is requested, then the before callback is skipped.
The after callback always happens no matter the transaction status.
It would seem whats likely happening is #Transactional is creating the transaction boundary and the method is then invoked to perform its operations and when the method exits, the annotation forces a transaction commit leading to the observed behavior.
I can see a few options for you:
Override transaction behavior for tests to be read-only.
Design tests to clean-up their test data after validating the test use case.
Design tests to work even if existing test data exists in the tables.
Disable Envers via configuration if it isn't needed as a part of that integration test.

How to select PROPAGATION in spring transaction?

I am just reading the spring-mybatis.xml,here are some code of transaction-manager:
I want to know why some methods defines as "REQUIRED" or "SUPPORTS"?How to think about it and decide which to choose?
Your question is, I think it's about Spring transaction and it's depend on your business logic and how you can control spring transaction.
To understand the Spring transaction "REQUIRED" or "SUPPORTS", you need to understand spring transaction definition. This transaction definition types are come from org.springframework.transaction.TransactionDefinition class. But first you need to understand 1)Spring transaction types and then 2)Spring transaction Definition.
1) Spring supports two types of transaction management:
Programmatic transaction management: This means that you have manage the transaction with the help of programming. That gives you extreme flexibility, but it is difficult to maintain.
Declarative transaction management: This means you separate transaction management from the business code. You only use annotations or XML based configuration to manage the transactions.
2) Spring Transaction Definition
PROPAGATION_REQUIRED:
Spring REQUIRED behavior means that the same transaction will be used if there is an already opened transaction in the current bean method execution context. Create a new one if none exists.
In short this means that if an inner(2nd Transaction) method causes a transaction to rollback, the outer(1st Transaction) method will fail to commit and will also rollback the transaction.
PROPAGATION_SUPPORTS:
Support a current transaction; execute non-transactionally if none exists.
Understandanding these "REQUIRE" "SUPPORTS" is not enough, as I mentioned to you that you need to understand all Spring definition under org.springframework.transaction.TransactionDefinition class.
Unfortunately, I had one power point about this Spring types and transaction which I wrote at Dec 2014 in slideshare website.
Spring Transaction Management
In this slide, I added very important point about Spring transaction in power point note session. So please not only refer to slide content but also refer to slide note session. Hope it help.
Example, refer as power point notes session too for more understanding about Spring transaction definition.
Edited:
Propagation Means: Typically, all code executed within a transaction scope will run in that transaction. However, you have the option of specifying the behavior in the event that a transactional method is executed when a transaction context already exists. For example, code can continue running in the existing transaction (the common case); or the existing transaction can be suspended and a new transaction created. Spring offers all of the transaction propagation options familiar from EJB CMT. To read about the semantics of transaction propagation in Spring, see Transaction Propagation

Spring #Async - no data found in integration test

I'm trying to unit (integration) test a method annotated with Spring's #Async.
The test sets up some data in in-memory h2 database, then runs the asynchronous method. Asynchronous code does not see test data :O
Removing #Async fixes the problem.
Any help? :)
I had the same error. The solution was quite simple for me: I did not put a COMMIT; to the end of my data_init-h2.sql
I presume you did not put this either. If you think about it this is quite logical. Your main thread fires up a transaction but does not actually commit it to h2. Spring fires up another thread and the #Async method is run there in a separate transaction.
Because of the lack of commit you do not see the data changes on this other thread. On the main thread you can see your data changes even before they are committed as you are in that transaction.
The transaction isn't propagated like it was before your #Async.
#Async and #Transactional: not working
Your test could commit the data and delete it either side of the test, removing Spring's automated rollback inside test #Transactionals.
You could create a default-access method that the async method calls into, that your test could also call direct, though you would no longer be testing the Async behaviour.
There's likely a nicer spring implementation that supports what you need, making the transaction available but I don't have it.

JTA Callbacks in Spring

Is it possible to register some kind of callback with a JTA transaction in a Spring application?
I've got some mock services that are standing in for remote services that belong to another application that's normally accessed using Spring's HttpInvoker. These mock services model data in-memory in a trivial fashion using Maps and the like.
The Unit tests don't necessarily know which of these services might get used; the service the test case is targetting might use them behind the scenes.
The unit tests are transactional, and Spring's SpringJUnit4ClassRunner will rollback the transaction after each test, meaning that the state fo our unit test database is preserved between tests.
How can I rollback the state of this custom in-memory service implementation? If there was a way of finding out if there's a transaction currently going on, then I was hoping there'd be a way of registering a callback with the TransactionManager to be executed before the transaction is completed.
I don't think it's a good idea to clean up test mock in such an implicit way - tests usually perform cleanup explicitly.
However, if you really want, take a look at TransactionSynchronizationManager.registerSynchronization().

Resources