해결된 질문
작성
·
97
·
수정됨
0
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예)
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)
[질문 내용]
현재 회원 엔티티 시점 Member(회원) 1 : N Pet(반려견) 일대다 관계인 상태입니다.
-Member(회원) 엔티티
-Pet(반려견) 엔티티 연관관계 설정해 둔 모습입니다.
-이게 문제가 되고 있는 서비스 로직입니다. MemberRepository에서 회원의 role(역할)을 찾은 후,
role(역할)이 CUSTOMER(고객)인 경우 해당 회원의 정보와 갖고 있는 List<>형태의 pet을 한 번에 갖고 와서 DTO로 변환 해서 반환하려고 합니다. 실제로 어떻게 리포지토리에서 조회하고 있는지 보여드리겠습니다.
-role(역할)이 CUSTOMER(고객)인 경우 findByCustomerId()로 위와 같이 회원과 pet을 한 번에 조회하고 있습니다. (실제 쿼리문이 나가는 것을 봤을 때도 fetch join 쿼리로 나가서 pet이 프록시가 아닌 실 객체 데이터로 조회되는 것을 확인했고, 디버깅도 해봤는데 pet 컬렉션에 데이터가 있는 것도 확인했습니다.)
다음으로 member 엔티티를 DTO로 어떻게 반환하는 지 보여드리겠습니다.
-Pet(반려견)을 PetResponse라는 DTO로 다시 바꿔서 반환 시켜주려고 하고 있는 상황이고
-이게 PetResponse 입니다.
여기까지가 현재 저의 문제가 되고 있는 코드와 동작 방식입니다.
현재 뜨고 있는 오류는 lazyinitializationexception 에러가 발생하고 있습니다.
알아보니 트랜잭션 주기 안에서 컬렉션을 조회하는 것이 아니라, 하이버네이트 세션이 끝난 후 영속성 컨텍스트가 사라진 상태에서 엔티티가 갖고 있는 컬렉션(pets)을 조회하게 돼서, 트랜잭션 내에 존재한 영속성 컨텍스트 내의 pets를 조회하는 것이 아닌 새로운 영속성 컨텍스트에서 조회를 하게 돼버려서 실 객체 데이터가 조회되는 것이 아닌 프록시 객체가 조회가 되고 있다는 내용으로 이해했습니다.
그래서 제가 여태 해본 방법으로는,
서비스 레벨의 findById(long id) 메서드에서 member 정보만 따로 조회하고 pet만을 따로 각각 조회한 후 dto로 반환 해보기 (예: memberRepository.findByid()로 회원만 조회, petRepository.findbyCustomerId()로 pet만 따로 조회 후, new MemberResponse.GetCustomer()로 반환 -> 안됨.(X)
현재 메서드 레벨 위에 작성되어 있는 트랜잭션 어노테이션이 readOnly = true로 읽기 전용이면 pets를 초기화 할 수도 없을 수 있다길래 readOnly = true 옵션도 빼 보기 -> 안됨.(X)
yml 설정 파일에 spring: jpa: open-in-view: true 옵션 설정 해보기 -> 안됨.(X)
Member(회원) 엔티티에 읽기 전용으로 있는 List<> pets를 지연 로딩이 아닌 즉시로딩으로 바꾸기. -> 이 경우에만 됨.(O)
Hibernate.initialize(member.getPets()); 을 사용해서 컬렉션 강제 초기화 하기 -> 안됨.(X)
현재 이렇게 까지 시도해 봤고 pets를 즉시 로딩으로 바꾸는 경우 말고는 아예 되지 않는 상황입니다.
보통 저와 같은 경우 간단하게 해결되는 문제던데 스택오버플로우와 구글의 힘을 빌려 검색해서 시도해 본 결과 해결되지 않았습니다.. 제가 어떤 실수를 하고 있는지, 뭐가 문제인지 봐주시면 감사하겠습니다.!!
추가 설명이 필요하거나 깃허브 주소라도 필요하면 댓글로 남기겠습니다!
답변 2
0
안녕하세요. 이도현님, 공식 서포터즈 y2gcoder입니다.
시도하신 해결방법은 같은 상황에서 저도 다 사용해볼 법한 것들입니다!
관련이 없을 수도 있겠으나 저는 패키지명을 관례대로 지키지 않으면 예상치 못한 에러가 자주 발생했습니다. 자바, 스프링 부트 프로젝트에서는 패키지명을 자바 관례대로 소문자로만 작성해주지 않으면 예상치 못한 에러가 발생했습니다.
패키지명들을 전부 소문자로 바꿔주시고 (com.petcare.domain.member 와 같이 대문자나 하이픈도 제거해주십쇼) 다시 시도해보시면 좋을 것 같습니다 🙂
감사합니다.
Caused by: org.attoparser.ParseException
저는 해당 부분이 의심스럽습니다. 저는 해당 부분이 타임리프에서 발생하는 에러가 아닌가 생각이 듭니다!
혹시 타임리프까지 Member 엔티티가 그대로 전달되는 케이스가 있다면, 도현님께서 말씀해주신 것이 다 들어맞겠다는 생각이 들었습니다 🙂
사이드 프로젝트라 이 이상의 도움을 드리지 못하는 점 죄송합니다😭
와.. 선생님께서 말씀해주신 것을 듣고 갑자기 문득 생각이 나서 타임리프 코드를 봤는데, 타임리프에서 발생하는 에러가 맞았습니다. 컨트롤러에서 서비스를 호출해서 Member 조회 후, 모델 어트리뷰트에 조회한 member를 전달해주는 방식이었는데, 막상 타임리프 코드에서는 현재 로그인한 세션의 member의 정보를 뿌려주는 코드로 작성해놔서 모든 방법을 해도 문제였었네요. 타임리프 코드를 서버에서 전달받은 member의 정보로 보여주는 코드로 수정하니 모두 잘 해결되었습니다. 비록 사이드 프로젝트이지만 친절히 답변해 주셔서 감사했습니다.! 새해 복 많이 받으십쇼.
0
안녕하세요, 인프런 AI 인턴입니다. 이도현님.
LazyInitializationException
은 엔티티의 지연 로딩 시 영속성 컨텍스트 밖에서 데이터를 접근하려고 할 때 발생하는 오류입니다. 이 오류는 보통 데이터베이스 세션이 이미 닫힌 상태에서 지연 로딩된 데이터에 접근할 때 발생합니다.
이 상황에서 고려할 수 있는 몇 가지 해결 방법을 소개드릴게요:
트랜잭션 범위 확장: @Transactional
어노테이션이 서비스 내에 여러 메서드에 걸쳐 제대로 설정되어 있는지 확인하세요. 트랜잭션 범위가 포함되어야 Lazy
로드가 제대로 작동할 수 있습니다.
Fetch Join 사용 확인: 현재 리포지토리 코드에서 펫 데이터를 페치 조인하여 가져오고 계신데, 이 부분이 제대로 작동하고 있는지 쿼리 로그를 통해 확인하세요. Fetch Join을 사용하면 컬렉션의 지연 로딩 문제를 해결할 수 있습니다.
DTO 사용하여 페치: 현재 이 문제가 해결되지 않는다면, DTO를 별도로 작성하여 필요한 정보만 하나의 쿼리로 가져오는 방법도 고려해볼 수 있습니다.
@Transactional
설정 재확인: 서비스 메서드에서 readOnly = true
속성을 제거하였을 때 전체 애플리케이션 설정이나 다른 관련 환경 설정이 제대로 되어있는지 확인해 볼 필요가 있습니다.
open-in-view = true
사용 가능성 제고: 이 설정은 보통 위험할 수 있는 방법이기 때문에 실제로 해결되는지에 대한 확인이 필요합니다. 이를 통해 지연 로딩도 가능하지만 발생할 수 있는 문제를 염두에 두세요.
이와 비슷한 문제에 대한 논의가 있는 기존의 질문들을 아래 안내드립니다:
LazyInitializationException
오류 관련 문제와 해결 방식들을 다루고 있어 유익한 자료가 될 것입니다.저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
안녕하세요! 답변해 주셔서 감사합니다. 말씀해 주신 대로 Caused by: org.attoparser.ParseException: failed to lazily initialize a collection of role: com.petcare.domain.member.Member.pets: could not initialize proxy - no Session
이런 식으로 모든 패키지명을 소문자로 바꿨는데도 여전히 똑같은 오류가 뜹니다 ㅠㅠ