본문 바로가기

JPA

7.3 복합키와 식별관계 매핑

7.3.1 식별관계 vs 비식별관계

- 식별관계

식별관계는 부모 테이블의 기본키를 내려받아서 자식테이블의 기본키 + 외래키로 사용하는 관계다.

 

- 비식별관계

- 비식별관계는 부모테이블의 기본키를 받아서 자식테이블의 외래키로만 사용하는 관계이다.

 

- 필수적 비식별 관계 : 외래키에 NULL을 허용하지 않는다. 연관관계를 필수로 맺어야한다.

- 선택적 비식별 관계 : 외래키에 NULL을 허용한다. 연관관계를 맺을지 선택할 수 있다.

 

7.3.2 복합키 : 비식별 관계 매핑

JPA는 영속성 컨텍스트에 엔티티를 보관할 때 엔티티의 식별자를 키로 사용한다. 그리고 식별자를 구분하기 위해 equals와 hashcode를 사용해서 동등성 비교를 한다. 때문에 식별자 필드가 두개 이상일 경우에는 별도의 식별자 클래스를 만들고 그곳에 equals와 hashcode를 구현한다.

 

@IdClass

- 부모 클래스

@Entity
@IdClass(Parent)
public class Parent {

	@Id
    @Column(name = "PARENT_ID1")
    private String id1; //ParentId.id1과 연결
    
    @Id
    @Column(name = "PARENT_ID2")
    private String id2; //ParentId.id2와 연결
    
    private String name;
}

 

- 식별자 클래스

public class ParentId implement Serializable {
	private String id1; //Parent.id1 매핑
    private String id2; //Parent.id2 매핑
    
    public ParentId(){}
    
    public ParentId(String id1, String id2){
    	this.id1 = id1;
        this.id2 = id2;
    }
    
    @Override
    public boolean equals(Object 0){}
    
    @Override
    public int hashcode(){}
}

 

@IdClass를 사용할 때 식별자 클래스는 다음 조건을 만족해야한다.

- 식별자 클래스의 속성명과 엔티티에서 사용하는 식별자의 속성면이 같아야 한다. 예제의 Parent.id1과 Parent.id2, 그리고 Parent.id2와 ParentId.id2가 같다.

- Serializable 인터페이스를 구현해야한다.

- equals, hashcode를 구현해야 한다.

- 기본생성자가 있어야한다.

- 식별자 클래스는 public이어야 한다.

 

- 식별자 클래스를 이용하여 조회

Parent parent = new Parent();
parent.setId1("myId1"); //식별자
parent.setId2("myId2"); //식별자
parent.setName("parentName");
em.persist(parent);

ParentId parentId = new ParentId("myId1", "myId2");
Parent parent = em.find(Parent.class, parentId);

- 저장코드를 보면 식별자 클래스인 parentId가 보이지 않는데, em.persist()를 호출하면 영속성 컨텍스트에 엔티티를 등록하기 직전에 내부에서 Parent.id1, Parent.id2 값을 사용해서 식별자 클래스인 ParentId를 생성하고 영속성 컨텍스트의 키로 사용한다.

 

- 자식클래스

@Entity
public class Child {

	@Id
    private String id;
    
    @ManyToOne
    @JoinColumns({
    	@JoinColumn(name = "parent_id1", referencedColumnName = "parent_id1"),
        @JoinColumn(name = "parent_id2", referencedColumnName = "parent_id2")
    })
    private Parent parent;
}

부모 테이블의 기본 키 컬럼이 복합키이므로 자식테이블의 외래키도 복합키다. 따라서 외래 키 매핑시 여러 컬럼을 매핑해야 하크로 @JoinColumns 어노테이션을 사용하고 각각의 외래 키 컬럼을 @JoinColumn으로 매핑한다.

@JoinColumn의 name속성과 referencedColumnName 속성의 값이 같으면 referencedColumnName은 생략이 가능하다.

 

@EmbeddedId

@IdClass가 데이터베이스에 맞춘 방법이라면 @EmbeddedId는 좀 더 객체지향적인 방법이다.

@Entity
public class Parent {

    @EmbeddedId
    private ParentId id;
    private String name;
    
}

@IdClass와는 다르게 @EmbeddedId를 적용한 식별자 클래스는 식별자 클래스에 기본 키를 직접 매핑한다.

 

@Entity
public class ParentId implements Serializable {

    @Column(name = "PARENT_ID1")
    private String id1;
    @Column(name = "PARENT_ID2")
    private String id2;
    
    //equals and hashcode 구현
}

 

@EmbeddedId를 적용한 식별자 클래스는 다음 조건을 만족해야 한다.

 

- @EmbeddedId 어노테이션을 붙여줘야한다.

- Serializable 인터페이스를 구현해야한다.

- equals, hashCode를 구현해야한다.

- 기본 생성자가 있어야 한다.

- 식별자 클래스는 public이어야 한다.

//저장
Parent parent = new Parent();
ParentId parentId = new ParentId("myId1", "myId2");
parent.setId(parentId);
parent.setName("parentName");
em.persist(parent);

//조회
ParentId parentId = new ParentId("myId1", "myId2");
Parent parent = em.find(Parent.class, parentId);

복합 키에는 @GenerateValue를 사용할 수 없다. 복합 키를 구성하는 여러 컬럼 중 하나에도 사용할 수 없다.

'JPA' 카테고리의 다른 글

08 프록시 객체  (0) 2021.01.15
6.1 다대일  (0) 2021.01.11
05 연관관계 매핑  (0) 2021.01.10
기본키 매핑방식  (0) 2021.01.10
hibernate.ejb.naming_strategy  (0) 2021.01.10