Spring/[인프런 김영한 자바 ORM 표준 JPA 프로그래밍 - 기본편]
[인프런 김영한 자바 ORM 표준 JPA 프로그래밍 - 기본편] 연관관계 매핑 기초
h2boom
2024. 8. 20. 21:35
연관관계 매핑
- 객체를 테이블에 맞춰 설계하는 경우 - 객체를 데이터 중심으로 설계하면 협력 관계를 만들 수 없다.
- 테이블은 외래 키로 조인을 해서 연관된 테이블을 찾는다.
- 객체는 참조를 사용해서 연관된 객체를 찾는다.
단방향 연관관계
@Entity
public class Member {
//내용 생략
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
- 단방향 연관관계 예제
- Team과 Member가 1:N 관계라고 가정할 때
- 객체는 참조를 사용하기 때문에 Team 객체를 Member에 포함
- Team과 Member는 일대다 관계이기 때문에 Team 객체에 @ManyToOne으로 명시
- @JoinColumn(name = "외래키명")으로 외래 키와 매핑해줘야 한다.
- 테이블은 외래 키로 조인을 해서 연관 테이블을 찾기 때문에 Team의 Id 값을 Member에 포함
- 이 경우 객체 Member에서 Team을 참조할 수는 있지만 Team에서 Member 객체를 참조할 수 없다.
- 객체는 참조를 사용하기 때문에 Team 객체를 Member에 포함
- Team과 Member가 1:N 관계라고 가정할 때
양방향 연관관계 / 연관관계의 주인
- 단방향 연관관계에서 양방향 연관관계로 변해도 테이블 연관관계는 변하지 않는다.
- 객체는 단방향 연관관계인 경우 양쪽 모두 알 수 없고 한쪽만 아는 관계이다.
- 테이블은 단방향, 양방향이라는 개념이 따로 존재하지 않고 외래 키로 서로의 연관 관계를 알 수 있다. => JOIN
//양방향 연관관계
@Entity
public class Member {
//내용 생략
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
@Entity
public class Team {
//내용 생략
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
...
}
- 양방향 연관관계 예제
- Team과 Member가 1:N 관계라고 가정할 때
- 1개의 Team에 여러 Member가 포함될 수 있기에 Team 클래스에서 Member List 객체에 @OneToMany로 명시해줘야 한다.
- @OneToMany(mappedBy = "객체명") => 자신과 연관관계에 있는 상대 클래스에서 사용되는 자신의 참조 객체명을 적어준다.
- 1개의 Team에 여러 Member가 포함될 수 있기에 Team 클래스에서 Member List 객체에 @OneToMany로 명시해줘야 한다.
- Team과 Member가 1:N 관계라고 가정할 때
- 객체와 테이블이 관계를 맺는 차이
- 객체 연관관계 = 2개
- ex) Member -> Team 연관관계 1개 (단방향)
Team -> Member 연관관계 1개 (단방향)
- ex) Member -> Team 연관관계 1개 (단방향)
- 테이블 연관관계 = 1개
- Member <-> Team 연관관계 1개 (양방향)
- 객체 연관관계 = 2개
- 객체의 양방향 관계는 양방향 관계가 아닌 서로 다른 단방향 관계 2개이다.
- 객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야한다.
- 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리한다.
- ex) MEMBER.TEAM_ID 외래 키 하나로 MEMBER와 TEAM 테이블의 양방향 연관관계를 가진다.
(양쪽으로 조인 가능)
- ex) MEMBER.TEAM_ID 외래 키 하나로 MEMBER와 TEAM 테이블의 양방향 연관관계를 가진다.
- 객체의 양방향 관계에서 외래 키를 관리할 때 둘 중 하나로 관리해야한다. ( = 연관관계의 주인)
- 연관관계의 주인
- 양방향 매핑 규칙
- 객체의 두 관계중 하나를 연관관계의 주인으로 지정
- 연관관계의 주인만 외래 키를 관리 (등록, 수정)하고 나머지는 읽기만 가능
- 주인은 mappedBy 속성을 사용하지 않고 주인이 아니면 mappedBy 속성으로 주인을 지정한다.
- 비지니스 로직을 기준으로 주인을 선택하는 것이 아닌 외래 키가 있는 곳을 기준으로 주인으로 정해야한다.
(정해진 것은 아니지만 김영한님의 추천)- 이 방식을 사용해야 엔티티와 테이블이 매핑이 된다.
- ex) 만약 반대로 Team.members를 연관관계 주인으로 한다면 Team을 수정했을 때 Member 테이블의 UPDATE 쿼리가 날라가게 되어 혼동이 있을 수 있다.
- 테이블의 입장에서는 외래 키가 있는 곳이 일대다 관계에서 N이며 N쪽이 연관관계의 주인이 되는 것.
- Member - Team 양방향 예제에서는 Member.team이 연관관계의 주인이다.
- ex) member.setTeam()으로 Team을 지정해줘야하고 만약 team.getMembers().add()로 Member를 지정해준다면 DB에 반영되지 않는다.
=> JPA에서는 List members는 읽기 전용이기 때문에 DB에 반영하지 않는다.
- ex) member.setTeam()으로 Team을 지정해줘야하고 만약 team.getMembers().add()로 Member를 지정해준다면 DB에 반영되지 않는다.
- 이 방식을 사용해야 엔티티와 테이블이 매핑이 된다.
- 양방향 매핑 규칙
- 양방향 매핑시 가장 많이 하는 실수
- 연관관계 주인에 값을 입력하지 않는 것.
= 역방향만 연관관계 설정해주는 것.
- 연관관계 주인에 값을 입력하지 않는 것.
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setUsername("member1");
member.setTeam(team);
em.persist(member);
// 두 가지를 모두 생략하면 예상과 다른 결과가 나온다.
em.flush();
em.clear();
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();
- 위 예제 코드에서 em.flush(), clear()가 없다면?
- em.find()로 findTeam을 조회할 때 1차 캐시에서 조회하게 된다.
- 1차 캐시 내부에는 member.setTeam()을 통해 Team에 member가 추가되어 있지 않다.
- findTeam.getMembers()를 하더라도 아무 member도 없게 된다.
- 위와 같은 상황과 테스트 케이스 작성 시 상황에서는 순수한 객체 관계를 고려해서 항상 양방향 매핑 시에는 되도록 양쪽 모두에 값을 넣어줘야 한다!!
- 연관관계 편의 메소드를 생성해서 사용하면 좋다.
@Entity
public class Member {
//생략
//연관관계 편의 메소드
public void setTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
}
- 양방향 매핑시 무한 루프 조심할 것
- ex) toString(), Lombok, JSON 생성 라이브러리...
- 해결 방법
- Lombok 사용시 toString() 기능은 제외하고 사용하는 것이 좋다.
- Controller에서 Entity를 절대 반환하면 안된다.
- JSON 생성 라이브러리에 의해서 무한 루프가 생길 수 있다.
- Entity가 수정될 수 있으며 수정되면 API 스펙이 바뀌기에 사용하는 입장에서 난처해진다.
- ex) toString(), Lombok, JSON 생성 라이브러리...
- 양방향 매핑 내용 정리
- 단방향 매핑만으로도 이미 연관관계 매핑은 완료된다.
- 실무에서 설계 시 양방향 매핑을 사용하지 않고 단방향 매핑으로 설계를 끝내야한다.
- 양방향 매핑은 나중에 JPQL에서 역방향 탐색할 때 필요하면 추가해서 사용하면 된다. (테이블에는 영향 x)
- 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가만 된 것일 뿐이다.
- 가능하면 최대한 단방향 매핑으로만 잘 설계하는 것이 가장 중요하며 양방향은 JPQL을 사용할 때 필요한 경우가 많기에 필요할 때 나중에 추가해라.
- 단방향 매핑만으로도 이미 연관관계 매핑은 완료된다.
출처: [인프런 김영한 자바 ORM 표준 JPA 프로그래밍 - 기본편]
https://www.inflearn.com/course/ORM-JPA-Basic/dashboard
자바 ORM 표준 JPA 프로그래밍 - 기본편 강의 | 김영한 - 인프런
김영한 | JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., 실무에서도
www.inflearn.com