Transactional
When annotating a method with @Transactional
, we declare that all the database execution within that method will be under a Database transaction.
For example
@Service
public class AuthorService {
private AuthorRepository authorRepository;
public AuthorService(AuthorRepository authorRepository) {
this.authorRepository = authorRepository;
}
@Transactional
public void updateAuthorNameTransaction() {
Author author = authorRepository.findById(1L).get();
author.setName("new name");
}
}
The @Transactional
saying that this method in here will be executed under a transaction. So everything has to go through or nothing will go through.
Transaction Propagation
Transaction propagation is for you to handle the use and creation of transactions
We can define the transaction propagation using the following
@Service
public class AuthorService {
private AuthorRepository authorRepository;
public AuthorService(AuthorRepository authorRepository) {
this.authorRepository = authorRepository;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateAuthorNameTransaction() {
Author author = authorRepository.findById(1L).get();
author.setName("new name");
}
}
We accept the following options:
REQUIRED
(default): either join an active transaction or join a new one if needed. It always need transactionSUPPORTS
: if there is an active transaction then join. Otherwise just execute without a transactional contextMANDATORY
: join an active transaction if one exist or throwException
if there is none active transactionNEVER
: should not be in a transaction context. If it's insidde one then throw an exceptionNOT_SUPPORTED
: if an active transaction exist, Spring will pause it and execute this one in non-transactional contextREQUIRES_NEW
: always start a new transaction for this method. If the method gets called with an active transaction, that transaction gets suspended until this method got executed.NESTED
: start a new transaction if the method gets called without an active transaction. If it gets called with an active transaction, Spring sets a savepoint and rolls back to that savepoint if an Exception occurs.
Read Only
Only allow this transaction to read
@Service
public class AuthorService {
private AuthorRepository authorRepository;
public AuthorService(AuthorRepository authorRepository) {
this.authorRepository = authorRepository;
}
@Transactional(readOnly = true)
public Author getAuthor() {
return authorRepository.findById(1L).get();
}
}
Rollback
We can rollback the transaction if there is something failed. Also we can declare not to rollback for a certain exceptions
@Service
public class AuthorService {
private AuthorRepository authorRepository;
public AuthorService(AuthorRepository authorRepository) {
this.authorRepository = authorRepository;
}
@Transactional(
rollbackFor = Exception.class,
noRollbackFor = EntityNotFoundException.class
)
public void updateAuthorName() {
Author author = authorRepository.findById(1L).get();
author.setName("new name");
}
}
Isolation
To control the I (isolation) in ACID
0. DEFAULT
By default, spring will use the database defined isolation
1. READ_UNCOMMITTED
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void log(String message) {
// ...
}
This only read the uncommited data from other concurrent transaction. Therefore we can get a different result on re-read.
Note: Postgres and Oracle does not support this operation. For Postgres it will automatically falls back to READ_COMMITTED
instead.
2. READ_COMMITTED
@Transactional(isolation = Isolation.READ_COMMITTED)
public void log(String message){
// ...
}
Prevent dirty read (the uncommitted) read. However, if some other concurrent transaction changed the result, our read could be changing.
Note: default for Postgres, Oracle and SQL server
3. REPEATABLE_READ
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void log(String message){
// ...
}
Prevent dirty, it guarantee that the read data cannot be changed. However new rows can still be added, but the read row are not allowed to be modified.
Note: default for MySQL, Oracle does not support REPEATABLE_READ
4. SERIALIZABLE
@Transactional(isolation = Isolation.SERIALIZABLE)
public void log(String message){
// ...
}
Prevent dirty, guarantee that the read data cannot be changed and new rows cannot be added for the concurrent transaction. This is blocking all the concurrent transaction.