본문 바로가기
개발/JPA

JPA 거짓 연관 관계 맺기(프록시)

by Debin 2025. 11. 22.
반응형

 

동일한 테이블의 2개의 엔티티

회사 프로젝트에 아래 예시 코드와 유사한 코드가 존재한다.

@Table(name = "prod")
@Entity
class Prod(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0,

    @Column(name = "name")
    val name: String,

    @JoinColumn(name = "vendor_id")
    @ManyToOne(fetch = FetchType.LAZY)
    val vendor: Vendor,

    /**
     * 더 연관 관계가 있다고 가정
     */
)

@Table(name = "prod")
@Entity
class ProdForExternalSystem(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0,

    @Column(name = "name")
    val name: String,

    // @ManyToOne, @OneToOne 등 연관 관계를 제거하고 외래 키 값만 보유한다.
    @Column(name = "vendor_id")
    val vendorId: Long,
)

 

  • Prod 엔티티는 실제 비즈니스를 해결하기 위해서 적극적으로 사용하는 클래스
  • ProdForExternalSystem 엔티티는 배치를 통해 외부 시스템과 연동할 때 사용하는 클래스

두 클래스는 모두 공통적으로 같은 테이블인 prod를 보고 있다.

 

그런데 왜 이렇게 분리가 되어있을까?

 

코드 히스토리를 추적해보니 Prod에서 @ManyToOne, @OneToOne 등 연관 관계를 조회해서 배치에서 저장하는게 싫어, ProdForExternalSystem을 작성했다고 나온다.

 

테스트 코드로 예시를 작성해보았다.

 

실제로 조회 쿼리가 1번 실행된다.

그래서 아래와 같이 id를 직접 저장하는 로직이 탄생했다.

조회 쿼리는 실행되지 않는다.

코드에 대한 생각

실제로 하나의 테이블에서 2개의 엔티티 클래스를 작성하는 일은 실무에서 빈번하다.

제일 많이 들은 사례는 어드민 엔티티와 고객 엔티티를 분리하는 일이다.

 

위 코드처럼 동일한 테이블인데, 연동 엔티티와 비즈니스 엔티티를 분리하는 건 괜찮은 분리라고 생각한다.

단일 책임 원칙을 잘 지킨 느낌이랄까 ?!

 

그러나 코드 히스토리에 남은 것처럼 의도가 중요하다고 생각한다.

 

단순히 조회를 한 번하고 연관관계를 매핑 하는게 싫어서, 분리한 것이라면 다른 멋진 대안이 존재한다.

바로 JpaRepository 인터페이스의 getReferenceById 메서드를 사용하면 된다.

 

JpaRepository 인터페이스의 구현체인 SimpleJpaRepository의 getReferenceById를 보면 구현이 아래와 같다.

이 메서드는 JPA 프록시 객체를 반환하는 메서드다.

해당 메서드를 사용하면 DB 조회를 하지 않고, 프록시 객체를 반환해 연관관계 매핑을 조회 없이 진행할 수 있다.

 

실행 쿼리를 살펴보면 조회 쿼리가 안나간걸 볼 수 있다.

getReferenceById를 통한 프록시를 반환 벙삭욿 조회 쿼리 없이 연관 관계 매핑하는 법을 알아봤다.

 

회사 코드를 수정할지는 고민 중에 있다.

이렇게 역할에 따른 엔티티 클래스 분리는 괜찮지 않을까..?! 라는 생각이 문득 들었다. (의도와는 다르지만!)

 

이번 포스팅에서는 실행된 쿼리를 직접 눈으로 보며 검증을 진행했는데, 다음 포스팅에서는 실행된 쿼리를 검증하는 자동화된 테스트에 대해 알아보겠다.

 

 

반응형