Tech/JPA
JpaRepository Query 작성
주지민
2021. 9. 14. 00:49
반응형
본 내용은 Spring Data JPA 2.5.2 버전 기준으로 작성되었습니다
https://docs.spring.io/spring-data/jpa/docs/2.5.2/reference/html/#jpa.repositories
1. JpaRepository Query
- Spring Data JPA는 메서드 이름만으로 Query를 만들어내는 기능을 제공한다
- SELECT, 조회 기능만 해당
- 메서드명 작성 규칙
find + [Distinct / Entity name]+ By + [Column] + [Option]
- Entity Name에 해당하는 부분은 어떤 문자가 들어가도 상관없다
- 리턴 타입 ( Collection Type )
- Page<T>
- Slice<T>
- List<T>
- Option 종류
Keyword Sample 실제 JPQL 부분 Distinct findDistinctByLastnameAndFirstname select distinct … where x.lastname = ?1 and x.firstname = ?2 And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2 Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2 Is
EqualsfindByFirstname
findByFirstnameIs
findByFirstnameEquals… where x.firstname = ?1 Between findByStartDateBetween … where x.startDate between ?1 and ?2 LessThan findByAgeLessThan … where x.age < ?1 LessThanEqual findByAgeLessThanEqual … where x.age <= ?1 GreaterThan findByAgeGreaterThan … where x.age > ?1 GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1 After findByStartDateAfter … where x.startDate > ?1 Before findByStartDateBefore … where x.startDate < ?1 IsNull
NullfindByAge(Is)Null … where x.age is null IsNotNull
NotNullfindByAge(Is)NotNull … where x.age not null Like findByFirstnameLike … where x.firstname like ?1 NotLike findByFirstnameNotLike … where x.firstname not like ?1 StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %) EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %) Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %) OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc Not findByLastnameNot … where x.lastname <> ?1 In findByAgeIn(Collection<Age> ages) … where x.age in ?1 NotIn findByAgeNotIn(Collection<Age> ages) … where x.age not in ?1 True findByActiveTrue() … where x.active = true False findByActiveFalse() … where x.active = false IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstname) = UPPER(?1) - 유의사항
- 엔티티 속성은 대소문자를 구분한다. ITEM, item은 다르다
- JPQL 키워드는 대소문자를 구분하지 않는다. DISTINCT와 distinct는 같다
- JPQL을 사용할 경우 반드시 별칭을 작성해야한다
2. 코드 예제
- 옵션을 한개씩 직접 예제를 통해서 알아보겠습니다
2-1. DISTINCT
- 사용법
- 메서드 명만으로
List<RelationItem> findDistinctByName(String name);
- JPQL
@Query(value = "SELECT DISTINCT i FROM RelationItem i WHERE i.name = :name") List<RelationItem> findDistinctByNameWithJPQL(String name);
- Native Query
@Query(nativeQuery = true, value = "SELECT DISTINCT * FROM relation_item WHERE name = :name") List<RelationItem> findDistinctByNameWithNativeQuery(String name);
- 메서드 명만으로
- 실제 쿼리
select distinct relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_ from relation_item relationit0_ where relationit0_.name=?
2-2. AND
- 사용법
- 메서드 명만으로
List<RelationItem> findByNameAndPrice(String name, Long price);
- JPQL
@Query(value = "SELECT i FROM RelationItem i WHERE i.name = :name AND i.price = :price") List<RelationItem> findByNameAndPriceWithJPQL(String name, Long price);
- Native Query
@Query(nativeQuery = true, value = "SELECT * FROM relation_item WHERE name = :name AND price = :price") List<RelationItem> findByNameAndPriceWithNativeQuery(String name, Long price);
- 메서드 명만으로
- 실제 쿼리
select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_ from relation_item relationit0_ where relationit0_.name=? and relationit0_.price=?
2-3. OR
- 사용법
- 메서드명만으로
List<RelationItem> findByNameOrPrice(String name, Long price);
- JPQL
@Query(value = "SELECT i FROM RelationItem i WHERE i.name = :name OR i.price = :price") List<RelationItem> findByNameOrPriceWithJPQL(String name, Long price);
- Native Query
@Query(nativeQuery = true, value = "SELECT * FROM relation_item WHERE name = :name OR price = :price") List<RelationItem> findByNameOrPriceWithNativeQuery(String name, Long price);
- 메서드명만으로
- 실제 쿼리
select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_ from relation_item relationit0_ where relationit0_.name=? or relationit0_.price=?
2-4. IS, EQUALS
- 사용법
List<RelationItem> findByName(String name); List<RelationItem> findByNameIs(String name); List<RelationItem> findByNameEquals(String name);
- 실제 쿼리 ( 다 같은 쿼리가 생성 )
select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_ from relation_item relationit0_ where relationit0_.name=?
2-5. BETEEN
- 사용법
List<RelationItem> findByPriceBetween(Long start, Long end);
- 실제 쿼리
select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_ from relation_item relationit0_ where relationit0_.price between ? and ?
2-6. LESS THAN, LESS THAN EQUAL
- GreaterThan, GreaterThanEqual 은 동작이 정반대이다
- 사용법
// LESS THAN, GREATER THAN List<RelationItem> findByPriceLessThan(Long price); List<RelationItem> findByPriceLessThanEqual(Long price); List<RelationItem> findByPriceGreaterThan(Long price); List<RelationItem> findByPriceGreaterThanEqual(Long price);
- 실제 쿼리
// LESS THAN select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_ from relation_item relationit0_ where relationit0_.price<? // LESS THAN EQUAL select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_ from relation_item relationit0_ where relationit0_.price<=?
2-7. AFTER, BEFORE
- DATE type을 비교할 때 용이하다
- 하지만 비교가 가능한 숫자형 타입을 넣어도 무관한듯..
- 사용법
List<RelationItem> findByRegisterTimeAfter(LocalDateTime registerTime);
- 실제 쿼리
select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_, relationit0_.register_time as register4_7_, relationit0_.update_time as update_t5_7_ from relation_item relationit0_ where relationit0_.register_time>?
2-8. NULL, NOT NULL
- 사용법
// NULL, NOT NULL List<RelationItem> findByUpdateTimeNull(); List<RelationItem> findByUpdateTimeNotNull();
- 실제 쿼리
// NULL select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_, relationit0_.register_time as register4_7_, relationit0_.update_time as update_t5_7_ from relation_item relationit0_ where relationit0_.update_time is null // NOT NULL select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_, relationit0_.register_time as register4_7_, relationit0_.update_time as update_t5_7_ from relation_item relationit0_ where relationit0_.update_time is not null
2-9. LIKE, NOT LIKE
- 정확하게 파라미터로 들어온 인자와 같은지만 판단한다 ( 정확한 일치 여부! )
- 추가적으로 생성되는 쿼리를 살펴보면 LIKE외에도 escape 키워드가 발생한다
- escape는 쿼리에서 사용되는 패턴 일치 문자를 실제 인자로 들어온 문자와 구분하기 위한 키워드이다
- 즉 검색하고자 하는 텍스트가 '주지민%'라고하면 쿼리 안에서는 %를 패턴 일치문자로 구분하기 때문에 그대로 보낼 경우 잘못된 결과를 낼 수 있다
- 이때 escape 키워드를 사용해 LIKE '%주지민$%%' ESCAPE '$' 으로 사용해 패턴 일치 문자와 실제 쿼리 상에서 사용할 파라미터를 구분할 수 있다
- JPARepository는 기본적으로 escape가 '\'로 설정되어있다
- "테스트1%% 아이템"을 검색하기위해서는 findByNameLike("테스트1\\%\\% 아이템") 으로 검색하면 된다
- 사용법
// LIKE, NOT LIKE List<RelationItem> findByNameLike(String text); List<RelationItem> findByNameNotLike(String text);
- 실제 쿼리
// LIKE select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_, relationit0_.register_time as register4_7_, relationit0_.update_time as update_t5_7_ from relation_item relationit0_ where relationit0_.name like ? escape ? # binding parameter [1] as [VARCHAR] - [테스트1] # binding parameter [2] as [CHAR] - [\]
2-10. StartingWith, EndingWith, Containing
- 메서드명에서도 유추해볼 수 있듯이 시작 값이 일치하는 경우, 끝나는 값이 일치하는 경우, 해당 텍스트가 일치하는 구간이 있는 경우에 대한 조회 기능이다
- 사용법
// StartingWith, EndingWith, Containing List<RelationItem> findByNameStartingWith(String start); List<RelationItem> findByNameEndingWith(String end); List<RelationItem> findByNameContaining(String text);
- 실제 쿼리
// StartingWith select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_, relationit0_.register_time as register4_7_, relationit0_.update_time as update_t5_7_ from relation_item relationit0_ where relationit0_.name like ? escape ? # binding parameter [1] as [VARCHAR] - [테스트1%] # binding parameter [2] as [CHAR] - [\]
- LIKE와 쿼리 차이는 없는 것을 확인할 수 있다
- 하지만 바인딩된 첫번째 파라미터를 보면 테스트1로 검색했음에도 뒤에 '%' 패턴 일치 문자가 추가적으로 붙은 것을 확인 할 수 있다
- 예상하시겠지만.. EndingWith은 파라미터 앞에 '%', Containing은 파라미터 앞뒤로 '%' 문자가 붙어 패턴 일치하는 결과를 조회합니다
2-11. ORDER BY
- ORDER BY {속성명} [asc/desc]
- 속성명 뒤에 [asc/desc]를 명시안해주면 기본적으로 asc 상태로 조회됩니다
- 사용법
// ORDER BY List<RelationItem> findByOrderByRegisterTimeDesc();
- 실제 쿼리
// ORDER BY ... DESC select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_, relationit0_.register_time as register4_7_, relationit0_.update_time as update_t5_7_ from relation_item relationit0_ order by relationit0_.register_time desc
2-12. NOT
- <>, != 즉 같지않다를 표현할 때 사용
- 사용법
// NOT List<RelationItem> findByNameNot(String name);
- 실제 쿼리
// NOT select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_, relationit0_.register_time as register4_7_, relationit0_.update_time as update_t5_7_ from relation_item relationit0_ where relationit0_.name<>?
2-13. IN, NOT IN
- 사용법
// IN, NOT IN List<RelationItem> findByPriceIn(Collection<Long> prices); List<RelationItem> findByPriceNotIn(Collection<Long> prices);
- 실제 쿼리
// IN select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_, relationit0_.register_time as register4_7_, relationit0_.update_time as update_t5_7_ from relation_item relationit0_ where relationit0_.price in ( ? , ? )
2-14. IGNORE CASE
- MySQL UPPER, LOWER 함수가 있다
- UPPER: 대문자로 변환
- LOWER: 소문자로 변환
- 사용법
// IGNORE CASE List<RelationItem> findByNameIgnoreCase(String name);
- 실제 쿼리
// IGNORE CASE select relationit0_.id as id1_7_, relationit0_.name as name2_7_, relationit0_.price as price3_7_, relationit0_.register_time as register4_7_, relationit0_.update_time as update_t5_7_ from relation_item relationit0_ where upper(relationit0_.name)=upper(?)
728x90
반응형