트랜잭션을 처리하기 위해서는 서비스 계층의 비즈니즈 로직에서 일일이 코드를 작성해야했다
public void accountTransfer(String fromId, String toId, int money) throws SQLException {
Connection connection = dataSource.getConnection();
try{
//트랜잭션 시작
connection.setAutoCommit(false);
//비즈니스 로직 수행
bizLogic(connection, fromId, toId, money);
//트랜잭션 종료
connection.commit();
} catch (Exception e){
connection.rollback();
throw new IllegalStateException(e);
}finally {
release(connection);
}
}
하지만 위와 같이 트랜잭션을 처리할 경우, 트랜잭션 처리 코드가 비즈니스 로직에 누수가 되는 문제가 있다
이 문제로 인해, 서비스 계층에는 비즈니스 로직에 온전히 집중할 수가 없으며, 단일책임의 원칙을 지키기 어려워진다
또한, 위 코드의 트랜잭션 처리방식은 JDBC에 의존하고 있어서 JDBC가 아닌 다른 기술로 전환할 경우, 코드의 수정이 어렵다
예) connection.setAutoCommit(false)와 connection.commit(), connection.rollback()은 JDBC에서 제공하는 메서드이다
첫번쨰로, JDBC에 의존하고 있는 트랜잭션 처리방식에 대한 문제를 해결해보자
스프링에서는 PlatformTransactionManager라는 인터페이스를 제공한다
PlatformTransactionManager는 다양한 기술에 대해 트랜잭션 처리에 관한 인터페이스를 제공한다
JDBC를 이용할 때 사용되는 DataSourceTransactionManager, JPA를 이용할 때 사용되는 JpaTransactionManager등
트랜잭션을 처리하는 주요 클래스들이 PlatformTransactionManager를 구현하고 있으므르 기술에 종속되지 않는 트랜잭션 처리가 가능하다

아래는 PlatformTransactionManager를 적용하여 트랜잭션을 처리하는 코드이다
JDBC에 의존하고 있던 코드를 제거하여 트랜잭션 처리에 관한 기술종속문제를 해결하였다
private final PlatformTransactionManager transactionManager;
public void accountTransfer(String fromId, String toId, int money) throws SQLException {
//트랜잭션 시작
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try{
//비즈니스 로직 수행
bizLogic(fromId, toId, money);
//트랜잭션 종료
transactionManager.commit(status);
} catch (Exception e){
transactionManager.rollback(status);
throw new IllegalStateException(e);
}
}
두번째로는 지금 트랜잭션 시작 및 commit 또는 rollback하는 코드가 모든 비즈니스 로직을 작성할 때마다 들어가야 한다는 문제를 해결해보겠다.
이러한 코드의 반복문제를 해결하기 위해서 스프링에서는 TransactionTemplate이라는 클래스를 제공한다
TransactionTemplate 클래스를 이용하면 트랜잭션 시작 및 commit 또는 rollback을 자동화 할 수 있다
private final TransactionTemplate transactionTemplate;
public MemberServiceV3_2(PlatformTransactionManager transactionManager, MemberRepositoryV3 memberRepository) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public void accountTransfer(String fromId, String toId, int money) throws SQLException {
//비즈니스 로직에 반환값이 있을 때는 execute 메서드 사용
//비즈니스 로직에 반환값이 없을 때는 executeWithoutResult 메서드 사용
transactionTemplate.executeWithoutResult(status -> {
try {
bizLogic(fromId, toId, money);
} catch (SQLException e) {
throw new IllegalStateException(e);
}
});
}
세번째로는, 아직 비즈니스 로직에 트랜잭션 처리로직이 남아있는 문제를 해결해보겠다
스프링에서는 @Transactional이라는 어노테이션으로 간단하게 트랜잭션을 처리할 수 있도록 해준다
비즈니스 로직을 구현하는 메서드 위에 @Transactional 어노테이션을 선언하면 AOP를 이용하여 자동으로 트랜잭션이 처리된다
(단, Unchecked Exception에 대해서만 Rollback 처리가 된다는 점을 주의하자)
@Transactional
public void accountTransfer(String fromId, String toId, int money) throws SQLException {
bizLogic(fromId, toId, money);
}
기존에 코드로 트랜잭션 처리를 구현했던 것에 비해 선언적으로 트랜잭션을 구현할 수 있게 되어 코드가 간결해지고 단일책임의 원칙도 지킬 수 있게 되었다
이로써, 서비스 계층에서 온전히 비즈니스 로직에만 집중할 수 있게 되었다
'Spring > Spring DB' 카테고리의 다른 글
| 자바 예외(Exception) (3) | 2025.07.21 |
|---|---|
| 문제해결 - 트랜잭션 동기화 문제 (1) | 2025.07.19 |
| DB Lock (0) | 2025.07.18 |
| DB Connection (0) | 2025.07.18 |
| 트랜잭션 (0) | 2025.07.18 |
댓글