티스토리 뷰

카테고리 없음

[JPA 스터디 3주차] - 고급 매핑

영지는 달리는중 2021. 7. 13. 16:14

상속 관계 매핑

1. 조인 전략

엔티티 각각을 모두 테이블로 만들고 자식 테이블이 부모 테이블의 기본 키를 받아서 기본 키 + 외래 키로 사용하는 전략

객체는 타입으로 구분할 수 있지만 테이블은 타입의 개념이 없으므로 타입을 구분하는 컬럼을 추가해야 한다. (기본값은 DTYPE)

@Entity
@Inheritance(strategy=IngeritanceType.JOINED)  //매핑 전략 지정
@DiscriminatorColumn(name="DTYPE")  //부모 클래스에 구분 칼럼 지정
public abstract class Item {
    @Id @GeneratedValue
    @Column(name="ITEM_ID")
    private Long id;
 
    private String name;
    private int price;
}
 
@Entity
@DiscriminatorValue("A")  //엔티티를 저장할 때 구분 컬럼에 입력할 값 지정
public class Album extends Item {
    private String artist;
}
 
@Entity
@DiscriminatorValue("M")
public class Movie extends Item {
    private String director;
    private String actor;
}
  • 장점
    • 테이블이 정규화된다
    • 외래 키 참조 무결성 제약조건을 활용할 수 있다
    • 저장공간을 효율적으로 사용한다
  • 단점
    • 조회할 때 조인이 많이 사용되므로 성능이 저하될 수 있다
    • 조회 쿼리가 복잡하다
    • 데이터를 등록할 INSERT SQL을 두 번 실행한다
  • 특징
    • JPA 표준 명세는 구분 칼럼을 사용하도록 하지만 하이버네이트를 포함한 몇 구현체는 구분 칼럼 없이도 동작한다

 

2. 단일 테이블 전략

테이블 하나만 사용하여 구분 칼럼으로 어떤 자식 데이터가 저장되었는지 구분

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE) //매핑 전략 지정
@DiscriminatorColumn(name="DTYPE")
public abstract class Item {
    @Id @GeneratedValue
    @Column(name="ITEM_ID")
    private Long id;
 
    private String name;
    private int price;
}
 
@Entity
@DiscriminatorValue("A")
public class Album extends Item {...}
 
@Entity
@DiscriminatorValue("M")
public class Movie extends Item {...}
 
@Entity
@DiscriminatorValue("B")
public class Book extends Item {...}
  • 특징
    • 자식 엔티티가 매핑한 컬럼은 모두 null 값을 허용해야 한다
    • ㄷ구분 칼럼을 꼭 사용해야 한다 - @DiscriminatorColumn
  • 장점
    • 조인이 필요 없으므로 일반적으로 조회 성능이 빠르다
    • 조회 쿼리가 단순하다 
  • 단점
    • 단일 테이블에 모든 정보를 저장하므로 테이블이 커질 수 있다
    • 상황에 따라서는 테이블이 커짐에 따라 조회 성능이 오히려 느려질 수 있다

 

3. 구현한 클래스마다 테이블 전략

자식 엔티티마다 테이블을 만들고 자식 테이블에 각각에 필요한 칼럼이 모두 있다

일반적으로 추천하지 않는 전략. 

@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn(name="DTYPE")
public abstract class Item {
    @Id @GeneratedValue
    @Column(name="ITEM_ID")
    private Long id;
 
    private String name;
    private int price;
}
 
@Entity
public class Album extends Item {...}
 
@Entity
public class Movie extends Item {...}
 
@Entity
public class Book extends Item {...}
  • 특징
    • 구분 컬럼을 사용하지 않는다
  •  장점
    • 서브 타입을 구분해서 처리할 때 효과적이다
    • not null 제약조건을 사용할 수 있다
  • 단점
    • 여러 자식 테이블을 함께 조회할 때 성능이 느리다
    • 자식 테이블을 통합해서 쿼리하기 어렵다

 

4. @MappedSuperclass

부모 클래스를 상속받는 자식 클래스들에게 매핑 저오만 제공하고 싶을 때 사용

실제 테이블과 매핑되지 않고 단순 매핑 정보를 상속할 목적으로만 사용된다

@MappedSuperclass
public abstract class BaseEntity {
    @Id @GeneratedValue
    private Long id; 
    private String name;
}
 
@Entity
@AttributeOverride(name="id", column=@Column(name="MEMBER_ID"))  // 부모에게 물려받은 매핑정보를 재정의하는 방법 
public class Member extends BaseEntity {...}
 
@Entity
public class Seller extends BaseEntity {...}

 

복합 키와 식별 관계 매핑

최근 비식별 관계를 주로 사용하고 꼭 필요한 곳에만 식별 관계를 사용하는 추세이다.

JPA는 식별 관계와 비식별 관계 모두 지원한다.

더보기

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

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

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

- 선택적 비식별 관계 : 외래 키에 null을 허용한다. 연관관계를 선택적으로 맺는다.

복합키 & 비식별 관계 매핑

1. @IdClass

복합키를 가진 부모 클래스

@Entity
@IdClass(ParentId.class)
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 implements Serializable {
    private String id1;  // Parent.id1 매핑
    private String id2;  // Parent.id2 매핑
 
    @Override
    public boolean equals(Object o) {...}
 
    @Override
    public int hashCode() {...}
}

 

자식 클래스

@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으로 매핑한다.

 

2. @EmbeddedId

복합 키를 가진 부모 클래스

@Entity
public class Parent {
    @EmbeddedId
    private ParentId id;
 
    private String name;
}
 
@Embeddable
public class ParentId implements Serializable {
    @Column(name="PARENT_ID1")
    private String id1;
    @Column(name="PARENT_ID2")
    private String id2;
 
    //equals and hashCode 구현
}
  • 객체지향적인 방식
  • 식별자 클래스 조건
    • @Embeddable 어노테이션을 붙여줘야 한다.
    • Serializable 인터페이스를 구현해야 한다.
    • equals, hashCode를 구현해야 한다.
    • 기본 생성자가 있어야 한다.
    • 식별자 클래스는 public이어야 한다.

 

복합키 & 식별 관계 매핑

부모 -> 자식 -> 손자까지 계속 기본키를 전달한다고 가정할 때

 

1. @IdClass

//부모
@Entity
public class Parent { 
    @Id @Column(name="PARENT_ID")
    private String id; 
    private String name; 
}  
 
//자식
@Entity
@IdClass(ChildId.class)
public class Child {
    @Id
    @ManyToOne
    @JoinColumn(name="PARENT_ID")
    public Parent parent;   // 기본키 + 외래키 (식별관계)
    
    @Id
    @Column(name="CHILD_ID")
    private String childId;
 
    private String name;
}
 
//자식 ID
public class ChildId implements Serializable {
    private String parent;
    private String childId;
 
    //equals, hashCode
}
 
//손자
@Entity
@IdClass(GrandChildId.class)
public class GrandChild {
    @Id
    @ManyToOne
    @JoinColumns({
          @JoinColumn(name="PARENT_ID"),
          @JoinColumn(name="CHILD_ID")})
    private Child child;
 
    @Id
    @Column(name="GRANDCHILD_ID")
    private String id;
 
    private String name;
}
 
//손자ID
public class GrandChildId implements Serializable {
    private ChildId child;
    private String id;
 
    //equals, hashCode
}
  • 자식과 손자의 기본 키가 복합 키가 됨에 따라 식별자 클래스를 생성

 

2. @EmbeddedId

//부모
@Entity
public class Parent { 
    @Id @Column(name="PARENT_ID")
    private String id; 
    private String name; 
}  
 
//자식
@Entity
public class Child {
    @EmbeddedId  //
    private ChildId id;
    
    @MapsId("parentId")  // ChildId.parentID 매핑
    @ManyToOne
    @JoinColumn(name="PARENT_ID")
    public Parent parent;
 
    private String name;
}
 
//자식 ID
@Embeddable
public class ChildId implements Serializable {
    private String parentId;  // @MapsId("parentId")로 매핑
 
    @Column(name="CHILD_ID")  
    private String id;
 
    //equals, hashCode
}
 
//손자
@Entity
public class GrandChild {
    @EmbeddedId
    private GrandChildId id;
    
    @MapsId("childId")  // GrandChildId.childId 매핑
    @ManyToOne
    @JoinColumns({
          @JoinColumn(name="PARENT_ID"),
          @JoinColumn(name="CHILD_ID")})
    private Child child;
 
    private String name;
}
 
//손자ID
@Embeddable
public class GrandChildId implements Serializable {
    private ChildId childId;  // @MapsId("childId)로 매핑
 
    @Column(name="GRANDCHILD_ID")  
    private String id;
 
    //equals, hashCode
}
  • 식별 관계로 사용할 연관관계의 속성에 @MapsId를 사용

 

식별 관계는 부모 테이블의 기본키를 자식 테이블로 전파하면서 점점 늘어난다. 결국 조인할 때 SQL이 복잡해지고 기본키 인덱스가 불필요하게 커질 수 있다. 따라서 될 수 있으면 비식별 관계를 사용하고 기본키는 Long 타입의 대리키를 사용하도록 한다.

 

조인 테이블

연관 관계를 관리하는 조인 테이블을 추가하고 두 테이블의 외래 키를 가지고 연관관계를 관리하는 방식

기본은 조인 컬럼을 사용하고 필요하다고 판단되면 조인 테이블을 사용하여 연관관계를 맺어주도록 한다.

 

일대일 조인 테이블

일대일 관계를 만들려면 조인 테이블의 외래 키 컬럼 각각에 총 2개의 유니크 제약조건을 걸어야 한다

@Entity
public class Parent {

    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;
    private String name;

    @OneToOne
    @JoinTable(name = "PARENT_CHILD",
        joinColumns = @JoinColumn(name = "PARENT_ID"),
        inverseJoinColumns = @JoinColumn(name = "CHILD_ID")
    )
    private Child child;
}

@Getter
@Setter
@Entity
public class Child {

    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;
    private String name;
}

 

다대일 조인 테이블

@Entity
public class Parent {

    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;
    private String name;

    @OneToMany
    private List<Child> child = new ArrayList<Child>();
}


@Getter
@Setter
@Entity
public class Child {

    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;
    private String name;


    @ManyToOne(optional = false)
    @JoinTable(name = "PARENT_CHILD",
            joinColumns = @JoinColumn(name = "CHILD_ID"),
            inverseJoinColumns = @JoinColumn(name = "PARENT_ID")
    )
    private Parent parent;

}

 

다대다 조인 테이블

다대다 관계를 만들려면 조인 테이블의 두 컬럼을 합해서 하나의 복합 유니크 제약조건을 걸어야 한다.

@Entity
public class Parent {

    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;
    private String name;

    @ManyToMany
    @JoinTable(name = "PARENT_CHILD",
            joinColumns = @JoinColumn(name = "PARENT_ID"),
            inverseJoinColumns = @JoinColumn(name = "CHILD_ID")
    )
    private List<Child> child = new ArrayList<Child>();
}

@Getter
@Setter
@Entity
public class Child {

    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;
    private String name;

}

 

엔티티 하나에 여러 테이블 매핑

잘 사용하지 않지만 @SecondaryTable을 사용하면 한 엔티티에 여러 테이블을 매핑할 수 있다

@Entity
@Table(name = "BOARD")
@SecondaryTable(name = "BOARD_DETAIL",
pkJoinColumns = @PrimaryKeyJoinColumn(name = "BOARD_DETAIL_ID"))
public class Board{

    @Id @GeneratedValue
    @Column(name = "BOARD_ID")
    private Long id;

    private String title;

    @Column(table = "BOARD_DETAIL")
    private String content;

}
  • @Table을 사용하여 BOARD 테이블과 매핑 후 @SecondaryTable을 사용해서 BOARD_DETAIL 테이블을 추가로 매핑

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함