개발자로서 살아남기

Spring boot & JPA 조인 시 null 반환하기

코드 살인마 2022. 9. 19. 10:58
728x90

개요

회사에서 PC방 관련 서비스를 이관하는 작업을 진행 중인데 테이블이 하나 추가되었고, 이 테이블을 기존의 사용하던 real_student(예시) 테이블에서 몇몇 필드가 제거된 테이블이다.(fake_student)

추가된 fake_student와 real_student를 함께 사용할 것이기 때문에 로직 수정이 필요한 상태이다.

요구사항

새로 추가된 fake_student와 real_student를 함께 사용한다는 말을 좀 더 풀어서 설명하자면 fake_student의 PK 값이 있는지 확인하고, 없다면 real_student에서 해당 PK 값을 찾는 것이 핵심 로직이다.

그러나 문제는 기존의 있던 real_student에 많은 테이블들이 조인되어 있다는 점이다. 이를 그대로 두고, fake_student를 이 시스템에 적용해야한다.

해결 방안

처음에는 단순하게 fake_student와 real_student를 강제로 1대1 조인하여 real_student가 조인된 다른 테이블에서 조회 시 자동으로 조인되게 하려고 했다. 그러나 JPA는 어떤 테이블에 대해 CRUD API를 호출하면 해당 테이블 관점에서 조인된 테이블만 호출한다.

@Getter @Setter @ToString
@Entity
@Table(name = "student_support_fund")
public class StudentSupportFund {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "no")
    private long no;
    @Column(name = "month")
    private LocalDate month;
    @Column(name = "rank")
    private int rank;

    @ManyToOne     // 조인부분
    @JoinColumn(name = "crm",insertable = false, updatable = false,nullable = false)
    private realStudent realStudent;

    @ManyToOne
    @JoinColumn(name = "crm",insertable = false, updatable = false,nullable = false)
    private StudentApproved StudentApproved;
}

위 예시를 예로 들자면 다른 테이블의 조인 관계랑은 상관없이 real_student와 student_approved 테이블만 호출한다.

그래서 다른 방법으로 real_student이 조인된 모든 테이블에 fake_student를 추가하는 방법을 생각했다.

많은 로직 수정이 필요하겠지만 두 테이블을 함께 사용할 수 있는 확실한 방법이라 생각한다.

  1. real_student이 조인되어 있는 테이블에 student_approved 추가(student_support_fund , student_monthly_rank)
  2. 관련 테이블을 조회하는 부분 로직 수정
  3. real_student 테이블 조회하는 부분 로직 수정

real_student이 조인되어 있는 테이블에 student_approved 추가하는데 real_student을 조인시킬 때 같은 어노테이션을 사용했는데, 둘 중 하나의 테이블에만 값이 있을 때 오류가 발생했다. null로 반환만 되면 조건문을 통해 두 테이블 중 값이 있는 필드만 사용하면 되는데, 조인이되는순간 해당 FK가 없으면 noEntity 에러가 발생하였다.

 

핀트를 잘 못 잡아서 지연로딩 문제 인 줄 알고, fetch의 방법을 바꾸고, optional, nullable 등 여러 옵션을 사용했지만 여전히 같은 문제가 발생하였다

.

결과적으로 조인 할 때 FK가 없으면 null로 반환하는 어노테이션 @NotFound(action = NotFoundAction.IGNORE)을 사용하면 아래와 같다.

 

수정후

@Getter @Setter @ToString
@Entity
@Table(name = "student_support_fund")
public class StudentSupportFund {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "no")
    private long no;
    @Column(name = "month")
    private LocalDate month;
    @Column(name = "rank")
    private int rank;

    @ManyToOne     // 조인부분
    @NotFound(action = NotFoundAction.IGNORE)
    @JoinColumn(name = "crm",insertable = false, updatable = false,nullable = false)
    private realStudent realStudent;

    @ManyToOne
    @NotFound(action = NotFoundAction.IGNORE)
    @JoinColumn(name = "crm",insertable = false, updatable = false,nullable = false)
    private StudentApproved StudentApproved;
 }
 
 /////////////// 사용예시 ///////////////////
 
 StudentSupportFund studentSupportFund = findAllByPk(...)
 if(studentSupportFund.getRealStudent() == null)
 	FakeStudent fakeStudent = studentSupportFund.getFakeStudent();