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

 

Spring Data JPA - Reference Documentation

Example 109. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del

docs.spring.io


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
    Equals
    findByFirstname
    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
    Null
    findByAge(Is)Null … where x.age is null
    IsNotNull
    NotNull
    findByAge(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
반응형