개발 이야기/SPRING

Spring Data JPA - JpaRepository 사용시 persist간 제네릭 인자 이외의 클래스를 사용하는 방법 (using JpaRepository multiple class)

최서리나리 2019. 8. 11. 04:34

* 편법입니다. 참고만 하세요.

 

JpaRepository를 상속받아 사용 할 경우 상속시 제네릭 인자로 던진 클래스만 JpaRepository의 persist가 가능해진다.

 

정확하게는 1depth child까지만.

JpaRepository사용시 인자클래스와 PK type을 받는데 pk 조회의 기준은 인자 클래스가 된다.

그렇기때문에 OneToOne이든, OneToMany든 자식엔티티 까지는 해당 PK Column이 존재하기때문에 JpaRepository에서 접근이 가능하지만, 손자까지는 사용이 불가능한것이다.

 

그렇다고 손자와 인자 클래스간 관계를 맺어 줄 사람은 없다고 생각한다.

 

보통은 이런경우 Repository를 모두 분리한다.

ParentRepository, ChildRepository, GrandChildRepository ... 와 같이.

 

현재 프로젝트의 구조는 One Repository, One Service, One Controller.

Repository를 분리해서는 안된다는 강박관념에 휩싸이기 시작했다.

 

그렇게 찾은 방법은 다음과 같다.

 

 

먼저 persist 할 수 없는 상황을 보자.

 

다음은 현재 Entity의 구조이다.

 

- 부모클래스

@Entity
@Getter
@Setter
@Table(
        name = "TB_EVENT_INFO"
)
public class TB_EVENT_INFO {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "EVENT_SEQ", nullable = false)
    private int eventSeq; // PK

    ...

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "tb_event_info", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<TB_EVENT_QUESTION_LIST> tb_event_question_lists; // 자식

}

 

- 자식클래스

@Entity
@Getter
@Setter
@Table(
        name = "TB_EVENT_QUESTION_LIST"
)
public class TB_EVENT_QUESTION_LIST {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "QUESTION_SEQ", nullable = false)
    private int questionSeq; // PK

    @Column(name = "EVENT_SEQ", nullable = false, insertable = false, updatable = false)
    private int eventSeq; // 부모 PK

    ...

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "EVENT_SEQ")
    private TB_EVENT_INFO tb_event_info; // 부모

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "tb_event_question_list", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<TB_EVENT_QUESTION_ANSWER_LIST> tb_event_question_answer_lists; // 자식

}

 

- 손자클래스

@Entity
@Getter
@Setter
@Table(
        name = "TB_EVENT_QUESTION_ANSWER_LIST"
)
public class TB_EVENT_QUESTION_ANSWER_LIST {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ANSWER_SEQ", nullable = false)
    private int answerSeq; // PK
    
    @Column(name = "QUESTION_SEQ", nullable = false, insertable = false, updatable = false)
    private int questionSeq; // 부모 PK

    ...

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "QUESTION_SEQ")
    private TB_EVENT_QUESTION_LIST tb_event_question_list; // 부모 클래스

}

 

 

JpaRepository는 다음과 같이 사용하고 있다.

JpaRepository<TB_EVENT_INFO, Integer>

 

이런 경우에는 JpaRepository의 save를 사용 할 경우 부모, 자식클래스는 정상적으로 작동하지만

손자클래스의 경우에는 에러를 뿜어낸다. TB_EVENT_INFO의 @Id로 매핑된 객체를 찾지 못했기 떄문이다.

 

 

다음과 같이 손자클래스를 수정하면 save 함수를 오버라이딩하여 사용 할 수 있다.

 

- 손자클래스

@Entity
@Getter
@Setter
@Table(
        name = "TB_EVENT_QUESTION_ANSWER_LIST"
)
public class TB_EVENT_QUESTION_ANSWER_LIST {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ANSWER_SEQ", nullable = false)
    private int answerSeq; // PK
    
    @Column(name = "QUESTION_SEQ", nullable = false, insertable = false, updatable = false)
    private int questionSeq; // 부모 PK
    
    @Transient
    private int eventSeq; // 할아버지의 PK를 추가, Transient 어노테이션을 통해 ddl, dml에서 제외시킴

    ...

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "QUESTION_SEQ")
    private TB_EVENT_QUESTION_LIST tb_event_question_list; // 부모 클래스

}

 

- repository

TB_EVENT_QUESTION_ANSWER_LIST save(TB_EVENT_QUESTION_ANSWER_LIST tb_event_question_answer_list);

 

 

 

첫 줄에도 명시해두었지만, 어디까지나 편법으로 사용하는 방법이다.

가능하다면 Repository를 분리하여 운영 할 수 있도록 하자.

 

끝.