🌷 Spring Transaction

GunhoΒ·2025λ…„ 1μ›” 1일
0

Object Oriented Programming (OOP) & Java

λͺ©λ‘ 보기
28/29

πŸ”οΈ Overview

Spring has its own trasaction approach and this directly derives from the fact that each data access technology handles transactions differently. For instance, the way transactions are applied in JDBC and JPA differs in the code itself:

JDBC Transaction

public void accountTransfer(String fromId, String toId, int money) throws SQLException {
	try {
		Connection con = dataSource.getConnection();
		con.setAutoCommit(false); // Transaction Begins
		// Business Logic
		bizLogic(con, fromId, toId, money);
		con.commit(); // Commit for Success
	} catch (Exception e) {
		con.rollback(); // Rollback for Failure
		throw new IllegalStateException(e);
	} finally {
		release(con);
	}
}

JPA Transaction

public static void main(String[] args) {
	// Create EntityManagerFactory
	EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");
	EntityManager em = emf.createEntityManager(); // Create EntityManager 
	EntityTransaction tx = em.getTransaction(); // Acquire Transaction
	
    try {
		tx.begin(); // Transaction Begins
		logic(em); // Business Logic
		tx.commit();// Transaction Commit
	} catch (Exception e) {
		tx.rollback(); // Transaction Rollback
	} finally {
		em.close(); // Terminate EntityManager
	}
	emf.close(); // Terminate EntityManager Factory
}

μΈν”„λŸ° (κΉ€μ˜ν•œ) Available here


Therefore, if the application ever requires a switch from the JDBC-based transaction to the JPA-based transaction, all relevant codes to the transaction must be modified. To address this issue, Spring provides transaction abstraction where from the perspective of transaction management, Spring's transaction abstraction allows both JDBC and JPA and any further implementations of transactions to be applied by implementing the Spring defined transaction abstraction.

Spring achieves this abstraction through the PlatformTransactionManager interface:

PlatformTransactionManager

public interface PlatformTransactionManager extends TransactionManager {
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
    
	void commit(TransactionStatus status) throws TransactionException;
    
	void rollback(TransactionStatus status) throws TransactionException;
}

PlatformTransactionManager's public interface exposes simple transaction, commit, rollback methods and by its abstraction, developers uniformly implement the transaction execution logics while the responsibility of implementing the actual logic falls to the concrete instances inheriting PlatformTransactionManager interface.

μΈν”„λŸ° (κΉ€μ˜ν•œ) Available here


🌷 Spring Transaction

Spring Transaction's implementation can be largely divided into two approaches:

  • πŸ”Š Declarative Transaction Management

    • that is often declared with @Transactional
  • πŸ§‘πŸ½β€πŸ’» Programmatic Transaction Management

    • that is manually designed by a developer via TrasactionManager or Transaction Template

πŸ§‘πŸ½β€πŸ’» Programmatic Transaction Management

Programmatic Transaction Management as a converntional transaction management approach invovles the participation of three elements:

  • TransactionManager (PlatformTransactionManager)

  • TransactionDefinition

  • TransactionStatus

where specifically:

PlatformTransactionManager is responsible for starting, committing, or rolling back a transaction.

TransactionDefinition defines the properties of a transaction. TransactionDefinition can set transaction propagation behaviour, isolation levels, timeouts, and more.

TransactionStatus represents the current state of a transaction. It allows you to check whether the transaction is in progress, has been committed, or has been rolled back.

These overall can be graphically represented as below with its code-level example:

μΈν”„λŸ° (κΉ€μ˜ν•œ) Available here


Transaction Code

//Transaction begins
TransactionStatus status = transactionManager.getTransaction(new
DefaultTransactionDefinition());
try {
	// Business Logic
	bizLogic(fromId, toId, money);
	transactionManager.commit(status); //Commit for success
} catch (Exception e) {
	transactionManager.rollback(status); //Rollback for failure
	throw new IllegalStateException(e);
}

Programmatic Transaction Management tightly couples the application code to the transaction-related technical code in which from a maintenance point of view, having two separate concerns in a single code is not considered ideal, leading to Declarative Transaction Management to be preferred and prevail over real-world projects.


πŸ”Š Declarative Transaction Management

Declarative Transaction Management often comes with @Transactional where in-depth, it internally applies proxy-based AOP:

μΈν”„λŸ° (κΉ€μ˜ν•œ) Available here


@Transactional Code (Proxy)

public class MemberService {
	
    @Transactional
    public void logic() {
    	// Business Logic
    }
}

public class TransactionProxy {
	private MemberService target;
	
    public void logic() {

	TransactionStatus status = transactionManager.getTransaction(..);
	try {

		target.logic();
		transactionManager.commit(status); 
	} catch (Exception e) {
		transactionManager.rollback(status); 
		throw new IllegalStateException(e);
		}
	}
}

Transaction overall also invovles Transaction Synchronisation Manager which is responsible for handling Connection instances that is preserved within the ThreadLocal instance:

μΈν”„λŸ° (κΉ€μ˜ν•œ) Available here


🧻 Issues

One common issue that often arises is the internal calls within a class from a non-@Transactional method to a method with @Transactional, anticipating that transaction should apply.

This is deeply relevant to AOP proxy that @Transactional essentially underlie and Java:

μΈν”„λŸ° (κΉ€μ˜ν•œ) Available here


Specifically, in Java, when a method is called without any explicit reference, it defaults to using 'this', which represents the current instance. For example, invoking this.METHOD_WITH_TRANSACTIION directly points to the actual instance of the target object. Since this kind of internal method call doesn't pass through the proxy, it bypasses any transaction management logic. As a result, the METHOD_WITH_TRANSACTIION on the target object is called directly, and the transaction is not applied.

πŸ’‘ One simple solution to this is to have separate classes for non-@Transactional and @Transactional methods.

🫚 Propagation

Spring transaction management allows controlling the scope of a transaction through propagation properties. Propagation properties define how a transaction interacts with other transactions. Propagation properties enable flexible configuration of a transaction's scope.

Spring supports various propagation properties, such as:

  • REQUIRED
  • REQUIRES_NEW
  • NESTED

REQUIRED propagation property uses the current transaction if one exists, and starts a new transaction if none exists.

REQUIRES_NEW propagation property always starts a new transaction. If a current transaction exists, it is suspended, and a new transaction begins.

NESTED propagation property starts a nested transaction if a current transaction exists, or starts a new transaction if none exists. NESTED transactions are influenced by the outer transaction, but NESTED transactions do not affect the outer transaction. Even if the nested transaction rolls back, the outer transaction can still commit. However, if the outer transaction rolls back, the nested transaction is also rolled back.


πŸ“š References

μΈν”„λŸ° (κΉ€μ˜ν•œ)
F-Lab

profile
Hello

0개의 λŒ“κΈ€