JPA 복합키 엔티티에서 단일 필드 조회 문제 해결하기

2025. 4. 1. 21:05·Data

개인프로젝트 개발 중 JPA 복합키를 사용한 엔티티에서 단일 필드로 조회에서 생긴 이슈 입니다 

📍 배경

SMS 발송 문구를 조립하는 기능을 구현하기 위한 세가지 테이블 구조는 이와 같습니다

  • SMS 템플릿 문구 (SMS_TMPLT) 테이블
  • 템플릿 변수 (TMPLT_VAR) 테이블
  • 두 테이블의 관계를 저장하는 SMS_TMPLT_VAR_REL 테이블

관계 테이블 (SMS_TMPLT_VAR_REL) 은 단순히 연관 관계만 저장하면 되므로 별도의 식별자 없이 복합키로 구성했습니다

복합키를 가진 엔티티는 다른 분들의 블로그 글을 참고하여 구현했습니다

 


📍 구현 과정

① 복합키 클래스 구현

@Embeddable
@Getter @Setter
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class SmsTmpltVarRelId implements Serializable {

    private Long smsTmpltId;
    private Long tmpltVarId;

}

저는 객체지향방식에 가까운 `@Embeddable` 방식으로 복합키를 구현했으며 아래의 조건을 충족시켰습니다

  • 식별자로 사용하기 위해 `@EqualsAndHashCode` 어노테이션 작성
  • `Serializable` 인터페이스 구현
  • 기본 생성자가 필수이므로 `@NoArgsConstructor`어노테이션 작성

 

② 복합키를 사용하는 연관 관계 엔티티 구현

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class SmsTmpltVarRel {

    @EmbeddedId
    private SmsTmpltVarRelId smsTmpltVarRelId;

    @MapsId("smsTmpltId")
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "sms_tmplt_id")
    private SmsTemplate smsTemplate;

    @MapsId("tmpltVarId")
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "tmplt_var_id")
    private TemplateVariable templateVariable;

    private SmsTmpltVarRel(SmsTmpltVarRelId smsTmpltVarRelId) {
        this.smsTmpltVarRelId = smsTmpltVarRelId;
    }
    
    ...
    ...

`@MapsId` 어노테이션을 사용하여 복합키 클래스의 필드와 엔티티 관계를 매핑했습니다.

`@MapsId` 어노테이션 내부에는 SmsTmpltVarRelId 클래스에 있는 필드를 그대로 작성하였습니다

 

③ JpaRepository 인터페이스 구현 

public interface JpaSmsTmpltVarRelRepository extends JpaRepository<SmsTmpltVarRel, SmsTmpltVarRelId> {
	List<SmsTemplateVariableRel> findBySmsTmpltId(Long smsTmpltId);
}

JpaRepository를 상속받아서 JpaSmsTmpltVarRelRepository구현할 때 

다른 엔티티 식별자를 Long 타입으로 작성하는게 익숙해져서 

복합키를 가진 SmsTmpltVarRel도 `extends JpaRepository<SmsTmpltVarRel, Long>` 으로 해서 오류를 잡는데 시간을 잡아먹었습니다

이 글을 보시고 복합키를 만드시는 분들이 있으시다면 저처럼 시간낭비하지 마시길 ^^;;ㅎㅎ

 

어쨌든 이렇게 JpaRepository를 구현할 때 처음에는 일반적인 단일 키 엔티티와 같은 방식으로 메서드를 정의했습니다.

복합키 중 하나인 smsTmpltId를 통해 데이터를 가져오기 위해 메서드를 정의했으나 실행 시 다음과 같은 오류가 발생했습니다

 

Caused by: java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.List com.jisutudy.repository.JpaSmsTmpltVarRelRepository.findBySmsTmpltId(java.lang.Long); No property 'smsTmpltId' found for type 'SmsTmpltVarRel'
	at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:107)
	at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:124)
	at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:258)
	at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:95)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lookupQuery(QueryExecutorMethodInterceptor.java:115)
	... 78 more
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property 'smsTmpltId' found for type 'SmsTmpltVarRel'
	at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:94)
	at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:455)
	at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:431)

📍 원인

오류 메시지는 SmsTmpltVarRel 엔티티에서 smsTmpltId 속성을 찾을 수 없다고 알려주고 있습니다.

이는 복합키 클래스 내부의 필드를 직접 참조하려고 했기 때문입니다.

엔티티 자체에는 smsTmpltId가 직접 필드로 존재하지 않고, smsTmpltVarRelId 복합키 객체 내부에 존재하기 때문에 Spring Data JPA가 이를 인식하지 못했습니다.


📍 해결 방법

① 복합키 필드 네이밍 규칙 적용

public interface JpaSmsTmpltVarRelRepository extends JpaRepository<SmsTmpltVarRel, SmsTmpltVarRelId> {
    List<SmsTmpltVarRel> findBySmsTmpltVarRelId_SmsTmpltId(Long smsTmpltId);
}

JPA Repository에서 복합키 내부의 필드에 접근하려면 특별한 네이밍 규칙을 사용해야 합니다

`{복합키필드명}_{복합키내부필드명}` 형식으로 메서드를 정의해야 합니다.

즉, `smsTmpltVarRelId_SmsTmpltId` 와 같이 언더바(_)를 사용하여 복합키 내부 필드에 접근할 수 있습니다.

 

② 엔티티 관계를 활용한 대안적 접근 방식

 List<TemplateVariable> tmpltVarList = smsTemplate.getTmpltVarRelList().stream()
                    .filter(rel -> rel.getTemplateVariable().getVariableType() == entry.getKey())
                    .map(SmsTmpltVarRel::getTemplateVariable)
                    .collect(Collectors.toList());

이 코드는 제가 개인 프로젝트에서 작성한 것으로 복합키 필드 접근 대신 JPA의 객체 양방향연관관계를 활용한 접근법입니다

`smsTemplate.getTmpltVarRelList()`  를 통해

1. 이미 로드된 `SmsTemplate` 엔티티에서 연관된 `SmsTmpltVarRel` 목록을 가져온 후

2. 스트림 API를 사용하여 필요한 조건에 맞는 `TemplateVariable` 객체만을 필터링하고 추출합니다.

 

이 방식은 JPA 복합키 필드에 직접 접근하는 대신, 객체 그래프 탐색을 통해 필요한 데이터를 얻는 객체지향적 접근법으로, 특히 이미 로드된 엔티티에서 데이터를 처리할 때 유용합니다.

 

하지만 주의할 점은 이 방식이 양방향 연관관계를 기반으로 작성된 코드라는 점입니다.

양방향 연관관계는 객체 그래프 탐색이 용이하다는 장점이 있지만 데이터 양이 많아질수록 성능 저하를 유발할 수 있습니다.

특히 대량의 데이터를 로드할 때 N+1 문제를 발생시키거나 메모리 사용량이 증가할 수 있으므로 대규모 프로젝트나 성능이 중요한 상황에서는 단방향 연관관계나 필요에 따라 직접 쿼리를 작성하는 방식을 고려하는 것이 좋습니다.

 


📍 배운점

  • `@Embeddable` 클래스의 내부 필드에 접근하려면 `{복합키필드명}_{복합키내부필드명}` 형식을 사용해야 합니다
  • Repository 인터페이스 구현 시 제네릭에 정확한 엔티티 타입과 ID 타입을 명시해야 합니다

이 경험을 통해 JPA 복합키 사용 시 세부적인 접근 방법에 대해 더 깊이 이해하게 되었습니다

비슷한 상황에 직면한 개발자들에게 도움이 되길 바랍니다

 

 

'Data' 카테고리의 다른 글

금융권에서 오라클 DBMS를 선택하는 이유  (6) 2025.08.01
JPA 연관관계 매핑 vs 값 컬렉션 매핑: 뭘 선택해야 할까  (0) 2025.04.13
JpaRepository를 상속받으면 @Repository 어노테이션을 안달아도 되는 이유  (0) 2025.03.26
데이터 마이그레이션이란?  (2) 2024.09.26
'Data' 카테고리의 다른 글
  • 금융권에서 오라클 DBMS를 선택하는 이유
  • JPA 연관관계 매핑 vs 값 컬렉션 매핑: 뭘 선택해야 할까
  • JpaRepository를 상속받으면 @Repository 어노테이션을 안달아도 되는 이유
  • 데이터 마이그레이션이란?
꾸준히 기록하는 지수
꾸준히 기록하는 지수
서비스 백엔드 개발자가 되기 위해 제 경험들을 하나씩 기록해봅니다
  • 꾸준히 기록하는 지수
    지수블로그
    꾸준히 기록하는 지수
  • 전체
    오늘
    어제
    • 분류 전체보기 (37)
      • Spring (15)
      • JAVA (9)
      • 클라우드_인프라 (4)
      • Data (5)
      • CS (3)
      • 돌아보기 (0)
      • 취업준비 (1)
  • 블로그 메뉴

    • 홈
    • 태그
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    자격증
    스프링테스트
    SCG
    데이터아키텍처준전문가
    AWS
    DASP합격후기
    궁금증
    트러블슈팅
    DASP합격
    스프링
    스프링트랜잭션
    스프링클라우드
    MSA
    SpringCloudGateway
    DASP후기
    스프링부트
    트랜잭션전파
    Spring
    트랜잭션
    DAsP
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
꾸준히 기록하는 지수
JPA 복합키 엔티티에서 단일 필드 조회 문제 해결하기
상단으로

티스토리툴바