@ManyToOne
public class Answer {
@ManyToOne
private Question question;
}
- Answer가 Question을 참조한다.
- DB에서는 question_id 외래키가 생성된다.
- 외래키는 항상 자식(N쪽) 에 생성된다.
@OneToMany
public class Question{
@OneToMany(mappedBy = "question")
private List<Answer> answers = new ArrayList<>();
}
Question에서 답변 목록을 바로 조회할 수 있다.
mappedBy = "question" 의 의미
mappedBy = "question"은 연관관계의 주인이 Answer.question 필드라는 의미이다.
@ManyToOne
private Question question;
여기 있는 question 필드를 가리키는 것이다.
따라서 Question은 관계를 관리하지 않고 조회만 담당한다.
@ManyToOne이 실제 외래키를 관리하는 연관관계의 주인이고, @OneToMany(mappedBy = "...")는 그 관계를 조회하기 위한 반대편 필드이다. 따라서 연관관계의 변경은 항상 ManyToOne 쪽에서 이루어진다.
왜 new ArrayList<>()를 해야 할까?
private List<Answer> answers = new ArrayList<>();
초기화를 하지 않으면 answers가 null 상태가 된다. 그러면
question.getAnswers().add(answer);
를 실행했을 때 NullPointerException이 발생한다.
미리 빈 리스트를 만들어 두면 언제든 안전하게 add()를 사용할 수 있다.
또한 JPA에서도 컬렉션 필드는 항상 초기화하는 것이 권장되는 방식이다.
OneToMany vs ManyToOne
| 구분 | OneToMany | ManyToOne |
| 관계 | 1:N | N:1 |
| 위치 | 부모 엔티티에 위치 | 자식 엔티티에 위치 |
| 필드 타입 | Collection 타입 (List, Set 등) | 단일 엔티티 타입 |
| 외래키 | 자식 테이블에 생성 | 자식 테이블에 생성 |
| 예시 | Question 엔티티의 answers 필드 | Answer 엔티티의 question 필드 |
| 조회 방식 | 부모에서 자식들을 바로 조회 가능 | 자식에서 부모를 바로 조회 가능 |
| 사용 목적 | 부모 엔티티에서 자식 엔티티들을 관리할 때 | 자식 엔티티에서 부모 엔티티를 참조할 때 |
답변 조회 방법
1. Repository를 통해 직접 조회하는 방식
List<Answer> answers = answerRepository.findByQuestion(q5);
List<Answer> answers = answerRepository.findByQuestionId(5);
2. 객체지향 방식
List<Answer> answers = q5.getAnswers();
이미 Question 객체를 가지고 있다면 Repository를 다시 호출하지 않고 바로 답변 목록을 조회할 수 있다.
Repository보다 객체를 이용하는 방식
기존에는 Answer를 직접 생성하고 연관관계를 연결해야 했다.
Question question2 = questionRepository.findById(2).get();
Answer answer = new Answer();
answer.setContent("답변 내용");
answer.setQuestion(question2);
answerRepository.save(answer);
이를 Question 내부에서 처리하도록 변경할 수 있다.
Question question5 = questionRepository.findById(5).get();
question5.addAnswer("답변 내용");
Question 내부
public class Question {
@OneToMany(mappedBy = "question", cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private List<Answer> answers = new ArrayList<>();
// 이렇게 Question 내부에 메서드를 생성
public void addAnswer(String content) {
Answer answer = new Answer();
answer.setContent(content);
answer.setQuestion(this);
answers.add(answer);
}
}
이렇게 만들면 Answer 생성, Question 연결, 컬렉션 추가를 모두 Question이 책임지게 된다.
객체가 자신의 데이터를 스스로 관리하므로 책임이 명확해지고 캡슐화도 향상된다.
'BackEnd > Spring' 카테고리의 다른 글
| [Spring] Lazy Loading vs Eager Loading (0) | 2026.08.05 |
|---|---|
| [Spring] JPA CascadeType과 Flush (0) | 2026.08.05 |
| [Spring] Spring Boot에서 웹 요청은 어떻게 처리될까? (@Controller, @GetMapping) (0) | 2026.08.05 |
| [Spring] 외래키 (0) | 2026.08.04 |
| [Spring] 테스트 환경 분리(dev / test) (0) | 2026.08.04 |
댓글