엔티티의 연관관계를 매핑할 때는 다중성, 관계방향, 주인 을 고려해야 한다
데이터베이스 테이블의 N:1 관계에서 외래키는 항상 'N' 쪽이다
N에는 참조하는 필드가 존재하고 1에는 참조하는 필드가 없는경우
일대다 관계는 엔티티를 하나이상 참조할 수 있으므로 자바 컬렉션인 Collection, List, Set, Map 중 하나를 사용해야 한다
일대다 단방향 관계는 약간 특이하다. 보통 자신이 매핑한 테이블의 외래키를 관리하는데, 이 매핑은 반대쪽 테이블에 있는 외래 키를 관리한다.
일대다 양방향 매핑은 존재하지 않는다. 하지만 완전히 불가능한 것은 아니다.
매핑 반대편에 같은 외래 키를 사용하는 다대일 단방향 매핑을 읽기 전용으로 하나 추가하면 된다
insertable = false, updatable = false
로 설정하면 읽기만 가능하다.
but ! 이는 일대다 양방형처럼 보이게 할 뿐 일대다 단방향 매핑이 가지는 단점을 그대로 가진다 !!
그러니 다대일 양방향 매핑을 사용하도록 하자 !
일대일 관계는 아래와 같은 특징이 있다
일대일 관계는 대상테이블 중 누가 외래키를 가질지 선택해야 한다.
JPA는 주 테이블에 외래키가 있으면 좀 더 편리하게 매핑할 수 있다. 이 관계는 다대일 단방향(ManyToOne) 과 비슷하다.
관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없어 릴레이션 테이블을 이용한다. 하지만 객체는 테이블과 다르게 객체 2개로 다대다 관계를 만들 수 있다. ManyToMany를 사용하면 다대다 관계를 편리하게 매핑할 수 있다.
@Entity
public class Member {
@Id @Column(name = "MEMBER_ID")
private String id;
private String username;
@ManyToMany
@JoinTable(name = "MEMBER_PRODUCT",
joinColumns = @JoinColumn(name = "MEMBER_ID"),
inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID"))
private List<Product> products = new ArrayList<Product>();
}
@Entity
public class Product {
@Id @Column(name = "PRODUCT_ID")
private String id;
private String name;
}
@JoinTable
name: 연결 테이블을 지정
joinColumns: 현재 방향인 회원과 매핑할 조인 컬럼 정보를 지정
inverseJoinColumns: 반대 방향인 상품과 매핑할 조인 컬럼 정보를 지정
public void save() {
Product product = new Product();
product.setId("product");
product.setName("상품");
em.persist(product);
Member member = new Member();
member.setId("member");
member.setUsername("회원");
member.getProducts().add(product);
em.persist(member);
}
회원과 상품 연관관계를 설정했으므로 회원을 저장할 때 연결 테이블에도 값이 저장됨
해당 코드를 실행하면 아래와 같은 SQL이 실행된다
INSERT INTO PRODUCT...
INSERT INTO MEMBER ...
INSERT INTO MEMBER_PRODUCT ...
public void find() {
Member member = em.find(Member.class, "member");
List<Product> products = member.getProducts();
for (Product product : products) {
System.out.println("product.name = " + product.getName());
}
}
getProducts() 를 호출하면 아래와 같은 SQL이 실행된다
SELECT * FROM MEMBER_PRODUCT MP
INNER JOIN PRODUCT P ON MP.PRODUCT_ID = P.PRODUCT_ID
WHERE MP.MEMBER_ID = ?
ManyToMany 덕분에 복잡한 다대다 관계를 어플리케이션에서는 아주 단순하게 사용할 수 있다.
양쪽 중 원하는 곳에 mappedBy로 연관관계의 주인을 지정한다.
(mappedBy가 없는 부분이 주인이다 !)
다대다 양방향 연관관계는 편의 메소드를 추가해서 관리하는 것이 편리하다.
@ManyToMany를 사용하면 연결 테이블을 자동으로 처리해주므로 도메인 모델이 단순해지고 편리해진다. 하지만 연관관계 정보 외에 다른 추가적인 정보들을 담을 수 없다.
릴레이션 테이블이 필요하며 해당 엔티티에 @IdClass
를 사용하면 복합 기본키를 매핑할 수 있다.
@Entity
@IdClass(MemberProductId.class)
public class MemberProduct {
@Id
@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member;
@Id
@ManyToOne
@JoinColumn(name = "PRODUCT_ID")
private Product product;
...
}
복합 기본 키
@IdClass
를 사용해서 식별자 클래스를 지정하면 된다.Serializable
을 구현해야 한다equals
와 hashCode
메소드를 구현해야 한다public
이어야 한다@IdClass
를 사용하는 방법 외에 @EmbeddedId
를 사용하는 방법도 있다식별 관계
복합키를 사용하는 방법은 복잡...
컬럼 하나만 기본키로 사용하는 것과 비교해서 복합 키를 사용하면 ORM 매핑에서 처리할 일이 상당히 많아지며 식별자 클래스도 만들어야하고 그 안에 equals, hashCode도 구현해야하며 @IdClass 또는 @EmbeddedId도 사용해야한당..
추천하는 기본 키 생성 전략
다대다 관계를 일대다 다대일 관계로 풀어내기 위해 연결 테이블을 만들 때 식별자 구성 방법