2023. 3. 21. 15:39ㆍspring
[문제발생]
테스트를 위해 @beforeEach를 이용해 DB에 데이터를 세팅해놓고, 양방향 맵핑시 데이터가 올바르게 들어갔는지 확인을 하는 과정이었다.
@Test
@DisplayName("답변에 연결된 질문 찾기 vs 질문에 달린 답변 찾기")
@Transactional //테스트 환경에서는 리포지토리를 이용한 통신만 가능하다.
void t009(){
QuestionEntity question = this.questionRepository.findById(Long.valueOf(3)).orElse(null);
assertNotNull(question);
List<AnswerEntity> answerList = question.getAnswerList();
assertEquals(1, answerList.size());
}
- QuestionEntity와 AnswerEntity는 1:N 연관관계이다.
- @beforeEach에서 테스트를 위해 QuestionEntity [1] 에 대한 답변으로 AnswerEntity [1]을 등록하였다.
- 그리고 해당 QuestionEntity를 통해 등록되어있는 AnswerEntity를 찾고자 answerList를 반환하였다.
- 하지만 계속 answerEntity에서 NullPointException이 발생했다.
[해결 과정]
- 처음엔 맵핑이 잘못되어 있나 싶어서 맵핑을 확인하니 문제가 없었다.
- JPA의 영속성에 관한 문제가 발생하는가 해서 @Transactional을 붙이며 코드를 수정했으나 계속해서 예외 발생한다.
- 어느 시점에서 예외가 발생하나 log를 찍어보니 @beforeEach 데이터 세팅시 예외가 발생하는 것을 확인하였다.
AnswerEntity answer = AnswerEntity
.builder()
.question(question3)
.content("홍길동입니다.")
.build();
question3.addAnswer(answer); //바로 여기 !!
AnswerEntity savedAnswerEntity = answerRepository.save(answer);
- 그래서 AnswerEntity 코드를 다시 한 번 보았다.
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class QuestionEntity extends BaseEntity {
@Column(length = 200)
private String subject;
@Column(columnDefinition = "TEXT")
private String content;
@OneToMany(mappedBy = "question", cascade = CascadeType.REMOVE)
private List<AnswerEntity> answerList = new ArrayList<>();
public void addAnswer(AnswerEntity answer) {
answerList.add(answer);
}
}
- 보다시피, answerList는 @oneToMany로 클래스 내부에서 객체를 생성해 주었는데 @Builder 로 인해 null값으로 덮이게 되어서 NullPointException을 발생시킨 것이었다.
@Builder은 QuestionEntity 생성 직후에 만들어지기 때문에 생성자에서 초기화 시킨 것은 덮이게 된다.
[해결 방안]
@Builder.Default
@OneToMany(mappedBy = "question", cascade = CascadeType.REMOVE)
private List<AnswerEntity> answerList = new ArrayList<>();
- 필드에서 의도적으로 초기화 시켜줘야 하며, Builder로 인해 덮이기를 원치 않는 필드는 @Builder.Default로 Builder의 영향에서 벗어날 수 있다.
'spring' 카테고리의 다른 글
[SpringBoot] 글 삭제했을 때 원래 페이지로 redirect 되게 하기 (0) | 2023.03.31 |
---|---|
[SpringBoot] 하나의 form을 재활용 하기 위한 방법 (ex_등록,수정을 하나의 form으로 <csrf>) (0) | 2023.03.31 |
[SpringBoot] 실행 전 데이터 세팅 방법, CommandLineRunner (0) | 2023.03.20 |
[SpringBoot] 쿠키와 세션 (0) | 2023.03.16 |
스프링 DB 테스트 (0) | 2023.02.01 |