인프런 커뮤니티 질문&답변

최태훈님의 프로필 이미지

작성한 질문수

실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화

one to one lazy미동작 메커니즘에 대해서

작성

·

282

0

안녕하세요. 영한님. one to one 에서 FK가 없는쪽에서 지연로딩이 제대로 동작하지 않습니다. 이유는 null을 프록시로 감쌀수없기 때문에 null인지 아닌지를 체크하기 위해 FK가 없는쪽은 조회를 해봐야만 알기 때문이라고 알고있습니다. 그런데 왜 하이버네이트 팀은 null을 프록시로 감쌀수없게 처리해놓았을까요? lazy로 동작하게 끔 해논다음에 실제 참조가 될때 null일수도 아니면 real object가 있을 수도 있게끔 처리해놓으면 lazy로 동작하는 메커니즘이 전혀 문제가없어보이는데요 왜냐하면 one to many일때도 lazy로 동작할때 그 참조하려는 list가 empty list일수도 실제 collection에 객체가 있을 수도 있기 때문에 참조시점에 쿼리가 나가는 방식인데.. one to one도 마찬가지 메커니즘으로 동작하게 끔 만들어졌어야 맞는거아닌가요?
 
답변주시면 감사합니다..

 

답변 3

1

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 최태훈님

단순하게 생각해보면 간단합니다.

member -> locker 라는 참조가 있습니다.

그런데 만약에 locker라는 데이터가 없다고 가정해보겠습니다.

그러면 member -> null(locker의 참조)이 되어야 합니다.

null은 객체가 아니기 때문에 프록시로 감싸거나 하는것이 불가능합니다.

member -> locker(프록시, null 데이터) null인데 이렇게 하면 이미 locker는 null이 아니라 객체(프록시)가 되어버립니다.

감사합니다.

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 최태훈님

많이 고민하시는 모습이 좋네요^^ 사실 저도 이전에 이 문제 때문에 고민을 많이 했습니다.

답을 말씀드리면,

JPA 스펙에서 값이 없는 경우에는 null이어야 하는 규약이 있습니다.

그런데 프록시를 두게 되면 이미 null이 아니라 프록시 객체가 됩니다. 이것 자체가 문제가 되는 것이지요.

예를 들어서 값이 없는 경우 member.locker == null 이라는 공식 자체가 실패하게 되는 것입니다. 값이 없는 경우에도 프록시 객체가 들어있게 되니까요.

대안으로는 프록시 대신에 바이트 코드를 직접 조작하는 로드 타임 위빙이라는 기술을 사용하면 가능하기는 하지만 설정이 너무 복잡하고, 다른 문제들이 추가로 발생할 수 있어서 잘 사용하지는 않습니다.

감사합니다.

0

최태훈님의 프로필 이미지
최태훈
질문자

영한님 답변주셔서 감사합니다. 

말씀하신 예제를 구현해보았습니다.

 

실행결과는 당연히 아래와 같습니다.

첫번째 lock메소드에서 한번 real object가 로드되고 나머지 lock메소드 호출에서는 로드된 real object를 재사용합니다.

 

만약 여기서 하이버네이트 팀이 nullable proxy가 가능하도록 구현하였으면 애초에 저희가 FK가 없는쪽에서 lazy loading이 발생하지 않는 사태를 만들지 않을수있지 않았을까라는 의문이 자꾸 듭니다.

코드는 아래와 같습니다.

 

위 코드는 비록 client가 NPE에 노출되는 점은 있지만,

위의 흐름이 일반적인 관점에서 봤을때 사용자가 설정한 lazy메커니즘을 깨지않으면서 더 자연스러워 보일수도 있을것같습니다.

왜냐하면  클라이언트가 위코드처럼 lazy동작이 아니라 eager로 동작하게끔 설정해놔도

(현재 하이브네이트가 선택한 방식처럼..) fetch이후 null일거기 때문에 NPE에 노출 되는거는 똑같은거 같아보이거든요.

 

둘다 결국 member가 locker가 null인지 널체크를 하면서 lock()을 호출해야하는건 동일해보입니다.

제가 뭔가 잘못이해하고있는 것인지 고견을 주시면 감사하겠습니다. 

좋은 하루되세요.