![[자바 ORM 표준 JPA 프로그래밍 - 기본편] 5. 연관관계 매핑 기초](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBJMej%2FbtsC4I8lALl%2FjRzPtFLA7M0H6mPVWdNI0K%2Fimg.png)
인프런 김영한 강사님의 [자바 ORM 표준 JPA 프로그래밍 - 기본편] 을 수강하고 정리한 글입니다.
자바 ORM 표준 JPA 프로그래밍 - 기본편 강의 - 인프런
현업에서 실제로 JPA로 개발을 하고 있습니다. 그런 입장에서보면 지금 작성하고 있는 코드들이 어떻게 작동하는지 이해하는데 큰 도움을 주는 강의입니다. 다음은 제가 느낀 이 강의의 장점들
www.inflearn.com
📌 연관관계 매핑
✅ 객체를 테이블에 맞춘 모델링
객체를 테이블에 맞추어 데이터 중심으로 모델링하게 되면, 협력 관계를 만들 수 없다.
테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다.
객체는 참조를 사용해서 연관된 객체를 찾는다.
테이블과 객체, 둘은 다르다. 이런 큰 간격이 있다.
📌 단방향 연관관계
✅ 객체 지향 모델링
아래는 객체의 참조와 테이블의 외래 키 매핑을 코드로 표현한 것이다.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
private int age;
// @Column(name = "TEAM_ID")
// private Long teamId;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
…
아래는 연관관계 저장을 코드로 표현한 것이다.
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team); //단방향 연관관계 설정, 참조 저장
em.persist(member);
아래는 참조로 연관관계를 조회하는 코드다. (객체 그래프 탐색)
//조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam();
아래는 연관관계 수정을 코드로 나타낸 것이다.
// 새로운 팀B
Team teamB = new Team();
teamB.setName("TeamB");
em.persist(teamB);
// 회원1에 새로운 팀B 설정
member.setTeam(teamB);
📌 양방향 연관관계
✅ 양방향 매핑
객체 연관관계에서는,
기존에는 member에서 team은 갈 수 있어도, team에서 member는 갈 수 없었다.
테이블 연관관계에서는,
양방향을 하기 위해서 테이블 연관관계에서는 딱히 추가할 것이 없다.
조인만 하면 서로의 연관을 모두 얻을 수 있기 때문이다.
외래키 하나로 양방향이 모두 가능하다.
이를 JPA 코드로 표현해보자면,
아래 Member 엔티티에서는 단방향과 코드가 동일하다. 즉, 달라지는 것이 없다.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
private int age;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
…
}
그러나 Team 엔티티 코드에는 새로운 컬렉션이 추가된다.
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>();
…
}
양방향에서 반대 방향으로 객체 그래프를 탐색하는 방법은 아래와 같다.
//조회
Team findTeam = em.find(Team.class, team.getId());
int memberSize = findTeam.getMembers().size(); //역방향 조회
✅ 연관관계의 주인과 mappedBy
객체와 테이블 간, 연관관계를 맺는 방법의 차이를 우선 이해해야 한다.
양방향 객체 연관관계는 총 2 개의 단방향 관계이다. (회원 -> 팀, 팀 -> 회원)
아래처럼 List members가 추가되었다.
양방향 테이블 연관관계는 총 1 개의 양방향 관계이다. (회원 <-> 팀)
아래 처럼, 달라지는 게 없다.
이 때, 둘 중 하나로 외래 키를 관리해야 한다.
결국 양방향 매핑의 규칙은 아래와 같다.
- 객체의 두 관계 중 하나를 연관관계의 주인으로 지정해야 한다.
- 그리고 그 주인만 외래 키를 관리한다. (등록, 수정 등)
- 주인이 아닌 관계는 Read-Only
- 주인은 mappedBy 속성을 사용할 수 없다.
- 주인이 아니라면, mappedBy 속성을 활용하여 주인을 연결해야 한다.
그렇다면 어떤 관계를 주인으로 정해야 할까?
외래 키가 있는 곳을 보통 주인으로 정한다.
Member - Team 관계에서는, Member.team이 연관관계의 주인이 된다.
양방향 매핑 시, 연관관계의 주인에 값을 입력해야 한다.
순수한 객체 관계를 고려하면 항상 양쪽 모두 값을 입력해야 한다.
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
team.getMembers().add(member);
//연관관계의 주인에 값 설정
member.setTeam(team); //**
em.persist(member);
양방향 매핑 시, 무한 루프를 항상 조심해야 한다.
toString(), lombok, JSON 생성 라이브러리 등에서 무한 루프를 조심해야 한다.
이를 해결하기 위해서는 일단 Lombok에서는 toString() 사용을 지양하고, JSON 생성 라이브러리는 Controller 단에서 entity를 절대 반환하지 않음으로 해결하자.
entity는 DTO로 변환한 후 반환하는 것이 여러모로 안전하다.
✅ 양방향 매핑 정리
테이블 설계는 단방향 매핑 만으로 모두 끝나야 한다.
대신 양방향 매핑은 반대 방향으로 조회하는 기능이 추가된 것 뿐이다. (객체 그래프 탐색)
특히 JPQL 에서 역방향으로 탐색할 일이 많다.
따라서, 단방향 매핑은 기본이고, 양방향 매핑은 필요할 때만 추가해도 상관없다. (테이블에 전혀 영향이 없기 때문)
연관관계의 주인은 비즈니스 로직 기준이 아니라, 외래 키의 위치를 기준으로 정하자.
'Backend > JPA' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 7. 고급 매핑 (0) | 2024.01.04 |
---|---|
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 6. 다양한 연관관계 매핑 (0) | 2024.01.04 |
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 4. 엔티티 매핑 (0) | 2024.01.04 |
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 3. 영속성 관리 - 내부 동작 방식 (0) | 2024.01.04 |
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 2. JPA 시작하기 (0) | 2024.01.03 |
개발자가 되고 싶어요.