Transaction handling in Spring

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 Transaction handling in Spring Reviewed by Ravi Yasas on 1:58 AM Rating: 5

No comments:

Powered by Blogger.