양방향 연관관계와 연견관계의 주인 설정
1. [테이블 연관관계]
- 테이블 연관관계에서는 FK (TEAM_ID) 하나로 TEAM 과 MEMBER 의 서로 연관 관계를 모두 알 수 있다.
2. [양방향 객체 연관관계] = List<> 초기화
- Team 클래스에 List <members> 를 넣어줘야 서로 관계를 모두 알 수 있다
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany // 일대다
private List<Member> members = new ArrayList<>();
@OneToMany(mappedBy = "team") // 일대다
private List<Member> members = new ArrayList<>();
(mappedBy = "team") : 해당 코드를 추가 하였다. 해당 코드의 의미는 Member 클래스의 team 으로 맵핑이 되어 있다는 뜻이다.
public List<Member> getMembers() {
return members;
}
public void setMembers(List<Member> members) {
this.members = members;
}
( getter setter 를 추가 한다).
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
// @Column(name = "TEAM_ID")
// private Long teamId;
// 외래키 설정
@ManyToOne // 연관관계 매핑 시 @ManyToOne
@JoinColumn(name = "TEAM_ID") // 연관 관계 설정할 컬럼 조인 @JoinColumn (현재 Member 클래스의 TEAM_ID 는 PK 이다)
private Team team;
( Member 코드를 살펴보면 private Team team <- 이 코드와 매핑)
try {
// 저장
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();
Member findmember = em.find(Member.class, member.getId());
List<Member> members = findmember.getTeam().getMembers();
for (Member m : members) {
System.out.println("m = " + m.getUsername());
}
tx.commit();
}
(main 코드에서 getTeam() 으로 getMember 의 정보를 가지고 올 수 있는 것을 확인 할 수 있다.)
** 중간 정리 : 객체와 테이블이 관계를 맺는 차이 (*개념)
1) 객체 연관관계 = 2개 (단방향 + 단방향 = 양방향)
- 회원 -> 팀 (첫번쨰 연관관계 )
- 팀 -> 회원 (두번쨰 연관관계 )
>> 정리 : 객체 연관관계는 위의 단방향 연관관계가 두 가지가 있으며 이것을 양방향 연관관계라고 정의 할 수 있다.
사실 양방향이라고 보기보다는 서로 다른 단방향 연관관계가 2개가 있는 것이다,
2) 테이블 연관관계 = 1개
MEMBER 입장에서 TEAM 테이블의 TEAM_ID 를 알고 싶다면 MEMBER FK(TEAM_ID) 를 통해 TEAM 테이블의 PK(TEAM_ID) 를 알 수 있다.
반대로, TEAM 테이블에서 MEMBER 테이블의 TEAM_ID 를 알고 싶다면 TEAM 테이블의 PK(TEAM_ID) 를 통해 MEMBER 테이블의 FK(TEAM_ID) 를 알 수 있다.
이처럼 테이블의 연관관계는 외래키를 설정 해놓으면 두 테이블간의 연관관계를 외래키 하나로 양쪽의 연관관계를
알 수 있는 것이다.
3. 연관관계의 주인
만약 등록 및 수정(변경) 시 반드시 Member 와 Team 둘 중 하나의 객체를 MEMBER 테이블과 연관관계를 위해 FK로 등록 해줘야 한다.
** 양방향 매핑 규칙
- 객체의 두 관계 중 하나를 연관관계의 주인(FK)으로 지정
- 연관관계의 주인만이 외래키를 관리(등록, 수정)
- 주인이 아닌쪽은 읽기만 가능 (수정 X)
- 주인은 mappedBy 속성 사용 불가
- 주인이 아니면 mappedBy 속성으로 주인을 지정
*** 누구를 주인으로?
- 외래 키(FK) 가 있는 곳을 주인으로 정해야 된다.
- 아래의 코드에서는 Member.team 이 연관관계의 주인
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
// @Column(name = "TEAM_ID")
// private Long teamId;
// 외래키 설정
@ManyToOne // 연관관계 매핑 시 @ManyToOne
@JoinColumn(name = "TEAM_ID") // 연관 관계 설정할 컬럼 조인 @JoinColumn (현재 Member 클래스의 TEAM_ID 는 PK 이다)
private Team team;
4.양방향 매핑 시 가장 많이 하는 실수
1) 연관관계의 주인에 값을 입력하지 않음
>> 정상적인 코드 (주인쪽에 데이터 값을 넣어준다.)
양방향 연관관계에서는 진짜 매핑(연관관계 주인) 과 가짜 매핑(주인 반대편) 둘 다 값을 설정 하는 것이 좋다
try {
// 저장
// 읽기 전용
Team team = new Team();
team.setName("TeamA");
// team.getMembers().add(member); // team.
em.persist(team);
// 실제 등록&수정 하는 주인
Member member = new Member();
member.setUsername("member1");
member.setTeam(team); // 진짜 매핑(주인)에 값 설정
em.persist(member);
team.getMembers().add(member); // 가짜 매핑에 값 설정
+ 아래 처럼 연관관계 편의 메서드를 사용하는 것도 방법이다.
public void setTeam(Team team) {
this.team = team;
team.getMembers().add(this); // 가짜 매핑 값 설정 기능 추가
}
(* 편의 메서드 : setTeam() 내부에 team.getMembers().add(this) 를 추가 하면 외부에서 setTeam() 진짜 매핑 메서드를 호출 할 때 가짜 매핑의 값도 동시에 실행 된다.)
try {
// 저장
// 읽기 전용
Team team = new Team();
team.setName("TeamA");
em.persist(team);
// 실제 등록&수정 하는 주인
Member member = new Member();
member.setUsername("member1");
member.setTeam(team); // 진짜 매핑(주인)에 값 설정
em.persist(member);
// team.getMembers().add(member); 연관관계 편의 메서드로 인하여 해당 코드 생략 가능!
(* 메인 클래스에서 가짜 매핑 값 설정 코드 생략 가능)
참고로 이러한 양방향 관계 매핑을 설정 할때는 메서드 이름을 변경하여 특수한 메서드라는 것을 표시 하는 것이 좋다
ex) setName() -> changeName() 등