Property Expressions

2024. 10. 19. 20:17Spring Boot/Spring Data JPA

Spring Data JPA에서 Property Expressions는 속성 표현식은 이전 예에서 표시된 것처럼 관리되는 엔터티의 직접적인 속성[direct property]만 참조할 수 있습니다. 쿼리 생성 시 이미 파싱된 속성이 특정 엔티티 클래스의 속성인지 확인합니다. 그러나 중첩된 속성을 탐색하여 제약 조건을 정의할 수도 있습니다.


직접 속성(Direct Property)이라는 용어는 Spring Data JPA에서 쿼리를 생성할 때, 객체의 속성이 직접적으로 참조되는 경우를 의미합니다. 쉽게 말해, 특정 클래스의 속성 중에서 해당 클래스의 인스턴스에서 바로 접근할 수 있는 속성을 가리킵니다.

예시로 설명

import javax.persistence.*;

@Entity
public class Container {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String qCode;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "code_id")
    private Code q;

    // Constructors, getters, setters, etc.
    public Container() {}

    public Container(String qCode, Code q) {
        this.qCode = qCode;
        this.q = q;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getqCode() {
        return qCode;
    }

    public void setqCode(String qCode) {
        this.qCode = qCode;
    }

    public Code getQ() {
        return q;
    }

    public void setQ(Code q) {
        this.q = q;
    }
}

@Entity
public class Code {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String code;

    // Constructors, getters, setters, etc.
    public Code() {}

    public Code(String code) {
        this.code = code;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}
  1. Direct Property:
    • Container 클래스에서 qCode는 직접 속성입니다. Container 인스턴스에서 qCode에 직접적으로 접근할 수 있기 때문입니다.
      record Container(String qCode, Code q) {}
      여기서 qCodeContainer 클래스의 직접 속성이므로, Container 객체를 통해 바로 접근할 수 있습니다.
  2. Indirect Property:
    • 반면, Code 클래스의 code 속성은 간접 속성입니다. codeContainer 클래스의 q 속성을 통해 접근해야 하므로, 직접적으로는 사용할 수 없습니다.
      record Code(String code) {}
      이 경우, Container 객체의 q 속성을 통해 Code 객체에 접근하고, 그 안의 code 속성에 접근해야 합니다:
      Container container = new Container("example", new Code("12345"));
      String codeValue = container.q().code(); // 간접 접근

Spring Data JPA에서의 적용

  • Spring Data JPA는 쿼리를 생성할 때 직접 속성을 우선적으로 고려합니다. 즉, 메서드 이름에서 직접 속성이 먼저 매칭되는 경우, 해당 속성을 사용하여 쿼리를 생성합니다.
  • 위의 예시처럼 Container 클래스의 qCode가 먼저 처리되므로, 같은 이름을 가진 Code 클래스의 code 속성은 무시될 수 있습니다.

요약

  • 직접 속성은 객체의 속성 중에서 인스턴스에서 바로 접근 가능한 속성을 의미합니다.
  • 간접 속성은 다른 속성을 통해 접근해야 하는 속성으로, Spring Data JPA에서 쿼리를 작성할 때 주의해야 할 요소입니다.

1. 기본 개념

Property Expressions는 관리되는 엔티티의 속성을 참조하는 표현식입니다. 예를 들어, 다음과 같은 메서드 시그니처를 고려해 봅시다:

List<Person> findByAddressZipCode(ZipCode zipCode);

여기서 PersonAddress라는 속성을 가지고 있으며, AddressZipCode를 가지고 있다고 가정합니다. 이 메서드는 address.zipCode라는 속성 탐색을 생성합니다.

2. 속성 탐색 과정

쿼리를 생성하는 과정에서, Spring Data JPA는 메서드 이름을 분석하여 적절한 속성을 찾습니다. 다음과 같은 단계로 진행됩니다:

  1. 전체 속성 이름 해석: AddressZipCode를 전체 속성으로 해석합니다. 이 경우, 소문자로 변환하여 도메인 클래스에서 해당 속성이 존재하는지 확인합니다.
  2. 속성 분할: 만약 첫 번째 단계에서 해당 속성이 존재하지 않는다면, Camel Case(대문자로 시작하는 단어)로 나누어 속성을 찾습니다. 예를 들어 AddressZipCode로 나누고, 각각의 속성을 확인합니다.
  3. 속성 일치 확인: 만약 Address라는 속성이 발견되면, 다음 단계로 내려가서 남은 부분(ZipCode)을 계속 탐색합니다. 이 과정을 반복하면서 올바른 경로를 찾습니다.

3. Path의 모호성 문제

이 알고리즘은 대부분의 경우 잘 작동하지만, 특정 상황에서는 잘못된 속성을 선택할 수 있습니다. 예를 들어, Person 클래스에 addressZip라는 속성이 있을 경우, 알고리즘은 첫 번째 분할 단계에서 addressZip 속성과 일치하여 잘못된 속성을 선택할 수 있습니다. 이 경우 addressZip 속성은 code 속성을 가지지 않기 때문에 오류가 발생할 수 있습니다.

이러한 모호성을 해결하기 위해, 메서드 이름에 _를 사용하여 탐색 포인트를 수동으로 정의할 수 있습니다. 따라서 메서드는 다음과 같이 작성할 수 있습니다:

List<Person> findByAddress_ZipCode(ZipCode zipCode);

4. 주의사항

  • 언더스코어(_) 사용: 언더스코어는 예약 문자로 취급되므로, Java의 표준 네이밍 규칙을 따르는 것이 좋습니다. 즉, 속성 이름에는 언더스코어를 사용하지 않고 Camel Case를 사용하는 것이 바람직합니다.
  • 언더스코어로 시작하는 필드 이름: 필드 이름이 언더스코어로 시작할 수 있습니다. 예를 들어, String _name과 같이 사용합니다. 이 경우, 언더스코어를 보존하고 중첩 경로를 분할할 때는 user__name과 같이 두 개의 언더스코어를 사용해야 합니다.
  • 모든 대문자로 구성된 필드 이름: 모든 문자가 대문자인 필드 이름도 사용할 수 있습니다. 이 경우 중첩 경로를 사용할 때는 USER_name과 같이 분할해야 합니다.
  • 두 번째 대문자 필드 이름: 소문자로 시작하고 그 다음에 대문자가 오는 필드 이름(예: String qCode)은 두 개의 대문자로 시작해야 해석됩니다. 이 경우 QCode와 같이 작성해야 합니다. 이로 인해 경로 모호성이 발생할 수 있습니다.

5. Path 모호성 예시

Path 모호성(Path Ambiguity) 문제는 Spring Data JPA에서 메서드 이름으로 쿼리를 작성할 때, 어떤 속성에 접근할지 혼동이 생기는 경우를 말합니다. 아래에 이 개념을 상세하게 설명하고 예시를 들어보겠습니다.

예시 설명

record Container(String qCode, Code q) {}
record Code(String code) {}
  1. Container 클래스:
    • 이 클래스는 두 개의 속성을 가집니다: qCode (문자열 타입)와 q (Code 타입).
    • qCode 타입의 객체이며, 이 객체는 code라는 문자열 속성을 가집니다.
  2. Path 모호성 발생:
    • 쿼리를 작성할 때, 예를 들어 findByQCode와 같은 메서드를 만들면, Spring Data JPA는 qCode 속성과 q 속성의 code 속성을 모두 고려합니다.
    • 하지만, Spring Data JPA의 규칙에 따라 직접 속성을 먼저 검색하기 때문에, findByQCodeContainer 클래스의 qCode 속성을 선택합니다.
  3. 모호성의 원인:
    • 만약 Container 클래스에서 qCode와 같은 이름을 가진 속성이 있고, 동시에 q 속성 내에 code라는 이름의 속성이 존재하면, Spring Data는 qCode를 직접 일치로 처리합니다.
    • 따라서 q 속성의 code 속성은 쿼리에서 고려되지 않고, 이로 인해 사용자가 원하는 결과를 얻지 못할 수 있습니다.

해결 방법: 언더스코어 (_) 사용

이런 모호성을 해결하기 위해, 속성 이름에 언더스코어를 사용하여 쿼리를 작성할 수 있습니다. 예를 들어:

List<Container> findByQ_Code(String code);
  • 위와 같이 _ 표기법을 사용하면, Spring Data JPA는 Q_CodeCode 객체 내의 code 속성을 나타내는 것임을 이해하고 해당 속성을 검색하게 됩니다.
  • 즉, Q_Codeq 속성 내의 code를 명확히 지정하여, Spring이 잘못된 속성을 선택하지 않도록 도와줍니다.

요약

  • Path 모호성은 두 개 이상의 속성이 동일한 이름 또는 유사한 구조를 가질 때 발생하는 문제입니다.
  • 직접 속성이 우선적으로 처리되기 때문에, 간접 속성이 무시되는 경우가 생깁니다.
  • 이를 방지하기 위해 언더스코어(_) 표기법을 사용하여 명확하게 속성을 구분할 수 있습니다.

 

Property Expressions는 Spring Data JPA에서 유용한 기능으로, 복잡한 쿼리를 작성할 때 필드 탐색을 용이하게 합니다. 그러나 특정 속성을 잘못 선택하거나 경로의 모호성이 발생할 수 있으므로, 이를 피하기 위해 적절한 네이밍 규칙을 따르고 _ 문자를 활용해야 합니다.

'Spring Boot > Spring Data JPA' 카테고리의 다른 글

Streaming Query Results, Asynchronous Query Results  (0) 2024.10.20
Repository Methods Returning Collections or Iterables  (0) 2024.10.19
Reserved Method Names  (1) 2024.10.19
Query Creation  (0) 2024.10.19
Query Lookup Strategies  (0) 2024.10.19