💡 발단
엔티티 생성자에서 정말 기본적인걸 놓치고 있었다.
1:N (일대다) 관계의 두 엔티티가 있었고, 나는 자식쪽에서 부모 엔티티만 연결해주고 있었다. 마치 레코드를 INSERT할 때 FK만 넣어서 참고하는 것처럼 말이다.
기존의 코드:
객관식 문제(Quiz
)와 그 선택지(Choice
)이다.
중요한 부분 외엔 생략, 단순화시켰다.
// 문제쪽의 생성자
@Builder
public Quiz(String properties, List<Choice> choiceList) {
this.properties = properties
this.choiceList = choiceList;
}
// ...
// 선택지쪽의 생성자
@Builder
public Choice(String properties, Quiz quiz) {
this.properties = properties
this.quiz = quiz;
}
// 서비스 코드
Quiz quiz1 = quizRepository.save(
Quiz.builder().properties("a").build()
);
choiceRepository.save(
Choice.builder().properties("b").quiz(quiz1).build()
);
난 단지 생각하기 싫다는 이유로 양쪽의 생성자 메서드에 연관관계 연결 로직을 모두 넣어놨고, 문제가 있다는 사실도 알지 못했다.
가장 큰 문제점은 영속성 컨텍스트 시점에서 생긴다.
부모 엔티티쪽에서 get…List를 했을 때, 영속성 컨텍스트 시점에서는 자식 엔티티들이 조회되지 않는다.
자식쪽에서는 부모를 연결해줬지만, 부모쪽에서는 자식쪽을 갖고 있지 않기 때문이다.
트랜잭션이 커밋된 이후에는 JPA적으로 문제가 없다. 그러나 Spring(영속성 컨텍스트)적으로는 문제가 있다.
위의 코드에선 딱 저장만 하는 로직만 있어서 에러가 발생하지 않았다.
✅ 해결
연관관계 주인 (자식 엔티티)의 생성자에서 연결해주기로 했다.
// 선택지쪽의 생성자
@Builder
public Choice(String properties, Quiz quiz) {
this.properties = properties
setQuiz(quiz);
}
private void setQuiz(Quiz quiz) {
this.quiz = quiz;
quiz.getChoiceList().add(this);
}
// ...
// 문제쪽의 생성자
@Builder
public Quiz(String properties) {
this.properties = properties;
}
❗그리고 save가 너무 반복적으로 많이 호출됐었다. CASCADE ALL임을 이용하면, 부모 엔티티 하나만 저장해도 연관관계 엔티티들은 모두 저장할 수 있다.
Quiz quiz1 = Quiz.builder().question("다음 중 일반 쓰레기가 아닌 것은?").explanation("정답은 바나나 껍질!!\n이 밖에도 파인애플 껍질은 일반쓰레기, 바나나 겁질은 음식물쓰레기, 족발 뼈나 갈비뼈 등은 일반쓰레기라는 점 기억해주세요!").build();
Choice.builder().quiz(quiz1).isAnswer(false).content("견과류 껍데기").build();
Choice.builder().quiz(quiz1).isAnswer(false).content("일회용 티백").build();
Choice.builder().quiz(quiz1).isAnswer(true).content("바나나 껍질").build();
Choice.builder().quiz(quiz1).isAnswer(false).content("계란 껍데기").build();
quizRepository.save(quiz1);
분명히 김영한님 강의를 들으면서 공부했는데, 이렇게 홀랑 까먹은게 참 미스터리하다.
앞으로는 꼭 생각을 하고 코드를 짜자…
참고
'Back-end > Spring Boot' 카테고리의 다른 글
[Spring Boot] 개발 환경 분리와 ddl-auto 재앙 방지 + @Profile (0) | 2023.09.03 |
---|---|
[Spring Boot] @Valid 유효성 검사 (jakarta validation) (0) | 2023.08.27 |
[Spring JPA] 실시간으로 적재되는 데이터와 부모 엔티티 묶어서 가져오기 (0) | 2023.08.10 |
[Spring Boot] 스웨거 springdoc-openapi 적용 (webmvc-ui) (0) | 2023.08.09 |
[Spring Boot] 예외처리 야무지게 하기 (0) | 2023.08.04 |