본문 바로가기

프로그래밍(JPA)

Entity Relation 시, ID Generator로 생성한 ID FK로 자동 할당하기

Entity에 ID Generator로 생서한 ID값이 연관관계에 의한 Entity에 자동으로 할당하는 방법에 대해서 기술한다.

 

우리가 사용하는 DB는 Entity와 Entity간의 연결관계(PK-FK)로 이루어져 있다.

 

JPA는 테이블을 하나의 Class로 정의하고, 연관관계를 Composite attribute로 관리한다.

 

연관관계에 대한 설명은 다른 블로그나 몇번의 검색을 하면 잘 알 수 있으니 여기에서는 설명하지 않겠다.

 

이번 글에 주요 내용은 ID Generator로 생성한 PK를 연관관계에 있는 Entity의 FK로 어떻게 하면 자동으로 할당할 수 있는지에 대한 내용이다.

 

일단 아래의 내용을 읽기 전에 Entity 간의 관계에 대한 학습을 먼저 하고 오기를 부탁한다.

 

PK-FK 관계에서의 식별/비식별관계, OneToOne, OneToMany관계, 단방양/양방향 관계에 대해서 말이다.

 

그런 기본적인 내용은 알고 있다는 전제하에 설명을 진행하도록 하겠다.

 

각 상황별 관계에 따라 작업해야 하는 내용을 요약하면 다음과 같다.

 

FK 관계 연관관계 관계설정 식별관계시 작업 내용
식별 OneToOne 양방향 연관관계 주인 Entity에서 @MapsId annotation 사용
비식별 OneToMany 단방향 연관관계 주인 Entity에서 @JoinColumn 내에 nulable = false 사용
자식Entity(FK를 가진 Entity)의 FK에 해당하는 컬럼에 insertable = false, updatable = false 사용
식별 OneToMany 양방향 연관관계 주인 Entity에서 @JoinColumn 내에 insertable = false, updatable = false 사용, @PrePersist를 사용하여 FK값 설정
비식별 ManyToOne 단방향 연관관계 주인 Entity에서 FK에 해당하는 컬럼에 insertable = false, updatable = false 사용
식별 ManyToOne 단방향 연관관계 주인 Entity에서 @JoinColumn 내에 insertable = false, updatable = false 사용, @PrePersist를 사용하여 FK값 설정

 

보통은 테이블관계를 ManyToMany는 사용하지 않고, 설정하는 방법을 모르기 때문에 여기에서는 제외하도록 했다.

 

OneToOne은 FK가 PK이기 때문에 식별관계 밖에 없고, 양방향 관계 설정으로 해결해야 한다.

 

OneToMany의 경우 비식별관계일 경우에는 단방향관계로 설정해야 하고, 식별관계일 경우에는 양방향 관계로 설정해야 한다.

 

ManyToOne의 경우 비식별/식별 관계 모두 단방향 관계 설정으로 해결할 수 있다.

 

위의 FK 관계와 연관관계에 따라서 ID Generator로 생성한 PK를 연관관계에 있는 Entity의 FK로 Entity 단에서 어떻게 처리해야 하는지에 대한 내용이다. 그것이 관계설정과 작업내용에 기술하고 있다.

 

그러면 각 상황에 따른 실제 소스코드로 설명한다.

 

연관관계에 있는 두 Entity를 기준이 되는 Entity를 부모Entity라고 칭하고, 다른 Entity를 자식 Entity라고 칭하겠다.

 

1. OneToOne 관계

부모 Entity

@OneToOne 사용, MappedBy 속성 사용

1
2
3
4
5
6
7
    @Id
    @Column
    private String parentId;
 
    @OneToOne(mappedBy = "parent", cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
    Child child;
 
cs

자식 Entity

@OneToOne 사용, @MapsId 사용, @JoinColumn으로 연관관계 field정의

1
2
3
4
5
6
7
8
    @Id
    @Column
    private String parentId;
 
    @OneToOne
    @MapsId
    @JoinColumn(name = "parentId")
    private Parent parent;
cs

OneToOne 관계에서의 주요 내용은 연관관계의 주인Entity인 자식 Entity에서 @MapsId 를 사용한다는 것이다.

2.OneToMany 관계

1) 비식별

부모Entity

@OneToMany 사용, @JoinColumn 내에 nullable = false 사용

1
2
3
4
5
6
7
8
    @Id
    @Column
    private String parentId;
 
    @OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
    @JoinColumn(name = "parentId", nullable = false)
    private List<Child> childs = new ArrayList<>();
 
cs

자식 Entity

FK로 사용되는 Column 에 insertable = false, updatable = false 사용

1
2
    @Column(insertable = false, updatable = false)
    private String parentId;
cs

OneToMany 비식별 관계는 부모Entity에서 nullable = false사용과 자식Entity에서 insertable = false, updatable = false로 처리한다.

2) 식별관계

부모Entity

@OneToMany tkdyd, MappedBy 사용

1
2
    @OneTomany(mappedBy = "parent", cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
    private List<Child> childs = new ArrayList<>();
cs

자식Entity

@ManyToOne 사용, @JoinColumn으로 연관관계 field정의, insertable = false, updatable = false 속성 정의

@Prepersist로 정의된 메소드에서 부모Entity와의 연관관계 field의 값을 자식 Entity의 field에 설정

1
2
3
4
5
6
7
8
9
10
11
12
    @Id
    @Column
    private String parentId;
 
    @ManyToOne
    @JoinColumn(name = "parentId", insertable = false, updatable = false)
    private Parent parent;
 
    @PrePersist
    private void prePersist() {
        this.parentId = this.parent.getParentId();
    }
cs

기본적으로 JPA에서는 PK에 뭔가를 자동적으로 할당해주는 것을 금지하고 있다.

 

OneToOne에 한해서 MapsId로 그것을 처리해주고 있다.

 

OneToMany의 식별관계를 양방향으로 설정하는 이유가, 부모에서 자동으로 생성한 ID를 persist하기 전에 연관관계의 부모에서 값을 get/set 하도록 한다.

(@PrePersist는 persist()호출 또는 Sprint JPA의 save() 호출 시, Persist가 되기 전에 실행하도록 하는 annotation이다)

 

3. ManyToOne

1)비식별

부모Entity

@ManyToOne사용, FK컬럼에 insertable = false, updatable = false 속성 사용

1
2
3
4
5
6
    @Column(insertable = false, updatable = false)
    private String parentId;
 
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parentId")
    private Parent parent;
cs

MayToOne은 부모Entity가 FK를 가지고 있기 때문에, 부모의 FK컬럼에 insertable = false, updatable = false를 설정하면 된다.)

2)식별

부모Entity

@ManyToOne사용, @JoinColumn으로 연관관계 field정의, insertable = false, updatable = false 속성 정의

@PrePersis로 정의된 메소드에서 연관관계 field의 값을 설정

1
2
3
4
5
6
7
8
9
10
11
12
    @Id
    @Column
    private String parentId;
 
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parentId", insertable = false, updatable = false)
    private Parent parent;
 
    @PrePersist
    private void prePersist() {
        this.parentId = this.parent.getParentId();
    }
cs

ManyToOne의 식별관계는 OneToOne식별관계의 자식부분과 동일하다.

 

마치며

JPA를 처음에 접하고, 그 매력에 빠졌지만, 무지에 의한 좌절의 연속이었다.

 

개인적으로 뭔가를 하면 멋지게 해야 한다고 생각하기 때문에, Entity간에 공유해야 하는 PK, FK를 자동으로 생성하고, 공유하는 것을 Entity내에서 처리해야 한다고 생각했다.

 

그래서 다양한 삽질을 통해 위와 같은 결과를 얻게 되었고, 나와 같은 어려움에 있는 개발자들에게 조금이나마 도움이 되었으면 하는 마음이다.

 

위의 코드 방식은 현재 내가 적용해서 사용하고 있는 방법이지만 앞으로 버전업이 되면서 방식은 바뀔 수 있다.

 

IT는 기술변화의 속도가 엄청나게 빠른 분야라는 것을 일을 하면 할 수록 알게되고, 그 속도는 시간이 가면 갈 수록 빨라진다고 생각한다.

 

기술 전반적인 내용이 아닌 겪었던 어려움을 해결했던 내용 위주로 공유하도록 하겠다.