(1) 트랜잭션 범위와 영속성 컨텍스트
Last update: a year ago by nowwaterReading time: 2 min
스프링 컨테이너의 기본 전략
스프링 컨테이너는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용.
- 트랜잭션의 범위와 영속성 컨텍스트의 생존 범위가 같다.
- 트랜잭션을 시작할 때 영속성 컨텍스트를 생성하고 트랜잭션이 끝날 때 영속성 컨텍스트를 종료한다.
- 같은 트랜잭션 안에서는 여러 위치(여러 레포지토리)의 엔티티 매니저를 사용해도 항상 같은 영속성 컨텍스트에 접근한다.
다양한 위치에서 엔티티 매니저를 주입받아 사용해도 트랜잭션이 같으면 항상 같은 영속성 컨텍스트를 사용한다.
예를 들면, Transaction 범위의 한 클래스에서 repo1 의 어떤 em.~~~() 수행하는 것과, repo2 의 em.~~!() 를 수행하는 것은둘 다 똑같은 영속성 컨텍스트를 사용하는 것.반면, 트랜잭션이 다르면 동일한 엔티티 매니저를 사용해도 다른 영속성 컨텍스트를 사용
Transaction 범위의 A 클래스, Transaction 범위의 B 클래스가 한 repo의 em.~~() 를 동시에 사용해도 트랜잭션에 따라 접근하는 컨텍스트가 다르다.
즉, 스프링 컨테이너는 스레드마다 각각 다른 트랜잭션을 할당한다. -> 멀티 스레드 상황에 안전함
비즈니스 로직을 처리하는 서비스 계층에 @Transaction
어노테이션을 선언해서 트랜잭션을 시작한다.
어노테이션이 붙은 메소드를 실행하기 직전에 스프링의 트랜잭션 AOP
가 먼저 동작한다.
트랜잭션 AOP
대상 메소드를 호출하기 전에 트랜잭션을 시작, 대상 메소드가 정상 종료되면 트랜잭션을 커밋하면서 종료
트랜잭션 커밋 시 영속성 컨텍스트 플러시 => 변경 내용 데이터베이스에 반영
예외 발생 시 트랜잭션을 롤백 -> 이때는 플러시 호출 X
@Controllerclass HelloController {@Autowired HelloService helloService;public void hello(){Member member = helloService.logic(); // 반환된 Member 엔티티는 준영속 상태}}@Serviceclass HelloService {@PersistenceContextEntityManager em;@Autowired Repository1 repository1;@Autowired Repository2 repository2;// 메소드를 호출할 때 트랜잭션을 먼저 시작@Transactionalpublic void logic() {repository1.hello();// member는 영속상태 : 현재 트랜잭션 범위 안에 있으므로 영속성 컨텍스트의 관리를 받는다.Member member = repository2.findMember();return member;}// 트랜잭션 종료 : 트랜잭션 커밋, 영속성 컨텍스트 종료, 조회한 member는 이제부터 준영속 상태}@Repositoryclass Repository1 {@PersistenceContextEntityManager em;public void hello(){em.xxx(); // A 영속성 컨텍스트 접근}}@Repositoryclass Repository2 {@PersistenceContextEntityManager em;public void findMember(){return em.find(Member.class, "id1"); // B 영속성 컨텍스트 접근}}
EntityManager는 다르지만 A와 B 모두 동일한 영속성 컨텍스트이다. => 동일한 트랜잭션 범위
이를 통해 컨테이너가 트랜잭션과 복잡한 멀티스레드 상황을 처리해줌으로써, 개발자는 싱글 스레드 애플리케이션처럼 단순하게 개발할 수 있고 비즈니스 로직 개발에 집중할 수 있다.