연관관계 매핑 기초 - 2
"인프런 - 자바 ORM 표준 JPA 프로그래밍 강의를 듣고 작성한 글 입니다."
www.inflearn.com/course/ORM-JPA-Basic#
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다. 초급 웹 개발 프로그
www.inflearn.com
양방향 연관관계
객체와 테이블은 패러다임에서 차이가 있다.
객체는 참조, 테이블은 외래 키를 통한 Join 을 활용한다.
이 둘 간의 차이에서 오는 것을 이해해야 한다.
- 양방향 매핑
Team 도메인 클래스에 Member 데이터를 얻을 수 있게 Member 도메인 클래스의 객체를 참조값으로서 선언한다.
테이블 같은 경우는 단방향이나 양방향이나 차이가 없으나, 객체 참조는 서로서로 참조값을 가지고 있어야 한다.
테이블은 왜 변화가 없을까? : 테이블은 양방향 연관관계에 있어서 서로서로 외래 키 또는 기본 키로 Join 을 하면 된다.
여기서 중요한 점은, 테이블의 연관관계는 외래 키 하나로 양방향이 모두 존재할 수 있다는 것이다.
(사실상 테이블의 연관관계 에서는 방향이라는 개념 자체가 없다, 그냥 외래키 하나만 집어넣으면 연관관계의 설정이 끝난다.)
그러나 단방향 연관관계에서 객체는 한쪽 도메인은 반대쪽 도메인의 참조값을 가지고 있으나, 반대쪽 도메인 에서는 자신을 참조하고 있는 객체를 참조값으로 가지고 있지 않다.
그렇기 때문에 반대쪽 도메인, 즉 Team 클래스에서는 List 타입의 members 를 선언해주어야 반대쪽 도메인 클래스인 Member 클래스의 참조값을 가지고 올 수 있는 것이다.
(List 인 이유는 애초에 Member 와 Team 도메인의 연관관계가 N : 1 이므로 여러개의 Member 데이터를 받을 수 있는 Team 도메인 클래스에서는 그에 맞게 여러개의 데이터를 저장할 수 있는 자료형을 사용해야 한다.)
또한 여기서 Member 와 Team 간의 연관관계가 N : 1 인점을 통해 Member 측에서 선언한 참조 객체에는 @ManyToOne 어노테이션을 사용해야 하고, Team 측에서 선언한 List 타입의 참조 객체에는 @OneToMany 어노테이션을 사용해야 한다.
아래는 양방향 매핑을 위한 Member 도메인 클래스와 Team 도메인 클래스의 코드이다.
- Member.java
@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 참조값과 데이터베이스의 외래 키(TEAM_ID) 가 매핑되어야 한다.
// Getter, Setter ....
}
- Team.java
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
privatge String name;
// 일대다 매핑에서 어떤 것과 연결되어 있는지를 알려준다.
// 반대편 도메인에서 참조값으로 사용하고 있는 변수명으로 지정해주어야 한다.
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>(); // 양방향 연관관계 설정
// ArrayList 로 초기화하는 이유? : add 할 때 NullPointer 가 발생하지 않기 때문(일종의 관례이다.)
// Getter, Setter ....
}
여기서 양방향 연관관계 매핑을 위해 사용한 어노테이션 @OneToMany 에서 mappedBy 속성은 무엇일까?
(해당 속성이 사용되는 이론적인 이유를 모르고 개발에 들어가면 이걸 어느 타이밍에 써야 하는지 감이 오지 않는다.)
이 속성을 사용하기에 앞서 객체와 테이블 간에 연관관계를 맺는 차이를 이해해야 한다.
객체는 연관관계를 맺을 때 연관관계가 되는 키 포인트가 2가지 있다.
1. 회원 -> 팀 연관관계 (단방향)
2. 팀 -> 회원 연관관계 (단방향)
-> 사실상 단방향 연관관계가 2개 있는 것이다. 즉, 객체의 양방향 관계가는 사실 양방향 관계가 아니라 서로 다른 단방향 관계 2개로 이루어져 있는 것이다.
그러나 테이블의 연관관계의 경우는 키 포인트가 1가지 뿐이다.
1. 외래 키 - 기본키 간 Join 관계
즉, 외래 키 값 하나로 모든 연관관계가 끝난다. (테이블은 외래 키 하나로 두 테이블 간의 연관관계를 관리한다.)
반대로 객체의 경우 양쪽 도메인 모두 참조값이 존재해야 하며, 객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야 한다.
이쯤에서 딜레마 가 발생한다.
Member 도메인 클래스에 Team 도메인의 참조값이 존재하고, Member 테이블에 외래 키 값이 존재한다.
그렇다면 Team 에서 Member 도메인의 참조값을 가져야 할 때, 둘 중 어떤 것으로 매핑을 해야 할까?
테이블의 외래키 값을 update 하는데 있어, Member 클래스에서 team 이 바뀌었을 때로 지정해야 할까?
아니면 Team 클래스에서 members 가 바뀌었을 때로 지정해야 할까?
- 위와 같은 딜레마를 해결하기 위한 룰이 존재한다.
둘 중 하나로 외래 키를 관리해야 한다. : Member, Team 두 도메인 클래스 중에서 어떤 도메인 클래스로 외래 키 값을 관리할 지 '주인' 을 결정해야 한다는 것이다.
이것을 두고 연관관계의 주인(Owner) 라고 한다.
객체의 두 관계 중 하나를 연관관계의 주인으로 지정해야 한다.
여기서 중요한점은 주인이 아닌 도메인 클래스쪽은 읽기만 가능해진다는 것이다.(조회만 가능하다)
즉, 데이터를 수정할 때는 주인으로 지정된 도메인 클래스에서 선언한 참조값으로만 변경 시킬 수 있다.
여기서 주인 클래스 측은 mappedBy 속성을 사용할 수 없다.(mappedBy 는 매핑되어 졌다는 뜻의 수동적인 속성이다.)
주인이 아닌 도메인 클래스 측은 mappedBy 로 연관관계 어노테이션 속성을 지정해준다.
그렇다면 어떤 도메인 클래스를 주인으로 지정해야 할까?
-> 외래 키를 가지고 있는 클래스(외래 키가 있는 곳) 를 주인으로 지정해야 한다.
외래 키를 가지고 있는 클래스를 주인으로 지정하지 않았을 경우 생기는 문제점?
: 위의 예제에서 Team 클래스를 주인으로 지정할 경우, 해당 도메인 클래스에서 값을 변경시켰을 때 업데이트 되는 테이블은 Member 테이블이 된다.
다시 말해, 주인으로 지정된 도메인 클래스와 테이블의 이름이 달라진다는 뜻이다.(주인 클래스와는 다른 테이블에 update query 가 전달되게 된다는 뜻)
이럴 경우 어떤 문제가 발생하게 될까?
- 일단 개발할 때 헷갈려지며, 성능과 관련된 이슈가 발생하게 된다.(성능과 관련해서는 뒤에 있는 수업을 듣고 글을 새로 작성해보자.)
그냥 외래 키가 있는 곳을 주인으로 정해놓으면 개발할 때 헷갈릴 일이 없다.
그렇다면 외래 키가 있는 곳을 주인으로 지정하면 어떤 고민거리들이 해결될까?
-> DB 입장에서 볼 때 외래 키가 있는 곳이 무조건 N 이고 외래 키가 없는 곳은 무조건 1 이다.(N 쪽이 무조건 연관관계의 주인이 된다는 뜻이다.)
다음글에서는 양방향 연관관계와 연관관계의 주인을 다루는데 있어서 주의해야 할 점을 작성해보자.