JAVA/JPA

[JPA] 다양한 연관관계

4Legs 2021. 3. 19. 19:09

이번 포스팅에서는 다양한 연관관계들과, 그 연관관계를 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 프로그래밍

'JAVA > 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