객체간의 연관관계를 매핑할 때는 다음의 요소들을 고려해야한다.
1. 다중성 - 1:1, 1:N, N:1, N:M
2. 방향성 - 단방향, 양방향
3. 연관관계의 주인
위의 요소들을 고려하여 JPA 에서는 다양한 연관관계 매핑을 사용할 수 있다.
1. 다대일 [N:1]
다대일 단방향
여러개의 객체가 하나의 객체를 참조하는 구조이다.
데이터베이스 테이블의 관계에서는 N 에 해당하는 테이블이 1 에 해당하는 테이블의 키를 외래키로 가진다. 객체의 연관관계에서는 외래키가 있는 테이블의 객체가 1 에 해당하는 테이블을 참조하여 매핑한다.
@ManyToOne, @JoneColumn 등의 어노테이션을 사용하여 구현한다.
@Entity
public class Team {
@Id @GeneartedValue
@Column(name = "TEAM_ID")
private Long id;
// ...
}
@Entity
public class Member {
// ...
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
다대일 양방향
다대일 단방향에서 N 에 해당하는 테이블의 객체만 반대편 객체를 참조했다. 다대일 양방향에서는 반대편 객체에 리스트형의 객체 참조 필드를 추가하여 양쪽에서 서로 조회할 수 있다.
반대편 객체에서 @OneToMany(mappedBy = "객체 이름") 어노테이션을 통해 구현한다.
@Entity
public class Team {
@Id @GeneartedValue
@Column(name = "TEAM_ID")
private Long id;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
// ...
}
@Entity
public class Member {
// ...
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
2. 일대다 [1:N]
일대다 단방향
객체 연관관계에서 "1" 의 객체만 "N" 을 참조하는 구조이다.
데이터베이스 테이블 관계에서는 다대일 관계와 동일하게 "N" 쪽의 테이블이 반대편 테이블의 키를 외래키로 가진다. 하지만 객체 쪽에서는 "1" 쪽의 객체만 반대편 객체들을 참조하고 있다. 이 경우에 "1" 쪽의 참조가 변경되면 데이터베이스에서는 "N" 쪽의 테이블의 키가 변경된다.
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
@OneToMany
@JoinColumn(name = "TEAM_ID")
private List<Member> members = new ArrayList<>();
// ...
}
이 구조에서는 "1" 의 객체가 연관관계의 주인이 된다. 객체와 테이블의 차이로 본인 테이블이 아닌 반대편 테이블의 외래키를 관리하는 특이한 구조가 된다. 이러한 연관관계를 처리하기 위해서 JPA 는 쿼리를 작성할 때 반대편 외래키를 변경해주는 UPDATE SQL 을 추가로 실행한다.
구현시에는 @JoinColumn 을 무조건 사용해서 매핑할 컬럼을 지정해주어야 한다. 그렇지 않으면 JoinTable 을 사용하여 중간 테이블이 하나 자동으로 추가되게 된다.
구조가 복잡해지기 때문에 실무에서는 권장하지 않는 구조이다. 실무에서는 일대다 단방향 매핑보다는 다대일 양방향 매핑을 권장한다.
일대다 양방향
JPA 실제 공식 스펙으로 지원하는 매핑은 아니다. 다만 구현시에 @JoinColumn 에 insertable=false, updatable=false 를 주어서 읽기 전용으로 매핑하여 이와같은 구조로 사용할 수 있다.
이 구조도 일대다 단방향과 동일하게 다대일 양방향으로 변경하여 사용하는 것을 권장한다.
@Entity
public class Member {
// ...
@ManyToOne
@JoinColumn(name = "TEAM_ID", insertable = false, updatable = false)
private Team team;
}
3. 일대일 [1:1]
일대일 단방향
주 테이블이나 대상 테이블 중에 아무데서나 외래키를 선택할 수 있다. 일대일 관계라서 양쪽이 동등하기 때문이다. 이때 외래키에는 데이터베이스 유니크 (UNI) 제약 조건을 추가한다.
JoinColumn() 을 사용하거나, @OneToOne 에서 mappedBy 로 매핑할 컬럼을 지정해줄 수 있다.
외래키의 위치
1) 주 테이블에 외래키
- 주 객체가 대상 객체의 참조를 가지는 것처럼, 주 테이블에 외래키를 두고 대상 테이블을 찾는다.
- 객체지향 개발자 선호하는 방식으로 JPA 에서 매핑이 편리하다.
- 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인이 가능하다. 하지만 값이 없는 경우에 외래키에 null 값을 허용하게 된다.
2) 대상 테이블에 외래키
- 대상 테이블에 외래키가 존재하는 구조로 전통적인 데이터 베이스 구조이다.
- 주 테이블과 대상 테이블을 일대일에서 일대다로 변경할 때 테이블 구조를 유지할 수 있다.
- JPA 프록시 기능의 한계로 지연 로딩을 설정해도 항상 즉시 로딩되는 문제가 있다.
일대일 양방향
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@OneToOne
@JoinColumn(name = "LOCKER_ID")
private Locker locker;
// ...
}
public class Locker {
@Id @GeneratedValue
@JoinColumn(name = "LOCKER_ID")
private Long id;
@OneToOne
@JoinColumn(name = "MEMBER_ID")
private Member member;
// ...
}
4. 다대다 [N:M]
관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다. 그렇기 때문에 두 테이블 간에 연결 테이블을 추가해서 일대다, 다대일 관계로 풀어내야한다. 객체는 컬렉션을 사용하여 객체 2개로 다대다 관계를 구현할 수 있다.
구현 시에는 @ManyToMany, @JoinTable 어노테이션을 사용하여 구현할 수 있다.
실제 실무에서 사용하지 않는 것이 좋다. 다대다 대신에 중간에 연결 테이블 객체를 만들어서 다대일 - 일대다의 형식으로 만드는 것이 좋다.
'Tech > JPA' 카테고리의 다른 글
[JPA] 프록시와 지연로딩 (0) | 2022.05.23 |
---|---|
[JPA] 상속관계 매핑 (0) | 2022.05.23 |
[JPA] 연관관계 매핑 (2) - 양방향 연관관계 (0) | 2022.05.11 |
[JPA] 연관관계 매핑 (1) - 단방향 연관관계 (0) | 2022.05.09 |
[JPA] 영속성 관리 (0) | 2022.05.09 |