작성
·
757
8
안녕하세요. 선생님! 오랜만에 또 질문남깁니다..!
강의내용에서 JpaRepository의 구현체에 @Transactional 애노테이션이 붙어있기 때문에 서비스계층에 없어도 트랜잭션이 적용된다고 하셨습니다. JPA의 모든 변경은 트랜잭션 안에서 동작한다고도 하셨구요!
여기서 질문입니다.
Repository를 직접 정의하고 사용할 때, @Transactional 애노테이션 없어도 동작하는 이유는 뭔가요?
더 쉬운 예제로 Controller에서 em.find() 호출이 가능한데, 이유가 뭔지 궁금합니다.. EntityManger의 구현체에 @Transactional이 붙어있나 싶어 em.getClass()를 찍어보았는데, class com.sun.proxy.$Proxy113 이렇게 찍혀서 구현체를 어떻게 봐야하는지를 모르겠어요..
@RestController
@RequiredArgsConstructor
public class MemberApiController{
private final EntityManager em;
@GetMapping("/api/v1/member")
public Member getMemberV1(){
return em.find(Member.class, 1L); //트랜잭션 설정을 안했는데 왜 가능할까요?
}
}
답변 2
9
안녕하세요. pjok1122님^^ 오랜만입니다.
JPA에서 데이터 변경이 필요할 때는 @Transactional이 필요합니다. 그런데 트랜잭션이 없어도, 단순히 읽기만 하는 것은 가능합니다. 이것을 트랜잭션 밖에서 읽기라 합니다. 물론 데이터 변경은 안됩니다.
(관련해서 자세한 내용은 JPA책 682p 15.4.2 읽기 전용 쿼리와 성능 최적화를 참고하시면 더 깊은 내용을 이해할 수 있습니다.)
그리고 EntityManager의 구현체가 $Proxy로 찍힌 이유는, JPA는 트랜잭션 단위로 영속성 컨텍스트를 제공합니다. 그런데 스프링에서 해당 컨트롤러를 비롯한 일반적은 객체들은 싱글톤 빈으로 생성됩니다. 그러면 엔티티 메니저를 모든 쓰레드가 공유하게 되지요. 쉽게 이야기해서 동시에 여러 사람이 같은 영속성 컨텍스트에 접근하는 끔직한? 일이 발생합니다.
그래서 스프링은 가짜(프록시) 엔티티 메니저를 제공합니다. 이 가짜 엔티티 메니저를 호출하면, 현재 트랜잭션(또는 쓰레드)에 연결된 엔티티 메니저를 찾아서 내부에서 연결해줍니다^^
이런 메커니즘을 제공해주는 덕분에 우리는 싱글톤 스프링 빈에서 멀티쓰레드에 대한 고민 없이 쉽게 개발할 수 있는 것이지요.
감사합니다^^
2