Spring Boot Transaction Management - Yash-777/MyWorld GitHub Wiki
Spring Boot + Transaction Management javainuse.com, youtube
@Transactional
Application Flow | Transaction Flow |
---|---|
![]() |
![]() |
If due to any reason any one of the above action fails then the other action should also be roll backed. So if Employee Information gets inserted but suppose due to some reason persist HealthInsurance is not successful, then Employee Information should also be rollbacked. It means it is all or none for a logical unit of work. Similar will be the case for exitOrganization Method which will be considered as one unit of work. Spring Boot Transaction Unit
Initially we will not be using any transaction management. By default the spring boot transaction is auto commit. But this is not a good practice we will see why in the next section.
@Transactional(propagation=Propagation.REQUIRES_NEW)
@Transactional(propagation=Propagation.SUPPORTS)
What is Transaction Propagation?
Any application involves a number of services or components making a call to other services or components. Transaction Propagation indicates if any component or service will or will not participate in transaction and how will it behave if the calling calling component/service already has or does not have a transaction created already.
Propagation | Behaviour |
---|---|
REQUIRED | Always executes in a transaction. If there is any existing transaction it uses it. If none exists then only a new one is created |
SUPPORTS | It may or may not run in a transaction. If current transaction exists then it is supported. If none exists then gets executed with out transaction. |
NOT_SUPPORTED | Always executes without a transaction. If there is any existing transaction it gets suspended |
REQUIRES_NEW | Always executes in a new transaction. If there is any existing transaction it gets suspended |
NEVER | Always executes with out any transaction. It throws an exception if there is an existing transaction |
MANDATORY | Always executes in a transaction. If there is any existing transaction it is used. If there is no existing transaction it will throw an exception. |
For an application transaction if any action fails then all other actions gets rolled back. Previous Transaction Management Example we had tested the rollback by throwing an unchecked exception. However in real time scenarios it is the checked exception that gets thrown. These are business exceptions based on some logic
So how will our transactions behave? in case of Checked Exceptions? In case of checked exceptions the previously executed transactions do not get rolled back automatically even if we have used transaction annotation. We need to inform the application how to handle roll back in event of checked exception. This is achieved using the RollbackFor annotation.
@Transactional(rollbackFor = InvalidInsuranceAmountException.class)
Transaction gets rollback automatically when the exception is of type unchecked
. For checked exceptions
/Business specific checked exception
we need to inform the transaction to get rollback.
Transaction with checked exceptions, not informed to get Rollback@Transactional
|
Transaction with checked exceptions, informed to get Rollback@Transactional(rollbackFor = InvalidInsuranceAmountException.class)
|
---|---|
// Using Transactional annotation we can define any isolation level supported by the underlying database.
@Transactional(isolation = Isolation.SERIALIZABLE)
What is Transaction Isolation?
Transaction Isolation defines the database state when two transactions concurrently act on the same database entity. It involves locking of database records. So it describes the behaviour or state of the database when one transaction is working on database entity and then some other concurrent transaction tries to simultaneously access/edit the same database entity. The ANSI/ISO standard defines four isolation levels. Isolation is one of the ACID (Atomicity, Consistency, Isolation, Durability) properties. So transaction isolation level is not something specific to Spring Framework. Using Spring we can change the isolation level to suit our business logic.
Before implementing Isolation Level using Spring, let us first understand isolation levels at Database level. We will be create a table name employee and using this table try understand the isolation levels-
CREATE TABLE employee (
empId VARCHAR(10) NOT NULL,
empName VARCHAR(100) NOT NULL
);
Some of the SQL commands I have used for implementing Isolation Levels are
//Show existing transaction isolation level if mysql version >= 8
SELECT @@TRANSACTION_ISOLATION;
//Show existing transaction isolation level if mysql version < 8
SELECT @@TX_ISOLATION;
//Set transaction isolation level to serializable. Using same syntax
//we can set it to other isolation level.
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
//By default auto commit is enabled for mysql transaction. So we will disable it.
SET AUTOCOMMIT=0;
//Start transaction
BEGIN
//Commit transaction
COMMIT
The following are the types of Transaction Isolation Levels-
SERIALIZABLE | REPEATABLE_READ | READ_COMMITTED | READ_UNCOMMITTED |
---|---|---|---|
If two transactions are executing concurrently then it is as if the transactions get executed serially i.e the first transaction gets committed only then the second transaction gets executed. This is total isolation. So a running transaction is never affected by other transactions. However this may cause issues as performance will be low and deadlock might occur. | If two transactions are executing concurrently - till the first transaction is committed the existing records cannot be changed by second transaction but new records can be added. After the second transaction is committed, the new added records get reflected in first transaction which is still not committed. For MySQL the default isolation level is REPEATABLE_READ. However the REPEATABLE READ isolation level behaves differently when using mysql. When using MYSQL we are not able to see the newly added records that are committed by the second transaction. |
If two transactions are executing concurrently - before the first transaction is committed the existing records can be changed as well as new records can be changed by second transaction. After the second transaction is committed, the newly added and also updated records get reflected in first transaction which is still not committed. | If two transactions are executing concurrently - before the first transaction is committed the existing records can be changed as well as new records can be changed by second transaction. Even if the second transaction is not committed the newly added and also updated records get reflected in first transaction which is still not committed. |
Summary
- Dirty Reads - Suppose two transactions - Transaction A and Transaction B are running concurrently. If Transaction A modifies a record but not commits it. Transaction B reads this record but then Transaction A again rollbacks the changes for the record and commits it. So Transaction B has a wrong value.
- Non-Repeatable Reads - Suppose two transactions - Transaction A and Transaction B are running concurrently. If Transaction A reads some records. Transaction B modifies these records before transaction A has been committed. So if Transaction A again reads these records they will be different. So same select statements result in different existing records.
- Phantom Reads - Suppose two transactions - Transaction A and Transaction B are running concurrently. If Transaction A reads some records. Transaction B adds more such records before transaction A has been committed. So if Transaction A again reads there will be more records than the previous select statement. So same select statements result in different number records to be displayed as new records also get added.
Isolation Level | Dirty Reads | Non-Repeatable Reads | Phantom Reads |
---|---|---|---|
SERIALIZABLE | This scenario is not possible as the second transaction cannot start execution until the first is committed. They never execute parallelly but only sequentially | This scenario is not possible as the second transaction cannot start execution until the first is committed. They never execute parallelly but only sequentially | This scenario is not possible as the second transaction cannot start execution until the first is committed. They never execute parallelly but only sequentially |
REPEATABLE_READ | This scenario is not possible as any existing record change gets reflected only if the transaction is committed. So other transaction will never read wrong value. | This scenario is not possible since any record can be changed only after a transaction has been committed. So multiple select statements before transaction commit will always return same existing records. | This scenario is possible as other transactions can insert new records even if first transaction commit has not taken place. |
READ_COMMITTED | This scenario is not possible as any existing record change gets reflected only if the transaction is committed. So other transaction will never read wrong value. | This scenario is possible as other transactions can modify existing records even if first transaction commit has not taken place. | This scenario is possible as other transactions can insert new records even if first transaction commit has not taken place. |
READ_UNCOMMITTED | This scenario is possible as any record can be read by other transactions even if the first transaction is not committed. So if first transaction rollbacks the record changes then other transactions will have wrong values | This scenario is possible since any record can be changed even if a transaction is not committed. | This scenario is possible as any record can be inserted even if a transaction is not committed. |