@Transactional
Defines how transactions relate to each other. Common options
-
Required
: Code will always run in a transaction. Create a new transaction or reuse one if availble. -
Requires_new
: Code will always run in a new transaction. Suspend current transaction if one exist.
Defines the data contract between transactions.
-
Read Uncommitted
: Allows dirty reads -
Read Committed
: Does not allow dirty reads -
Repeatable Read
: If a row is read twice in the same transaciton, result will always be the same -
Serializable
: Performs all transactions in a sequence
The different levels have different performance characteristics in a multi threaded application. I think if you understand the dirty reads
concept you will be able to select a good option.
Example when a dirty read can occur
thread 1 thread 2||
write(x)|||| read(x)||
rollback |
v v
value (x) is now dirty (incorrect)
So a sane default (if such can be claimed) could be Read Comitted
, which only lets you read values which have already been comitted by other running transactions, in combination with an isolation level of Required
. Then you can work from there if you application has other needs.
A practical example where a new transaction will always be created when entering the provideService
routine and completed when leaving.
publicclassFooService{privateRepository repo1;privateRepository repo2;@Transactional(propagation=Propagation.REQUIRES_NEW)publicvoid provideService(){
repo1.retrieveFoo();
repo2.retrieveFoo();}}
Had we used Required
instead the transaction will remain open if the transaction was already open when entering the routine. Note also that the result of a rollback
could be different as several executions could take part in the same transaction.
We can easily verify the behaviour with a test and see how results differ with propagation levels
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations="classpath:/fooService.xml")publicclassFooServiceTests{private@AutowiredTransactionManager;private@AutowiredFooService fooService;@Testpublicvoid testProvideService(){TransactionStatus status = transactionManager.getTransaction(newDefaultTransactionDefinition());
fooService.provideService();
transactionManager.rollback(status);// assert repository values are unchanged ... }