Can someone explain the isolation & propagation parameters in the @Transactional annotation via a real-world example?
Basically when and why I should choose to change their default values?
Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.
Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.
Lost your password? Please enter your email address. You will receive a link and will create a new password via email.
Please briefly explain why you feel this question should be reported.
Please briefly explain why you feel this answer should be reported.
Please briefly explain why you feel this user should be reported.
Good question, although not a trivial one to answer.
Propagation
Defines how transactions relate to each other. Common options:
REQUIRED: Code will always run in a transaction. Creates a new transaction or reuses one if available.REQUIRES_NEW: Code will always run in a new transaction. Suspends the current transaction if one exists.The default value for
@TransactionalisREQUIRED, and this is often what you want.Isolation
Defines the data contract between transactions.
ISOLATION_READ_UNCOMMITTED: Allows dirty reads.ISOLATION_READ_COMMITTED: Does not allow dirty reads.ISOLATION_REPEATABLE_READ: If a row is read twice in the same transaction, the result will always be the same.ISOLATION_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.
Defaults may vary between difference databases. As an example, for MariaDB it is
REPEATABLE READ.Example of when a dirty read can occur:
So a sane default (if such can be claimed) could be
ISOLATION_READ_COMMITTED, which only lets you read values which have already been committed by other running transactions, in combination with a propagation level ofREQUIRED. Then you can work from there if your application has other needs.A practical example of where a new transaction will always be created when entering the
provideServiceroutine and completed when leaving:Had we instead used
REQUIRED, the transaction would remain open if the transaction was already open when entering the routine.Note also that the result of a
rollbackcould 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:
With a propagation level of
REQUIRES_NEW: we would expectfooService.provideService()was NOT rolled back since it created its own sub-transaction.REQUIRED: we would expect everything was rolled back and the backing store was unchanged.