JPA는 @Transactional 로 묶인 곳에서 Entity 를 관리해요. @Transactional 은 스프링에서 트랜잭션을 관리하는 방법 중 하나인데, 프록시를 이용해서 타겟 오브젝트를 감싸고 트랜잭션 시작/끝 부가기능을 앞뒤에서 제공해요. 때문에 외부에서 접근해야 프록시가 적용되기 때문에 @Transactional 이 적용된 자기 자신(this)의 다른 메소드를 호출하려면 그냥 하면 안되고 부가적인 작업이 필요해요.
JPA는 데이터베이스로의 무분별한 접근을 막기위해 영속성 컨텍스트에서 엔티티를 관리해요. 엔티티를 관리할 때 Lazy 란 개념이 있어요. 어떤 엔티티의 모든 필드가 필요한것이 아닐 경우가 있어요.
A가 B를 필드로 가지고 있는데, B엔티티의 모든 필드가 당장 필요한게 아니라 A엔티티를 반환하기 위해 참조만 필요한 경우에! JPA는 Lazy Evaluation 을 적용해요.
즉! B엔티티를 DB에 접근해서 모든 필드를 가져오는 것이 아니라! 껍데기인 프록시 객체를 생성한 뒤 참조만 A엔티티에 넘겨줘요. 그리고 나중에 B엔티티를 사용할 필요가 있을 때만 DB에서 데이터를 가져와 프록시에 채워줘요. 물론! 영속성 컨텍스트에 보관되어 있는 객체들이 다시 접근할 경우엔! 데이터베이스에 접근하는게 아니라 컨텍스트에 있는 객체를 재사용 해요.
Lazy를 적용하면 프록시를 이용해서 껍데기인 참조만 리턴된다.
이게 findOne 과 getOne의 차이예요.
findOne : 데이터베이스에 접근해서 해당 엔티티를 찾아요. 없는 경우엔 null 반환.
getOne : Lazy Evaluation을 적용하기 위해 참조만 리턴해요. 해당 엔티티가 없을 경우 EntityNotFoundException이 발생해요.
Lazy Evaluation이 적용됬는지 가장 쉽게 확인할 수 있는 부분은 findOne(Eager)의 경우 없을 경우 null이 리턴되고, getOne(Lazy)의 경우 없을 경우 EntityNotFoundException 예외가 발생한다는 차이예요.