코딩공작소

JPA 프로그래밍 학습 정리 (4) - 고급 연관관계 매핑 본문

어플리케이션개발/JPA

JPA 프로그래밍 학습 정리 (4) - 고급 연관관계 매핑

안잡아모찌 2024. 1. 19. 23:57
상속 관계 매핑

추천되는 방식은 2 가지

  • 조인 전략
  • 단일테이블 전략

 

조인 전략

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

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
//Parent's Class
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item{
 
    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;
 
    ..
}
 
 
//Child's Class
@Entity
@DiscriminatorValue("A")
public class Album extends Item {
 
    ...
}
 
@Entity
@DiscriminatorValue("M")
public class Movie extends Item {
 
    ...
}
cs
  • @Inheritance(strategy = InheritanceType.JOINED) : 부모 클래스에 사용해야 하며 전략도 정해준다
  • @DiscriminatorColumn(name = "DTYPE") : 구분 컬럼 지정
  • @DiscriminatorValue("M") : 구분 컬럼 값 지정
  • @PrimaryKeyJoinColumn(name = "BOOK_ID") : ID 재정의 하는 어노테이션
  • 장점 :
     - 테이블 정규화
     - 외래 키 참조 무결성 제약조건 활용
     - 저장공간 효율성
  • 단점
     - 조인이 많아 성능 저하
     - 쿼리 복잡
     - INSERT SQL 두 번

 

단일 테이블 전략

테이블 하나에 싹 다 몰아 넣는 전략, 주의점으로는 자식 엔티티가 매핑한 컬럼은 모두 null을 허용해야 한다.

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
//Parent's Class
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item{
 
    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;
 
    ..
}
 
 
//Child's Class
@Entity
@DiscriminatorValue("A")
public class Album extends Item {
 
    ...
}
 
@Entity
@DiscriminatorValue("M")
public class Movie extends Item {
 
    ...
}
cs

 

  • 장점
    - 조인이 필요없으므로 조회 성능이 빠르다
    - 쿼리가 단순
  • 단점
    - 자식 엔티티가 매핑한 컬럼은 모두 null허용
    - 테이블이 커지면 조회 성능이 저하될 가능성이 있다
  • 특징
    - 구분 컬럼을 꼭 사용해야 한다

@MappedSuperclass

부모 클래스는 테이블과 매핑하지 않고 자식에게 매핑 정보만 제공하고 싶을때 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@MappedSuperClass
public abstract class BaseEntity{
 
    @Id @GeneratedValue
    private Long id;
    ...
}
 
 
@Entity
public class Member extends BaseEntity{
    //ID 상속
    //NAME 상속
    ...
}
 
@Entity
public class Seller extends BaseEntity{
    //ID 상속
    //NAME 상속
    ...
}
cs

추상 클래스로 만들어서 매핑 정보를 사용하기 위해 사용

@AttributeOverride(name = "id", column = @Column(name = "MEMBER_ID")) : 부모에게 상속받은 id 속성의 컬럼명을 MEMBER_ID로 재정의했다. 둘 이상을 재정의하려면 @AttributeOverrides를 사용하면 된다.


복합 키와 식별 관계 매핑

비식별관계 매핑이 추천된다.

 - 비식별 관계를 사용하고 기본 키는 Long 타입의 대리 키를 사용하고, 필수적 비식별 관계가 더 좋다.

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
32
33
34
35
36
37
38
39
40
//Parent
@Entity
public class Parent{
 
    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;
    ...
 
}
 
 
//Child
@Entity
public class Child {
 
    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;
 
    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    private Parent parent;
 
    ...
}
 
//GrandSon
@Entity
public class GrandChild{
 
    @Id @GeneratedValue
    @Column(name = "GRANDCHILD_ID")
    private Long id;
 
    @ManyToOne
    @JoinColumn(name = "CHILD_ID")
    private Child child;
    ...
}
cs

 


복합키 : JPA에서는 식별자 둘 이상 사용하려면 별도의 식별자 클래스를 만들어야한다.

그리고 equals 와 hashCode 를 구현해야 한다.

2개의 복합 키를 만들려면 별도의 식별자 클래스가 있어야 한다. 식별자 클래스는

  • Serializable을 구현해야한다
  • equals와 hashCode 메소드를 구현해야 한다.
  • 기본생성자가 있어야한다.
  • public 클래스여야 한다.

 

@IdClass

 :: 비식별 관계 매핑

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
32
33
@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 과 연결
 
    ...
}
 
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(Obejct o){...}
 
    @Override
    public int hashCode(){...}
}
cs

 

 :: 식별 매핑

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//Parent
@Entity
public class Parent{
 
    @Id @Column(name = "PARENT_ID")
    private String id1;
 
    ...
}
 
//Child
@Entity
@IdClass(ChildId.class)
public class Child {
 
    @Id
    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    public Parent parent;
 
    @Id @Column(name = "CHILD_ID")
    private String childId;
 
    ...
}
 
//Child's ID
public class ChildId implements Serializable{
 
    private String parent; //Child.parent 매핑
    private String childId; //Child.childId 매핑
 
    //equals, hashCode 
    ...
}
 
//GrandChild
@Entity
@IdClass(GrandChild.class)
public class GrandChild{
 
    @Id
    @ManyToOne
    @JoinColumns({
        @JoinColumn(name = "PARENT_ID"),
        @JoinColumn(name = "CHILD_ID"))
    })
    private Child child;
 
    @Id @Column(name = "GRANDCHILD_ID")
    priavte String id;
 
    ...
}
 
//GrandChild's ID
public class GrandChildId implements Serializable{
 
    priavte ChildId childId; //GrandChild.child 매핑
    priavte String id; // GrandChild.id 매핑
 
    //equals, hashCode
    ...
}
cs

 

 

@EmbeddedId

::비식별 관계 매핑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//Parent
@Entity
public class Parent{
 
    @EmbeddedId
    private ParentId id;
    
    ...
}
 
@Embeddedable
public class ParentId implements Serializable{
 
    @Column(name = "PARENT_ID1")
    private String id1;
    @Column(name = "PARENT_ID2")
    private String id2;
 
    //equals and hashCode 구현
    ...
}
cs

 

 :: 식별 관계 매핑

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//Parent
@Entity
public class Parent{
 
    @Id @Column(name = "PARENT_ID")
    private String id1;
 
    ...
}
 
//Child
@Entity
public class Child {
 
    @MapsId("parentId")
    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    public Parent parent;
 
    @EmbeddedId
    private String childId;
 
    ...
}
 
//Child's ID
@Embeddable
public class ChildId implements Serializable{
 
    priavte String parentId; // MapsId와 매핑
 
    @Column(name = "CHILD_ID")
    priavte String id;
 
    //equals, hashCode 
    ...
}
 
//GrandChild
@Entity
public class GrandChild{
 
    @MapsId("childId"//GrandChildId.childId 매핑
    @ManyToOne
    @JoinColumns({
        @JoinColumn(name = "PARENT_ID"),
        @JoinColumn(name = "CHILD_ID"))
    })
    private Child child;
 
    @EmbeddedId
    priavte GrandChildId id;
 
    ...
}
 
//GrandChild's ID
@Embeddable
public class GrandChildId implements Serializable{
 
    private ChildId childId; //MapsId와 매핑
 
    @Column(name = "GRANDCHILD_ID")
    priavte String id;
 
    //equals, hashCode
    ...
}
cs

 


일대일 식별 관계

자식 테이블의 기본 키 값으로 부모 테이블의 기본 키 값만 사용한다.

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
//부모
@Entity
public class Board {
 
    @Id @GeneratedValue
    @Column(name = "BOARD_ID")
    private Long id;
 
    @OneToOne(mappedBy = "board")
    private BoardDetail boardDetail;
    ...
}
 
//자식
@Entity
public class BoardDetail{
 
    @Id
    private Long boardId;
 
    @MapsId // BoardDetail.boardId 매핑
    @OneToOne
    @JoinColumn(name = "BOARD_ID")
    private Board board;
 
    ...
}
cs