728x90
반응형
트랜잭션 적용 방식
- 트랜잭션의 적용 방식을 알아보자.
- 어떻게 트랜잭션 코드가 개선되는지 확인해본다.
- AOP를 이용하여 트랜잭션 코드와 비즈니스 로직을 분류 해본다.
트랜잭션 의 적용 방식 종류
선언적 트랜잭션 관리
- @Transaction
- AOP 사용
- Proxy 구조
프로그래밍 방식의 트랜잭션 관리
- JDBC 트랜잭션
- 코드 작성 필요 - 트랜잭션 템플릿
- 코드 작성 필요
트랜잭션 사용 준비
- 앞서 커넥션 풀에 대해 공부 했다.
- 트랜잭션을 사용하기 위해선 동일한 커넥션을 사용해야 된다.
JDBC 트랜잭션 적용
- 동일한 커넥션을 사용하기 위해 커넥션을 파라미터로 넘겨주자.
- accountTransfer 메소드 시작 할때 dataSource 에서 커넥션을 가져온다.
- 커넥션의 오토 커밋을 false 로 하여 트랜잭션을 직접 다루도록 한다.
- bigLogic 을 보면 파라미터로 커넥션을 받는다 이는 동일한 커넥션을 사용하기 위함이다.
public void accountTransfer(String fromId, String toId, int money) throws SQLException {
Connection con = dataSource.getConnection(); // 커넥션 획득
try {
con.setAutoCommit(false); // 트랜잭션 시작
// 비즈니스 로직 수행
bizLogic(fromId, toId, money, con);
con.commit(); // 성공시 커밋
} catch (Exception e) {
con.rollback();
throw new IllegalStateException(e);
} finally {
release(con);
}
}
private void bizLogic(String fromId, String toId, int money, Connection con) throws SQLException {
Member fromMember = memberRepositoryV2.findById(con, fromId);
Member toMember = memberRepositoryV2.findById(con, toId);
memberRepositoryV2.update(con, fromId, fromMember.getMoney() - money);
validation(toMember);
memberRepositoryV2.update(con, toId, toMember.getMoney() + money);
}
private void release(Connection con) {
if (con != null) {
try {
con.close();
} catch (Exception e) {
log.info("error", e);
}
}
}
private void validation(Member toMember) {
if (toMember.getMemberId().equals("ex")) {
throw new IllegalStateException("이체중 예외 발생");
}
}
문제점
- JDBC 코드 와 비즈니스 로직이 섞여있어 코드의 복잡성이 올라갔다.
- 트랜잭션에 종속 되어 다른 트랜잭션을 사용하는 경우에도 불필요하게 적용이 된다
트랜잭션 매니저 사용
- 트랜잭션 매니저는 트랜잭션이 시작된 커넥션을 트랜잭션 동기화 매니저에 보관한다.
- 그렇기 때문에 트랜잭션 매니저에서 트랜잭션을 꺼내오면 쉽게 사용할 수 있다.
private final PlatformTransactionManager transactionManager;
private final MemberRepositoryV3 memberRepository;
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 을 해줘야 하고 익셉션 시 롤백을 해줘야 되는 코드가 존재 한다.
트랜잭션 템플릿 사용
- 트랜잭션 템플릿을 사용하게 되면 다음과 같은 이점이 있다.
- 비즈니스 로직에서 체크드 익셉션이 발생 하게 되면 커밋이 된다.
- 비즈니스 로직에서 언체크드 익셉션이 발생하게 되면 롤백이 된다.
- 참고로 트랜잭션 템플릿을 사용하려면 트랜잭션 매니저를 생성자에 넣어줘야 한다.
private final TransactionTemplate txTemplate;
private final MemberRepositoryV3 memberRepositoryV3;
public MemberServiceV3_2(PlatformTransactionManager transactionManager, MemberRepositoryV3 memberRepositoryV3) {
this.txTemplate = new TransactionTemplate(transactionManager);
this.memberRepositoryV3 = memberRepositoryV3;
}
public void accountTransfer(String fromId, String toId, int money) throws SQLException {
// 아래 복잡한 로직을 txTemplate 을 사용해서 해결한다.
// 안에서 안체크 예외가 터지면 롤백을 한다.
// 체크 예외인 경우 커밋함.
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 = memberRepositoryV3.findById(fromId);
Member toMember = memberRepositoryV3.findById(toId);
memberRepositoryV3.update(fromId, fromMember.getMoney() - money);
validation(toMember);
memberRepositoryV3.update(toId, toMember.getMoney() + money);
}
문제점
- 여전히 트랜잭션 관련 코드가 존재한다.
스프링의 트랜잭션 AOP 등장
- 비즈니스 로직에는 순수하게 비즈니스 로직만 두고 싶다
- 그럼 어떻게 해야 될까?
- 여기서 프록시라는 개념이 등장하게 된다.
프록시 적용 전 후 비교
프록시 적용전
프록시 적용후
- 프록시를 도입 하게 되면 프록시에 트랜잭션 처리 로직만 두게 된다.
- 프록시 로직 안에 내부 적으로 비즈니스 로직을 참조하게 된다.
- 프록시를 적용 하려면 간단하게 스프링 에서 제공해 주는
- @Transactional 어노테이션을 붙여 주면된다.
private final MemberRepositoryV3 memberRepositoryV3;
@Transactional
public void accountTransfer(String fromId, String toId, int money) throws SQLException {
bizLogic(fromId, toId, money);
}
private void bizLogic(String fromId, String toId, int money) throws SQLException {
Member fromMember = memberRepositoryV3.findById(fromId);
Member toMember = memberRepositoryV3.findById(toId);
memberRepositoryV3.update(fromId, fromMember.getMoney() - money);
validation(toMember);
memberRepositoryV3.update(toId, toMember.getMoney() + money);
}
해결된 부분들
- 트랜잭션 로직과 비즈니스 로직이 분류가 되었다.
참고:
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/unit/110095?tab=curriculum
728x90
반응형
'Spring > Spring DB 스터디' 카테고리의 다른 글
[Spring DB] 트랜잭션 (0) | 2022.07.31 |
---|---|
[Spring DB] 커넥션 풀과 데이터 소스 (0) | 2022.07.27 |
댓글