객체는 참조(주소)를 사용해서 관계를 맺고 테이블을 외래 키를 사용해서 관계를 맺는다. 이때, 객체 연관 관계와 테이블 연관관계를 매핑하는 일을 알아볼 것이다.
다음은 연관관계 매핑을 이해하기 위한 핵심 키워드이다.
다음은 연관관계의 종류이다.
1-1 : 참조키 방식 단방향/양방향, 키 공유 방식 단방향/양방향
N-1 : 단 방향
1-N : 콜렉션 단 방향
N-1/1-N : 양방향
M-N : 단방향/양방향
객체 연관관계
테이블 연관관계
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private String id;
private String username;
//연관관계 매핑
@ManyToOne
@JoinColumn(name="TEAM_ID")
private Team team;
//연관관계 설정
public void setTeam(Team team) {
this.team = team;
}
}public void testSave() {
// 팀1 저장
Team team1 = new Team("team1", "팀1");
em.persist(team1)
// 회원1 저장
Member member1 = new Memeber("member1", "회원1");
member1.setTeam(team1); // 연관관계 설정 member1 -> team1
em.persist(member1);
// 회원2 저장
Member member2 = new Member("member2", "회원2");
member2.setTeam(team1); // 연관관계 설정 member2 -> team1
em.persist(member2);
객체 그래프 탐색(객체 연관관계를 사용한 조회)
객체지향 쿼리 사용
private static void updateRelation(EntityManager em) {
// 새로운 팀2
Team team2 = new Team("team2", "팀2");
em.persist(team2);
// 회원1에 새로운 팀2 설정
Member member = em.find(Member.class, "member1");
member.setTeam(team2);
private static void deleteRelation(EntityManager em) {
Member member1 = em.find(Member.class, "member1");
member1.setTeam(null); // 연관관계 제거
member1.setTeam(null); // 회원1 연관관계 제거
member2.setTeam(null); // 회원2 연관관계 제거
em.remove(team); // 팀 삭제
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private String id;
private String username;
@ManyToOne
@JoinColumn(name="TEAM_ID")
private Team team;
// 연관관계 설정
public void setTeam(Team team) {
this.team = team;
}@Entity
public class Team {
@Id
@Column(name = "TEAM_ID")
private String id;
private String name;
@OneToMany(mappedBy = "team") // 일대다 관계 매핑
private List<Member> members = new ArrayList<Member>();public void biDirection() {
Team team = em.find(Team.clas, "team1");
List<Member> members = team.getMembers();
for(Member member : members) {
System.out.println("member.username = " + member.getUsername());
}
}앞서 언급하였듯이,
객체에는 양방향 연관관계라는 것이 없으며, 두 개의 서로 다른 단방향 연관관계를 애플리케이션 로직으로 잘 묶어서 양방향인 것처럼 보이게 한 것이다.
반면 테이블은 외래 키 하나만으로 양방향 연관관계를 맺는다.
이렇게 엔티티를 양방향 연관관계로 설정하면 객체의 참조는 둘인데, 외래 키는 하나이다. 이때 발생하는 차이로 인해 두 객체 연관관계 중 하나를 정해서 테이블의 외래키를 관리해야 하는데 이것을 연관관계의 주인이라 한다.
✔️mappedBy 속성은 양방향 연관관계에서 외래키를 관리할 객체, 즉 연관관계의 주인을 명시적으로 지정하는 역할을 한다.
이렇게 연관관계의 주인을 정하는 것은 외래 키 관리자를 선택하는 것이다.
그러므로 연관관계의 주인은 테이블을 외래 키가 있는 곳으로 정해야 한다.
✔️@ManyToOne은 항상 연관관계의 주인이 되므로 mappedBy를 설정할 수 없다. 따라서 @ManyTOOne에는 mapppedBy 속성이 없다.
다음은 팀1을 저장하고, 회원1과 회원 2에 연관관계의 주인을 통해서 회원과 팀의 연관관계를 설정하고 저장하는 코드이다.
public void test{
// 팀1 저장
Team team1 = new Team("team1", "팀1");
em.persist(team1);
// 회원 1 저장
Member member1 = new Member("member1", "회원1");
member1.setTeam(team1); // 연관관계 설정
em.persist(member1);
// 회원 2 저장
Member member2 = new Member("member2", "회원2");
member2.setTeam(team1); // 연관관계 설정
em.persist(member2);
}
✔️이때, 연관관계의 주인은 외래키인 Member.team 필드이다. 엔티티 매니저는 이곳에 입력된 값을 사용해서 외래 키를 관리한다.
연관관계의 주인에는 값을 입력하지 않고, 주인이 아닌 곳에만 값을 입력하는 경우 어떠한 문제점이 발생할까❓
ORM은 객체와 데이터베이스 모두 고려해야 한다.
양쪽 모두 관계를 설정하지 않으면 올바른 결과가 아닐 수 있다.
양쪽에 연관관계를 설정하면, 순수한 객체 상태에서도 동작하며, 테이블의 외래 키도 정상 입력된다.
따라서 객체의 양방향 연관관계는 양쪽 모두 관계를 맺어주어야 한다.
다음은 데이터베이스와 객체를 고려하여 양쪽 다 관계를 맺은 코드이다.
public void test{
// 팀1 저장
Team team1 = new Team("team1", "팀1");
em.persist(team1);
Member member1 = new Member("member1", "회원1");
// 양방향 연관관계 설정
member1.setTeam(team1); // member1 -> team1
team1.getMembers().add(member1); // team1 -> member1
em.persist(member1);
Member member2 = new Member("member2", "회원2");
// 양방향 연관관계 설정
member2.setTeam(team1); // member2 -> team1
team1.getMembers().add(member2); // team1 -> member2
em.persist(member2);
}
다음은 setTeam() 메소드로 양방향 관계를 모두 설정하는 코드이다.
public class Member{
private Team team;
public void setTeam(Team team){
this.team = team;
team.getMembers().add(this);
}
}
public void test{
// 팀1 저장
Team team1 = new Team("team1", "팀1");
em.persist(team1);
Member member1 = new Member("member1", "회원1");
member1.setTeam(team1); // 양방향 설정
em.persist(member1);
Member member2 = new Member("member2", "회원2");
member2.setTeam(team1); // 양방향 설정
em.persist(member2);
}
이렇게 리팩토링하면 효율적으로 관리할 수 있다.
이와 같이 한 번에 양방향 관계를 설정하는 메소드를 연관관계 편의 메소드라 한다.
✔️다만 주의해야 할 점이 있다❗
연관관계를 변경할 때는 기존의 연관관계를 삭제해야 한다.