개발 도서 읽기/자바 ORM 표준 JPA 프로그래밍

[7장]7.3.1~7.3.2 복합 키 : 비식별 관계 매핑

nnhhmm 2024. 11. 19. 18:02

1) 식별관계 vs 비식별 관계 

  • 식별관계 : 부모 테이블의 기본 키를 내려 받아서 자식 테이블의 기본 키 + 외래 키로 사용하는 관계
  • 비식별관계: 부모 테이블의 기본 키를 받아서 자식테이블의 외래 키로만 사용하는 관계
    - 필수적 비식별 관계 : 외래 키에 NULL 을 허용하지 않음
    - 선택적 비식별 관계 : 외래 키에 NULL 을 허용

2) 복합 키 : 비식별 관계 매핑 

 

기본 키를 구성하는 컬럼이 하나일 때

@Entity

public class Hellod {

    @Id

    private String id;

}

 

복합키 일때

-> JPA는 @IdClass와 @EmbeddedId 2가지 방법 제공

@IdClass

부모클래스

@Entity
@IdClass(ParentId.class)
public class Parent {
	@Id
    @Column(name ="PARENT_ID1")
    private String id1;	// ParentId.id1과 연결
    
    @Id
    @Column(name = "PARENT_ID1")
    private String id2;	// ParentId.id2와 연결
    
    private String name;
    ...
 }

 

식별자클래스

public class ParentId implements 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 o) {...}
    
    @Override
    publid int hashCode() {...}

 

@IdClass 를 사용할 때 식별자 클래스가 만족해야할 조건

  • 식별자 클래스의 속성명과 엔티티에서 사용하는 식별자의 속성명이 같아야한다.
    - 예제의 Parent.id1 과 ParentId.id1 
  • Serializable 인터페이스를 구현해야한다.
  • 기본 생성자가 있어야 한다.
  • 식별자 클래스는 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);

 

자식클래스 예제

@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;
   
   private String name;
}

 

부모 테이블의 기본 키 컬럼이 복합 키이므로 자식 테이블의 외래키도 복합 키다. 

여러컬럼을 매핑해야 하므로 @JoinColumns 어노테이션을 사용하고 가각의 외래 키 컬럼을 @JoinColumn으로 매핑한다.

 

@EmbeddedId

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

@Entity
public class Parent{

	@EmbeddedId
    private ParentId id;
    
    private String name;
    ...
}

Parent 엔티티에서 식별자 클래스를 직접 사용하고 @EmbeddedId 어노테이션을 적어주면 된다.

 

@Embeddable
public class ParentEd implements Serializable{

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

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

 

@EmbededId절 적용한 식별자 클래스가 만족해야하는 조건

  • @Embeddable 어노테이션을 붙여주어야 한다.
  • 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);

 

복합 키는  equals()와. hashCode()를 필수로 구현해야 한다. 

영속성 컨텍스트는 엔티티의 식별자를 키로 사용해서 엔티티를 관리한다. 그리고 식별자를 비교할 때 equals()와 hashCode()를 사용한다. 따라서 식별자 객체의 동등성이 지켜지지 않으면 예상과 다른 엔티티가 조회되거나 엔티티를 찾을 수 없는 등 영속성 컨텍스트가 엔티티를 관리하는 데 심각한 문제가 발생한다.