https://sundaland.tistory.com/408
[ ▶ Property Expressions ]
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;
}
}
△ Direct Property
Container 클래스에서 qCode는 직접 속성이다. Container 인스턴스에서 qCode에 직접적으로 접근할 수 있기 때문이다.
record Container(String qCode, Code q) {}
여기서 qCode는 Container 클래스의 직접 속성이므로, Container 객체를 통해 바로 접근할 수 있다.
△ Indirect Property
반면, Code 클래스의 code 속성은 간접 속성이다. code는 Container 클래스의 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에서 쿼리를 작성할 때 주의해야 할 요소다.
[ ▷ 기본 개념 ]
Property Expressions는 관리되는 엔티티의 속성을 참조하는 표현식이다. 예를 들어, 다음과 같은 메서드 시그니처를 고려해 본다.
List<Person> findByAddressZipCode(ZipCode zipCode);
여기서 Person은 Address라는 속성을 가지고 있으며, Address는 ZipCode를 가지고 있다고 가정한다. 이 메서드는 address.zipCode라는 속성 탐색을 생성한다.
[ ▷ 속성 탐색 과정 ]
쿼리를 생성하는 과정에서, Spring Data JPA는 메서드 이름을 분석하여 적절한 속성을 찾는다.
- 전체 속성 이름 해석: AddressZipCode를 전체 속성으로 해석합니다. 이 경우, 소문자로 변환하여 도메인 클래스에서 해당 속성이 존재하는지 확인한다.
- 속성 분할: 만약 첫 번째 단계에서 해당 속성이 존재하지 않는다면, Camel Case(대문자로 시작하는 단어)로 나누어 속성을 찾는다. 예를 들어 Address와 ZipCode로 나누고, 각각의 속성을 확인한다.
- 속성 일치 확인: 만약 Address라는 속성이 발견되면, 다음 단계로 내려가서 남은 부분(ZipCode)을 계속 탐색한다. 이 과정을 반복하면서 올바른 경로를 찾는다.
[ ▷ Path의 모호성 문제 ]
이 알고리즘은 대부분의 경우 잘 작동하지만, 특정 상황에서는 잘못된 속성을 선택할 수 있다. 예를 들어, Person 클래스에 addressZip라는 속성이 있을 경우, 알고리즘은 첫 번째 분할 단계에서 addressZip 속성과 일치하여 잘못된 속성을 선택할 수 있다. 이 경우 addressZip 속성은 code 속성을 가지지 않기 때문에 오류가 발생할 수 있다.
이러한 모호성을 해결하기 위해, 메서드 이름에 _를 사용하여 탐색 포인트를 수동으로 정의할 수 있다. 따라서 메서드는 다음과 같이 작성할 수 있다.
List<Person> findByAddress_ZipCode(ZipCode zipCode);
[ ▷ 주의사항 ]
- 언더스코어(_) 사용: 언더스코어는 예약 문자로 취급되므로, Java의 표준 네이밍 규칙을 따르는 것이 좋다. 즉, 속성 이름에는 언더스코어를 사용하지 않고 Camel Case를 사용하는 것이 바람직하다.
- 언더스코어로 시작하는 필드 이름: 필드 이름이 언더스코어로 시작할 수 있다. 예를 들어, String _name과 같이 사용한다. 이 경우, 언더스코어를 보존하고 중첩 경로를 분할할 때는 user__name과 같이 두 개의 언더스코어를 사용해야 한다.
- 모든 대문자로 구성된 필드 이름: 모든 문자가 대문자인 필드 이름도 사용할 수 있다. 이 경우 중첩 경로를 사용할 때는 USER_name과 같이 분할해야 한다.
- 두 번째 대문자 필드 이름: 소문자로 시작하고 그 다음에 대문자가 오는 필드 이름(String qCode)은 두 개의 대문자로 시작해야 해석된다. 이 경우 QCode와 같이 작성해야 한다. 이로 인해 경로 모호성이 발생할 수 있다.
[ ▷ Path 모호성 예시 ]
Path 모호성(Path Ambiguity) 문제는 Spring Data JPA에서 메서드 이름으로 쿼리를 작성할 때, 어떤 속성에 접근할지 혼동이 생기는 경우를 말한다.
record Container(String qCode, Code q) {}
record Code(String code) {}
△ Container 클래스
- 이 클래스는 두 개의 속성을 가진다. qCode (문자열 타입)와 q (Code 타입).
- q는 Code 타입의 객체이며, 이 객체는 code라는 문자열 속성을 가진다.
△ Path 모호성 발생
- 쿼리를 작성할 때, 예를 들어 findByQCode와 같은 메서드를 만들면, Spring Data JPA는 qCode 속성과 q 속성의 code 속성을 모두 고려한다.
- 하지만, Spring Data JPA의 규칙에 따라 직접 속성을 먼저 검색하기 때문에, findByQCode는 Container 클래스의 qCode 속성을 선택한다.
△ 모호성의 원인
- 만약 Container 클래스에서 qCode와 같은 이름을 가진 속성이 있고, 동시에 q 속성 내에 code라는 이름의 속성이 존재하면, Spring Data는 qCode를 직접 일치로 처리한다.
- 따라서 q 속성의 code 속성은 쿼리에서 고려되지 않고, 이로 인해 사용자가 원하는 결과를 얻지 못할 수 있다.
[ ▷ 해결 방법: 언더스코어 (_) 사용 ]
이런 모호성을 해결하기 위해, 속성 이름에 언더스코어를 사용하여 쿼리를 작성할 수 있다.
List<Container> findByQ_Code(String code);
- 위와 같이 _ 표기법을 사용하면, Spring Data JPA는 Q_Code가 Code 객체 내의 code 속성을 나타내는 것임을 이해하고 해당 속성을 검색하게 된다.
- 즉, Q_Code는 q 속성 내의 code를 명확히 지정하여, Spring이 잘못된 속성을 선택하지 않도록 도와준다.
[ ▷ 요약 ]
- Path 모호성은 두 개 이상의 속성이 동일한 이름 또는 유사한 구조를 가질 때 발생하는 문제이다.
- 직접 속성이 우선적으로 처리되기 때문에, 간접 속성이 무시되는 경우가 생긴다.
- 이를 방지하기 위해 언더스코어(_) 표기법을 사용하여 명확하게 속성을 구분할 수 있다.
Property Expressions는 Spring Data JPA에서 유용한 기능으로, 복잡한 쿼리를 작성할 때 필드 탐색을 용이하게 한다. 그러나 특정 속성을 잘못 선택하거나 경로의 모호성이 발생할 수 있으므로, 이를 피하기 위해 적절한 네이밍 규칙을 따르고 _ 문자를 활용해야 한다.
'Spring Boot > Spring Data JPA' 카테고리의 다른 글
Slice (0) | 2024.10.24 |
---|---|
Repository Methods Returning Collections or Iterables (0) | 2024.10.24 |
Reserved Method Names (0) | 2024.10.22 |
Query Creation (0) | 2024.10.22 |
Query Lookup Strategies (0) | 2024.10.22 |