코드의 중복은 언제나 문제를 발생시킨다.
트랜잭션이 있을 때마다 커밋
과 롤백
이 곁다리로 추가가 되며 짜증나는 try-catch
문이 가독성 마져 왕창 떨어뜨린다.
역시 이런경우를 대비해 만능의 스프링은 또 좋은걸 제공해준다.
바로 TransactionTemplate
TransactionTemplate
클래스는 TransactionOperations
라는 인터페이스를 상속받고 구현하고있다.
package org.springframework.transaction.support;
import java.util.function.Consumer;
import org.springframework.lang.Nullable;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
public interface TransactionOperations {
@Nullable
<T> T execute(TransactionCallback<T> action) throws TransactionException;
default void executeWithoutResult(Consumer<TransactionStatus> action) throws TransactionException {
execute(status -> {
action.accept(status);
return null;
});
}
static TransactionOperations withoutTransaction() {
return WithoutTransactionOperations.INSTANCE;
}
}
TransactionTemplate
의 경우 코드가 너무 길어서 그냥 대충 구조를 파악 할 수 있게 TransactionTemplate
의 부모 인터페이스 코드를 가져왔다.
자세한건 좀 더 심화과정에서 알아보고 우선 사용법만 간단히 익혀보자.(사실 이걸 사용안한다,,ㅋㅋ,,)
package hello.jdbc.service;
import hello.jdbc.domain.Member;
import hello.jdbc.repository.MemberRepositoryV3;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import java.sql.SQLException;
import java.sql.SQLException;
/**
* 트랜잭션 - 트랜잭션 템플릿
*/
@Slf4j
public class MemberServiceV3_2 {
private final TransactionTemplate txTemplate;
private final MemberRepositoryV3 memberRepository;
public MemberServiceV3_2(PlatformTransactionManager transactionManager, MemberRepositoryV3 memberRepository) {
this.txTemplate = new TransactionTemplate(transactionManager);
this.memberRepository = memberRepository;
}
public void accountTransfer(String fromId, String toId, int money) throws SQLException {
txTemplate.executeWithoutResult((status) -> {
//비즈니스 로직
try {
bizLogic(fromId, toId, money);
} catch (SQLException e) {
throw new IllegalStateException(e);
}
});
}
private void bizLogic(String fromId, String toId, int money) throws SQLException {
Member fromMember = memberRepository.findById(fromId);
Member toMember = memberRepository.findById(toId);
memberRepository.update(fromId, fromMember.getMoney() - money);
validation(toMember);
memberRepository.update(toId, toMember.getMoney() + money);
}
...
}
트랜잭션탬플릿
을 이용함으로써 트랜잭션 마다 반복해야했던 커밋
과 롤백
코드들이 사라졌다.
커밋과 롤백을 줄여서 매우 기분이 좋아졌지만 아직 짜증나는 try-catch
문이 계속 남아있다. 이는 사실 SQLException
을 처리하지 못했기 때문인데 아쉽게도 아직 이번장은 예외처리를 다루지 않기 때문에 눈물을 머금고 넘어가도록 하자.
많이 줄였음에도 불구하고 아직 트랜잭션 기술 로직에 대한 코드가 서비스단에 남아있다.
txTemplate.executeWithoutResult((status) -> {
하지만 스프링은 당연히 이 모든문제들에 대해 또 대비가 되어있었다,,, 놀라운 기술은 다음 장에서,,,
본 포스트는
김영한의 스프링 DB 1편 - 데이터 접근 핵심 원리 를 보고 정리했습니다.