When we are dealing with data and databases, transaction management is the most important thing.
How JDBC handles transactions
import java.sql.Connection;
Connection connection = dataSource.getConnection();
try (connection) {
connection.setAutoCommit(false);
// execute some SQL statements...
connection.commit();
} catch (SQLException e) {
connection.rollback();
}
Set isolation levels in JDBC
connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
Plain JDBC transaction handling vs Spring transaction handling
- Don't need to open or close database connections, you can use Transaction callbacks or a JDBC template
- You don't need to catch SQLExceptions, Spring will convert them into run-time exceptions.
- Easy to use Spring ecosystem
Transaction management with Spring
- In Spring Boot it automatically enabled transaction management.
- But if you are using Spring MVC, you need to add the @EnableTransactionManagement annotation in the main class
- @Transactional annotation tells Spring that a transaction is required to execute a method or class.
- Actually, Spring @Transactional annotation converts the method into a simple JDBC transaction model
@Transactional
public List<Employee> getEmployees() {
Connection connection = dataSource.getConnection();
try (connection) {
connection.setAutoCommit(false);
return employeeRepository.findAll();
connection.commit();
} catch (SQLException e) {
connection.rollback();
}
}
How the transaction works in Spring
Look at the following control and service
@RestController
@RequestMapping("/api/employee")
public class EmployeeController {
@Autowired
public EmployeeController(EmployeeService employeeService){
this.employeeService=employeeService;
}
private EmployeeService employeeService;
@GetMapping("/getEmployees")
public List<Employee> getAllEmployees(){
return employeeService.getEmployees();
}
}
@Service
@Transactional
public class EmployeeService {
@Autowired
public EmployeeService(EmployeeRepository employeeRepository){
this.employeeRepository=employeeRepository;
}
private EmployeeRepository employeeRepository;
public List<Employee> getEmployees() {
return employeeRepository.findAll();
}
}
- When EmployeeController injects the EmployeeService, Spring generates a dynamic CGLib proxy object that wraps the service object and provides the required codes to manage the transaction.
- The proxy delegates handling transactions to the transaction manager.
- Once the transaction is completed, it will commit or roll back the transaction.
Physical transaction vs logical transaction
Look at the following example
@Service
public class EmployeeService {
@Autowired
private AccountService accountService;
@Transactional
public void getAccountDetails() {
accountService.createAccountDocument();
}
}
@Service
public class AccountService {
@Transactional
public void createAccountDocument() {
}
}
- Here EmployeeService getAccountDetails() method calls another @Transactional method in AccountService
- According to the ACID properties, it should be one transaction.
- Spring calls this a physical transaction. It is just actual JDBC connections.
- But internally there are two logical transactions. The first one in EmployeeService and another one in AccountService
- If we change the propagation type in createAccountDocument( ) we can change it to have two physical connections.
Propagation levels in Spring
- You can set propagation levels like @Transactioanl(propagation=Propagation.REQUIRED) There are few propagation levels in the Spring.
- It explains the way of creating a transaction
Types of propagation levels
- REQUIRED
- This is the default propagation level
- It says I need a transaction either open one or use the existing one
- It means it always executes in a transaction
- SUPPORTS
- If a current transaction exists, then it uses. If not it gets executed without a transaction.
- It means it may use or not a transaction.
- MANDATORY
- This requires an existing physical transaction, if not it will cause an exception
- REQUIRES_NEW
- Requesting a completely own physical transaction
- If already there is a transaction, it will be suspended and create a new transaction
- NOT_SUPPORTED
- Always executes without a transaction.
- If there is a transaction, it gets suspended
- NEVER
- Always executes without a transaction
- This means, no physical transaction will exist. If found, it will cause an exception
- NESTED
- This is the same as REQUIRED but it uses save-points.
- Simply it means inner logical transactions may roll back independently.
Read operations in a transaction
- Dirty read
- This occurs when a transaction reads data that is written but not committed by another transaction.
- T2 updates a row, but not committed and T1 reads the row. But if T2 rollback, the value which is read by T1 is not valid.
- Nonrepeatable read
- This occurs when a transaction reads the same data twice but gets different values.
- T1 reads a row, then T2 updates the row and committed. Then T1 reads the row and gets a different value.
- Phantom read
- This occurs when a transaction executes a query and gets a value set. Then get additional values as well in different execution.
- T1 reads some data. T2 adds some additional data in another transaction. Then T1 reads data and gets an additional data set.
Isolation levels in Spring
- Isolation.DEFAULT
- Use the default isolation level in the underlying database
- Isolation.READ_COMMITTED
- This prohibits reading uncommitted data
- If you use this, dirty reads will be prevented
- Isolation.READ_UNCOMMITTED
- This allows to read one transaction update a row, but not committed and other transaction can read data even not committed.
- All read operations, dirty, non-repeatable, and phantom can occur.
- Isolation.REPEATABLE_READ
- This prohibits reading uncommitted data and also prohibits one transaction reads a row and another transaction alters the data.
- This prevents both dirty read and nonrepeatable reads
- Isolation.SERIALIZABLE
- all dirty reads, nonrepeatable reads, and phantom reads are prevented
rollbackFor and noRollbackFor
- rollbackFor can be used to indicate which exceptions cause the transaction rollback
- noRollbackFor can be used to indicate which exception, not cause the transaction rollback
readOnly
- The default value is false
- We can use, readOnly=true for search operations
Transaction handling in Spring
Reviewed by Ravi Yasas
on
1:58 AM
Rating:

No comments: