이번 포스팅에서는 다양한 연관관계들과, 그 연관관계를 JPA에서 어떻게 표현하는지 알아보자.
다대일 관계 (ManyToOne)
가장 많이 사용되기 때문에 꼭 알아야 하는 연관관계이다.
앞선 포스팅에서 살펴본 예시로, Member와 Team의 관계가 이에 해당한다.
Member는 하나의 Team만 가질 수 있고, Team은 다수의 Member를 가질 수 있기 때문에, 이는 Member 입장에서 다대일 관계에 해당한다.
@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;
}
일반적으로 다대일 관계에서 FK는 '다' 쪽에 해당하는 테이블이 가지고 있다.
따라서, '다'쪽에 해당하는 엔티티의 FK 필드에 조인에 사용되는 칼럼이라 표시를 해 줘야 한다.
이는 위 코드에서 @JoinColumn(name="TEAM_ID") 어노테이션으로 표현된다.
해당 어노테이션은 team 필드를 테이블에서의 TEAM_ID 칼럼으로 매핑하고, 그 칼럼을 Team 엔티티가 나타내는 테이블과 조인하는 데 사용한다는 의미를 가진다.
일대다 관계 (OneToMany)
다대일 관계에서 들었던 예시의 반대를 들 수 있다.
위에서 언급한 연관관계는 Team입장에서 일대다 관계에 해당한다.
일대다 관계는 객체 관점에서, '일'쪽에 해당하는 객체가 '다'쪽에 해당하는 객체의 List를 가지고 있는 형태로 나타낸다.
public class Team{
//...
@OneToMany(mappedBy = "team")
@JoinColumn(name = "TEAM_ID")
private List<Member> members = new ArrayList<Member>();
//...
}
연관관계의 주인(Owner)이 Member 라는 것에 유의하자. 연관관계 Owner에 관한 내용은 이전 포스팅인 [JPA] 연관관계 매핑 을 참고하자.
일대일 관계 (OneToOne)
일대일 관계는 외래 키가 Unique 속성을 가질 때 형성된다.
각 Member마다 개인 Locker가 있는 상황을 가정해 보자.
Member는 하나의 Locker만 가질 수 있고, 하나의 Locker는 한 명의 Member만 사용할 수 있다.
일대다 관계와는 달리, 연관관계의 Owner를 선택할 수 있다. 일반적으로 개념 상 더 자주 참조되는 엔티티를 연관관계의 주인으로 지정한다.
이 예시에서는, 회원을 알 때 그 사물함을 찾는 상황과 사물함을 알 때 사용하는 회원을 찾는 상황 중 더 많이 발생하는 상황에 따라 Owner를 지정한다. (전자는 Member, 후자는 Locker)
@Entity
public class Member{
//...
@OneToOne
@JoinColumn(name = "locker_id")
private Locker locker;
//...
}
@Entity
public class Locker{
//...
@OneToOne(mappedBy = "locker")
private Member member;
}
위 코드는 일대일 관계를 양방향 매핑으로 나타낸 코드이다.
Member를 Owner로 지정했기 때문에 Locker에 mappedBy를 지정한 것에 주목하자.
다대다 관계 (ManyToMany)
관계형 데이터베이스에서는 다대다 관계를 정규화된 두 테이블로 나타낼 수 없다.
따라서 일반적으로 조인 테이블(연결 테이블)을 추가해 [A - (일대다 관계) - 조인 테이블 - (다대일 관계) - B]의 형태로 표현한다.
하지만 객체는 컬렉션(List)을 통해 객체 두 개로 다대다 관계를 표현할 수 있다.
예를 들어, Member와 Product의 관계를 살펴 보자. Member는 다수의 Product를 사용할 수 있고, Product 역시 다수의 Member들에 의해 사용될 수 있다.
@Entity
public class Member{
//...
@ManyToMany
@JoinTable(name="member_product")
private List<Product> products = new ArrayList<>();
//...
}
@Entity
public class Product{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy="products")
private List<Member> members = new ArrayList<>();
}
위 코드는 다대다 관계를 양방향 매핑으로 나타낸 코드이다.
@ManyToMany 어노테이션으로 인해 조인 테이블이 자동으로 하나 생성되고, 외래 키의 제약 조건 등도 설정된다.
하지만 @ManyToMany를 사용하면 조인 테이블 자체에 추가적인 데이터를 생성할 수 없다.
만약 각 Member가 Product를 사용한 횟수를 기록하고 싶다면, @ManyToMany 어노테이션만을 사용해서는 구현할 수 없게 된다.
따라서 일반적으로 잘 사용되지 않고, 조인 테이블에 해당하는 엔티티를 별도로 생성해 이 엔티티와 각각 일대다, 다대일 관계를 설정한다.
@Entity
public class Member{
//...
@OneToMany(mappedBy="member")
private List<MemberProduct> memberProducts = new ArrayList<>();
//...
}
@Entity
public class Product{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy="product")
private List<MemberProduct> members = new ArrayList<>();
}
@Entity
@Getter@Setter
public class MemberProduct{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name="member_id")
private Member member;
@ManyToOne
@JoinColumn(name="product_id")
private Product product;
}
MemberProduct 엔티티가 Member, Product를 연결하는 조인 테이블에 해당하는 엔티티이다.
Reference
자바 ORM 표준 JPA 프로그래밍
'Framework > JPA' 카테고리의 다른 글
[JPA] 영속성 컨텍스트 (Persistence Context) - (1) (0) | 2021.03.30 |
---|---|
[JPA] 상속관계 매핑 (0) | 2021.03.30 |
[JPA] 연관관계 매핑 (0) | 2021.03.16 |
[JPA] JPA 매핑 - 기초 (0) | 2021.03.16 |
[JPA] JPA 소개 (0) | 2021.03.16 |