Query Method
Query Method는 JPA 에서 제공하는 핵심 기능 중 하나이다.
Repository Interface에 간단한 네이밍 룰을 이용,
별도의 쿼리 작성 없이 데이터베이스와 소통할 수 있게 해준다.
QueryMethod Naming Rule -
1. 메서드 이름은 "findBy" 로 시작.
2. findBy 다음에는 검색 조건으로 사용할 Entity field 이름을 적는다.
이 때, 필드 이름은 대소문자를 구분한다.
3. 필드 이름 다음에는 검색 조건 연산자를 적는다.
연산자는 다음과 같이 사용할 수 있다.
Equals: "findBy[Field]Equals"
NotEquals: "findBy[Field]Not"
LessThan: "findBy[Field]LessThan"
LessThanEquals: "findBy[Field]LessThanEquals"
GreaterThan: "findBy[Field]GreaterThan"
GreaterThanEquals: "findBy[Field]GreaterThanEquals"
Like: "findBy[Field]Like"
NotLike: "findBy[Field]NotLike"
StartingWith: "findBy[Field]StartingWith"
EndingWith: "findBy[Field]EndingWith"
Contains: "findBy[Field]Containing"
NotContains: "findBy[Field]NotContaining"
4. 조건이 여러개인 경우에는 "And"를 사용하여 연결한다.
findBy[firstField]And[secondField]
5. 조건이 여러개이지만, Or 조건으로 연결하고 싶은 경우에는 "Or"를 사용하여 연결한다.
findBy[sirstField]Or[secondField]
위 QueryMethod의 명칭을 보면 알 수 있듯 각 메서드의 역할을 글자만으로 이해할 수 있어 가독성이 좋다.
이렇게 작성된 메서드는 JPA에서 자동으로 해당 필드를 이용한 쿼리를 생성하게 된다.
또한 메서드의 파라미터에 값을 전달하면 이 값들이 쿼리의 조건으로 적용 된다.
QueryMethod를 직접 사용해서 테스트코드를 만들어보자.
간단히 [ 상품명 또는 상품가격을 통해 조회 ] 하는 Query Method를 작성 해보겠다.
Entity -
@Entity
@Table(name="item")
@Getter
@Setter
@ToString
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String itemName; // 상품명
private int price; // 가격
}
Repository -
public interface ItemRepository extends JPARepository<Item, Long> {
// 이름이 같거나 가격이 적다면
List<Item> findByItemNameOrPriceLessThan(String itemName, Integer price);
}
TestCode -
@SpringBootTest
class ItemRepositoryTest {
// 의존성 주입
@Autowired
ItemRepository itemRepository;
@Test
@DisplayName("상품명 OR 가격 조회 테스트")
public void QueryMethodTest() {
// 테스트용
Item item = new Item();
item.setItemName("테스트아이템");
item.setPrice(100);
// DB에 저장
itemRepository.save(item);
// 조회
List<Item> itemList = itemRepository.findByItemNameOrPriceLessThan("테스트아이템", 1000);
for (Item item : itemList) {
System.out.println(item.toString());
}
}
}
테스트 코드를 실행하면
정상적으로 값을 직접 setter로 바인딩한 Item [ItemName = "테스트아이템", price = 100] 이 출력된다.
QueryMethod의 조건을 Or로 하고 price 조건을 LessThan으로 했기 때문에
[ 정상 List ]
ItemName = "TestItem", [ --- false ]
price = 100 [ --- true ]
[ 정상 List ]
ItemName = "테스트아이템", [ --- true ]
price = 100000 [ --- false ]
[ 빈 리스트 ]
ItemName = "TestItem", [ --- false ]
price = 100000 [ --- false ]
위와 같은 결과를 확인할 수 있다.
QueryMethod는 앞서 말한바와 같이 메서드 명만으로 어떤 동작을 하는지 예측이 가능하므로
가독성이 있다는 말을 했지만, 사실 이건 반은 맞고 반을 틀린 말이다.
그 이유는, QueryMethod의 조건이 많을 경우 오히려 메서드명이 너무 길어져 읽기 불편하기 때문이다.
select *
from Item
where itemName = '핫식스'
and price between 100 and 300
and count > 100
- 개발자 : 상품명이 '핫식스'고 가격은 100사이에서 300사이, 개수가 100개 이상인 제품을 찾는 쿼리구나.
매우 간단한 이 쿼리를 QueryMethod로 나타내면
List<Item> findByItemNameAndPriceBetweenAndCountGreaterThan();
- 개발자 : 상품명을 찾고... 가격이 .. A랑 B 사이고 .. 개수가 .. Greater .. 보다 많은 것을 찾는 쿼리..?
이런식으로 알아보기 힘들어진다.
물론 위 쿼리는 and가 2개밖에 없으므로 쿼리가 능한 개발자라면 바로 이해하겠지만 (아직 난 아니다)
조금이라도 더 연산이 많아진다면 저 긴 문장을 해석할 시간에 차라리 MyBatis를 사용할것이다.
그렇기 때문에 꼭 JPA를 쓴다고 Database와 소통할 때 QueryMethod가 만능인게 아니다.
그렇다면 JPA는 긴 조건을 가진 쿼리를 다뤄야할때 불리한가?
전혀 아니다 !
QueryMethod에 한해서는 불리한게 맞지만,
JPA는 QueryMethod 이외에도 다양한 Database와 소통하는 방식을 가지고 있다.
그 중 대표적인게 바로 QueryDSL , @Query 어노테이션 이다.
나 또한 실무에서는 QueryMethod 보다 위 두개, 그 중 특히나 QueryDSL 을 많이 사용했다.
위 두 방법은 다음 포스팅에서 각각 기술하겠다.