객체를 테이블에 맞추어 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없다.
- 테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다.
- 객체는 참조를 사용해서 연관된 객체를 찾는다.
단방향 연관관계
회원과 팀이 있을 때, 회원 -> 팀 또는 팀 -> 회원처럼 한쪽만 참조하는 것을 단방향 연관관계라고 한다.
- 객체의 참조와 테이블의 외래 키를 매핑하여 객체지향적으로 모델링 할 수 있다.
- Member -> Team 인 단방향 연관관계
Member.class (Team 객체를 참조)
@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;
…
- 객체 연관관계 : Member 객체의 team 필드 사용
- 테이블 연관관계 : MEMBER 테이블의 TEAM_ID 외래 키 컬럼을 사용
Team.class
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
...
테이블 생성 쿼리
create table Member (
MEMBER_ID bigint not null,
USERNAME varchar(255),
TEAM_ID bigint,
primary key (MEMBER_ID)
)
create table Team (
TEAM_ID bigint not null,
name varchar(255),
primary key (TEAM_ID)
)
@ManyToOne
다대일(N:1)이라는 매핑정보이다.
@JoinColumn(name=" ")
조인 컬럼은 외래 키를 매핑할 때 사용한다. name 속성에 매핑할 외래 키 이름을 지정한다
양방향 연관관계
- 객체는 참조를 통해 연관관계를 관리한다.
- 객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단 방향 관계 2개다.
- Member -> Team 연관관계 1개(단방향)
- Team -> Member 연관관계 1개(단방향)
- 객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단 방향 관계 2개다.
- 테이블은 외래 키 하나로 연관관계를 관리한다.
- 회원 <-> 팀의 연관관계 1개(양방향)
- 사실 테이블 연관관계는 방향이란 개념이 없다. 외래 키를 통해 조인하여 무조건 양방향 연관관계기 때문이다.
연관관계의 주인
Member나 Team 객체 둘 중 하나로 외래키를 관리해야 한다.
양방향 매핑 규칙
- 객체의 두 관계 중 하나를 연관관계의 주인으로 지정
- 연관관계의 주인만이 외래 키를 관리(등록, 수정)
- 주인이 아닌 쪽은 읽기만 가능
- 주인은 mappedBy 속성 사용 X
- 주인이 아니면 mappedBy 속성으로 주인 지정
연관관계의 주인 지정
외래 키가 있는 있는 곳을 주인으로 정한다.
Member.class
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String name;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
...
Team.class
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team") //주인은 Member의 team
private List<Member> members = new ArrayList<>();
...
mappedBy 속성
- 연관관계의 주인을 정할 수 있는 속성이다.
- 양방향 매핑일 때 사용한다. 반대쪽 매핑의 필드 이름(Member의 team)을 값으로 주면 된다.
- 주인은 mappedBy 속성을 사용하지 않는다.
- 주인이 아니면 mappedBy 속성을 사용해서 속성의 값으로 연관관계의 주인을 지정해야 한다.
- @OneToMany(mappedBy = "team") private List<Member> members = new ArrayList<>();
- members는 team에 의해 관리가 된다.
- @OneToMany(mappedBy = "team") private List<Member> members = new ArrayList<>();
양방향 연관관계 주의
1. 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자
Team team = new Team();
team.setName("TeamA");
Member member = new Member();
member.setName("member1");
member.setTeam(team);
em.persist(member);
team.getMembers().add(member); //Team의 members에도 값 설정!
em.persist(team);
Member findMember = em.find(Member.class, member.getId()); //1차캐시에서 조회
List<Member> members = findMember.getTeam().getMembers(); //1차캐시에서 조회
for(Member m : members) {
System.out.println("m = " + m.getName());
}
- Member가 주인이니까 Member에만 값을 입력한 경우엔 트랜잭션이 끝나기 전에 만약 Team에서 members를 꺼낼 때 1차 캐시에서 조회하므로 members에는 값이 없어서 members를 조회하면 문제가 된다. 고로 Team에도 값 설정을 해주자.
2. 연관관계 편의 메서드를 생성하자
Member.class에 changeTeam() 추가
//연관관계 편의 메서드
public void changeTeam(Team team) {
this.team = team;
team.getMembers().add(this); //주인이 아닌 반대편 매핑 필드에도 값 설정
}
- Team의 members에도 값 설정하는 연관관계 편의 메소드 생성
Team.class에 addMember() 추가
//연관관계 편의 메서드
public void addMember(Member member) {
members.add(member);
member.setTeam(this);
}
JpaMain.class
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
em.persist(member);
member.changeTeam(team);
//team.addMember(member);
Member findMember = em.find(Member.class, member.getId()); //1차캐시에서 조회
List<Member> members = findMember.getTeam().getMembers(); //1차캐시에서 조회
System.out.println("=======================");
for(Member m : members) {
System.out.println("m = " + m.getName());
}
System.out.println("=======================");
- chageTeam()이나 addMember() 중 한 가지를 선택하여 사용하면 된다. 위 코드에서는 changeTeam()을 사용했다.
3. 양방향 매핑 시에 무한 루프를 조심하자
예: toString(), lombok, JSON 생성 라이브러리
양방향 매핑 정리
- 단방향 매핑만으로도 연관관계 매핑은 완료하자!
- 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것뿐이다.
- 단방향 매핑을 잘 하고 양방향은 필요할 때 추가해도 된다. (테이블에 영향을 주지 않기 때문)
- JPQL에서 역방향으로 탐색할 일이 많음
'JPA' 카테고리의 다른 글
고급 매핑 (0) | 2022.07.26 |
---|---|
다양한 연관관계 매핑 (0) | 2022.07.25 |
엔티티 매핑 (0) | 2022.07.21 |
영속성 관리 (1) | 2022.07.20 |
JPA란? (0) | 2022.07.20 |