티스토리 뷰

스프링 트랜잭션 추상화

 

서로 다른 데이터 접근 기술들은 트랜잭션을 처리하는 방식의 차이가 있다.

JDBC 트랜잭션

public void accountTransfer(String fromId, String toId, int money) throws SQLException {
 Connection con = dataSource.getConnection();
 try {
 	con.setAutoCommit(false); //트랜잭션 시작
 	//비즈니스 로직
 	bizLogic(con, fromId, toId, money);
 	con.commit(); //성공시 커밋
 } catch (Exception e) {
 	con.rollback(); //실패시 롤백
 	throw new IllegalStateException(e);
 } finally {
 	release(con);
 }
}

 

JPA 트랜잭션

public static void main(String[] args) {
	//엔티티 매니저 팩토리 생성
 	EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");
	EntityManager em = emf.createEntityManager(); //엔티티 매니저 생성
 	EntityTransaction tx = em.getTransaction(); //트랜잭션 기능 획득
 	try {
 		tx.begin(); //트랜잭션 시작
 		logic(em); //비즈니스 로직
 		tx.commit();//트랜잭션 커밋
 	} catch (Exception e) {
 		tx.rollback(); //트랜잭션 롤백
 	} finally {
 		em.close(); //엔티티 매니저 종료
 	}
 	emf.close(); //엔티티 매니저 팩토리 종료
}

 

데이터 접근 기술의 교체는 코드를 모두 변경해야 하는 번거로움을 야기하는데,

스프링은 PlatformTransactionManager라는 인터페이스를 통해 트랜잭션을 추상화한다.

 

스프링 트랜잭션 사용 방식

선언적 트랜잭션 관리 - @Transactional을 사용하여 트랜잭션을 적용

프로그래밍 방식의 트랜잭션 관리 - 트랜잭션 매니저나 템플릿을사용하여 트랜잭션 관련 코드를 작성하여 적용

 

@Transactional을 사용하면 프록시가 도입된다. -> 순수한 비즈니스 로직만 

전체 과정

@Transactional의 실제 방식

@Transactional이 클래스나 메서드에 하나라도 존재한다면, 트랜잭션 AOP는 프록시를 만들어 컨테이너에 등록

실제 클래스 대신 프록시를 스프링 빈에 등록하게 된다. 

실제 클래스 대신 프록시를 사용하여 다형성을 활용, 클라이언트의 요청에는 프록시를 통해 주입할 수 있다.

 

트랜잭션의 우선 순위

  • 더 구체적이고 자세한 것이 높은 우선순위를 가진다. (메서드 > 클래스, 구현 클래스 > 인터페이스)
  • 클래스에 적용하면 메서드는 자동 적용

 

트랜잭션 AOP 주의사항 - 프록시 내부 호출

위 코드를 보면

external() 메서드는 트랜잭션이 적용되지 않았고, internal() 메서드를 호출한다.

internal() 메서드는 트랜잭션이 적용되어 있다.

 

기대하는 흐름은 external() 호출 -> 트랜잭션이 적용된 internal() 호출이지만 그렇지 않다.

internal() 호출
external() 호출

트랜잭션 프록시가 external() 메서드를 호출할 때, 트랜잭션이 적용되어 있지 않기 때문에

internal() 메서드를 호출하더라도 트랜잭션이 적용되지 않는다.

이는 트랜잭션이 걸린 internal()메서드를 호출하는 것이 아닌, 자기 참조를 통한 메서드 호출이기 때문이다.

 

즉, 프록시를 사용하는 트랜잭션 AOP는 메서드 내부 호출에 프록시를 적용할 수 없는 한계가 존재하는 것이다.

-> 메서드를 별도의 클래스로 분리하기

별도로 분리된 클래스와 AOP Proxy

 

트랜잭션 AOP 주의사항 - 초기화 시점

초기화 코드(@PostContruct와 같은)와 @Transactional 코드를 함께 사용하면 안된다.

초기화 이후 트랜잭션 AOP가 호출되기 때문이다.

위 코드를 아래 코드로 변경한다면 해결할 수 있다.

-> 애플리케이션이 준비가 되었을 때 실행하는 이벤트리스너

@PostConstruct -> 초기화완료 -> 이벤트리스너(트랜잭션)

 

트랜잭션 옵션

  • value - 트랜잭션 매니저의 이름을 구분, 둘 이상의 트랜잭션 매니저 사용 시 구분한다.
  • rollbackFor - 롤백이 발생할 예외를 추가로 지정한다.
  • noRollbackFor - 롤백이 발생하지 않을 예외를 지정한다.
  • isolation - 트랜잭션 격리 수준을 지정한다.
  • timeout - 트랜잭션 타임아웃 시간을 지정한다.
  • readOnly - 읽기 전용인 트랜잭션을 생성한다. (성능 최적화를 위해)
  • propagation - 트랜잭션 전파를 위한 옵션이다.

 

 

 

 

본 포스팅은 인프런 강의 김영한님의

[ 스프링 DB 2편 - 데이터 접근 활용 기술 ] 을 수강하며 작성한 내용입니다.

공지사항
최근에 올라온 글
최근에 달린 댓글
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30