묻고 답해요
129만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결스프링 DB 2편 - 데이터 접근 활용 기술
[JPA] save할 때 @ManyToOne 필드가 null로 나옵니다.
안녕하세요! 강의를 통해 JPA를 접하게 되어 간단한 프로젝트를 진행하고 있습니다.프로젝트 진행 중 에러가 발생하여 질문드리고자 합니다! 아래는 답변 엔티티 코드입니다.public class Answer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(columnDefinition = "TEXT") private String content; @ManyToOn private Question question; //fk private LocalDateTime createAt; private LocalDateTime modifyAt; }보시는 것처럼 답변 엔티티에는 질문 엔티티(Question)가 fk로 설정되어 있습니다. 이 연관관계에서 이전까지는 아무런 문제없이 answer.save(..., ..., question, ..., ...); 을 하면 정상작동을 했지만, 갑자기 다시 기능을 실행하니 아래의 에러가 발생했습니다. JdbcSQLIntegrityConstraintViolationException: Referential integrity constraint violationhibernater 쿼리를 확인하니 fk 필드가 null로 찍혔습니다. 위 에러와 쿼리를 보고 fk 필드에서 오류가 난 것을 알게 되어 확실한 이유 없이 @ManyToOne(fetch = fetchType.LAZY)로 수정하니 제대로 동작했습니다.지연 로딩을 사용해야 한다는 말을 듣고 수정하긴 했지만 아직도 왜 해당 에러가 발생했는지는 모르겠습니다.
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
프록시 관련해서 질문이 있습니다.
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]영한님 안녕하세요. JPA로드맵, 스프링 로드맵 모두 들으면서 기초 개념을 잡아가고 있습니다. 감사합니다. 다름이 아니라, 학습을 하면서 프록시 객체의 개념이 많이 등장하는데요, 이런 프록시 객체에 대해 궁금한 점이 생겨서 질문드립니다. 스프링이 빈을 싱글톤으로 관리하기 위해 CGLIB 바이트코드 조작을 통해 가짜 프록시 객체를 스프링 빈으로 등록한다고 알고 있습니다. 그리고 스프링 AOP 적용 시에, Pointcut의 대상이 되는 객체(pointcut 대상 메서드가 포함된 객체)에 Advice를 적용하기 위해 CGLIB 또는 JDK 동적 프록시 기술로 프록시 객체를 만든다 배웠습니다. 마지막으로 여기서 JPA에서 지연로딩을 하기 위해 가짜 프록시 객체를 생성하고 실제 프록시 초기화 시점에 DB에서 쿼리를 불러온다고 보았습니다. (--> 이 프록시 객체는 영속성 컨텍스트가 시작될 때 생성되었다가 사라지는 것 같긴 합니다..) 실제로는 더 많은 사례가 있겠지만, 일단 제가 알기로는 이렇게 3가지가 있었던 것 같은데, 이때 생성되는 프록시 객체들은 다 별개의 객체들일까요? 예를 들어 싱글톤 빈으로 등록된 객체가 있는데(CGLIB 프록시), 이 객체가 AOP 적용 대상이라면 CGLIB 혹은 JDK 동적프록시를 통해 또다른 프록시 객체가 생성되는 건지 궁금합니다. 추가로, 지연로딩을 위한 프록시 객체는 영속성 컨텍스트가 시작될 떄 생성되어 영속성 컨텍스트가 종료되면 사라지는 것인지 궁금합니다. 질문이 다소 모호해서 죄송합니다.
-
미해결실전! 스프링 데이터 JPA
LazyInitializationException 에러 관련 질문 드립니다.
안녕하세요. LazyInitializationException 예외 관련 질문 드립니다. 우선 Employee 와 Company 라는 엔티티가 N:1 관계로 셋팅 되어 있고(ManyToOne으로 설정 했고 조인 컬럼도 명시 했으며, OneToMany쪽에는 mappedBy도 맞게 설정 하였습니다. 양쪽 다 모두 Lazy로딩으로 해놨구요), JpaRepository도 각각 적절히 셋팅되어 있으며, 다음과 같은 서비스 클래스가 있다고 가정했을 때, @Service class CompanyService { @Autowired EmployeeRepository employeeRepository; // JpaRepository를 상속한 인터페이스 @Transactional public test() { Employee employee = employeeRepository.findById(1L); // LazyInitializationException 예외 발생 Company company = employee.getCompany(); // could not initialize proxy - no session 예외 발생 } } Employee employee = employeeRepository. findById(1L) 을 호출 하면, 디버거에서 보이는 employee 객체 내의 company 값은 실제 객체 대신 다음과 같은 예외가 보입니다. method threw 'org.hibernate.LazyInitializationException' exception. Can not evaluate com....$HibernateProxy$lfgdgjt.toSting() 그리고 employee의 getCompany를 호출 하는 순간, could not initialize proxy - no session 이라는 예외가 발생합니다. 지연 로딩 시 영속성이 유지 되어야 하지만 findById 의 호출이 끝나는 순간 트랜잭션이 종료 되고 세션이 닫히는 게 이유가 아닐까 싶어, 트랜잭션 어노테이션을 서비스 레이어의 메소드에 추가도 해보고 전파 속성도 여러가지로 바꾸어 봤지만 문제가 해결되지 않았구요.. EAGER 로딩으로 바꾸 거나, 아래 속성을 줄 경우에 해결이 되었습니다.. enable_lazy_load_no_trans=true enable_lazy_load_no_trans속성이 자칫 N+1 문제를 야기할 수 있어 안티 패턴인 것 같아 근본 원인을 알고 싶은데요.. 물론 페치 조인으로도 해결 할 수 있지만, 위의 예제 코드도 당연히 동작을 해야 할 것 같은데 왜 트랜젝션 어노테이션을 주었음에도 영속성 세션이 test() 메소드 내에서 지속 되지 않는지 궁금합니다. --- 추가로 트랜잭션 로그를 찍어 보았는데 이벤트 순서가 아래와 같습니다.. test() 메소드의 트랜잭션 생성, EntityManager 열림 => findById() 가 호출 => SimpleJpaRepository의 inner transaction이 생성 및 새로운 EntityManager열림 => findById 메소드 호출 종료 => commit & inner transaction 종료 => EntityManager 닫힘 => test()메소드의 트랜잭션 resume 결국 inner트랜잭션이 별도로 생성되는게 문제인 것 같은데 이게 트랜잭션 propagation을 REQUIRE로 해도 각각 별도의 트랜잭션을 생성하고 있습니다. 어떤 부분을 더 의심하고 디버깅 해봐야 할까요?