JPA는 트랜잭션 안에서 작동을 한다. 그리고 그 트랜잭션을 생성하도록 도와주는 것이 바로 @Transactional
이다. 그동안 @Transactional
을 무의식적으로 사용만 했었지 제대로 공부를 해본적은 없는 것 같아 여기에 공부한 내용을 적어두고자 한다.
다음과 같은 TransactionService
가 있다고 해보자.
@Service
@RequiredArgsConstructor
public class TransactionService {
private final PostRepository postRepository;
private final EntityManager em;
public void runPersist(Post post) {
transactionalPersistSave(post);
}
public void runRepository(Post post) {
transactionalRepositorySave(post);
}
@Transactional
public void transactionalPersistSave(Post post) {
em.persist(post);
System.out.println("영속화 여부: " + em.contains(post));
}
@Transactional
public void transactionalRepositorySave(Post post) {
postRepository.save(post);
System.out.println("영속화 여부: " + em.contains(post));
}
}
그리고 다음과 같이 테스트코드를 작성해보자.
@SpringBootTest
public class TransactionalTest {
@Autowired TransactionService transactionService;
@Autowired ApplicationContext applicationContext;
@Test
@DisplayName("과연 transactionService 객체는 프록시인가")
public void proxyTest() throws Exception {
String className
= applicationContext
.getBean(transactionService.getClass())
.getClass().getName();
System.out.println(className);
}
}
위의 테스트코드를 실행하면 다음과 같이 출력된다.
com.jpa.basic.test.transactional.TransactionService$$EnhancerBySpringCGLIB$$b6802bbc
CGLIB 프록시 객체를 생성하는 라이브러리이므로 transactionService
에 프록시 객체가 주입되었음을 알 수 있다.
이제 @Transactional
을 달면 어떻게 프록시 객체가 생성되는지 그 과정에 대해서 알아보자.
어플리케이션이 시작되면 @Transactional
어노테이션을 스캔한다.
만일 하나의 메소드라도 @Transactional이 붙어있다면 스프링 컨테이너는 시작할 때 해당 클래스의 프록시 객체를 생성하여 빈으로 등록한다.
의존성 주입을할 때 실제객체가 아닌 프록시 객체를 주입한다.
여기서 중요한 것은 하나라도 @Transactional이 있으면 스프링 컨테이너가 프록시 객체를 생성하여 빈으로 등록한다는 점이다. 이 부분에 대해서 많은 구글링을 해도 자료가 나오지 않아 직접 실험해서 도출해낸 결론이다.
만약에 위의 TransactionService
에서 @Transactional
을 모두 없애고 위의 테스트 코드를 실행하면 다음과 같은 결과가 나온다.
com.jpa.basic.test.transactional.TransactionService
프록시 객체가 아닌 실제 객체가 주입됨을 알 수 있다.