하루살이 개발자

[Spring JPA] Query Method 본문

Backend/Spring

[Spring JPA] Query Method

하루살이 2022. 2. 15. 16:08

 

Query Method

- JPA(Java Persistence API)란 자바에서 사용하고 있는 ORM의 표준으로 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑해주는 일을 한다.
- 스프링 데이터 JPA는 메소드 이름으로 쿼리 생성을 하는 쿼리 메소드 기능을 제공한다.

- 쿼리메서드는 메서드의 이름을 분석해서 JPQL쿼리를 실행한다!
- 쿼리 메서드를 활용하면 쉽게 쿼리문을 만들어 사용할 수 있다.

 

Query Method 사용

Query method는 JpaRepository를 상속하는 것 만으로도 Jpa의 method들을 사용할 수 있다.
JpaRepository<> 에서 괄호에는 첫번째에는 Jpa로 사용할 entity(class), 두번째는 해당 class의 pk타입이다.

public interface UserRepository extends JpaRepository<User, Integer>

 

Select Query

  • 아래의 method들은 모두 select query의 일을 하는 method들이다.
  • 이름은 find(get,ready,,,,) By가 핵심이며 두 문자 뒤에 구분할 수 있는 변수명을 넣어 구분하며 만들어주면 된다. 그러면 자동으로 Select Qeury Method가 만들어진다.
  • 아래의 findBy, getBy, readBy, queryBy, searchBy, streamBy, find (Entity 명) By 는 모두 Select의 일을 하는 keyword이며 개발자는 이중에서 자신이 가독성이 높다고 생각되는 것을 이용하면 된다.
    User findByEmail(String email);
    User getByEmail(String email);
    User readByEmail(String email);
    User queryByEmail(String email);
    User searchByEmail(String email);
    User streamByEmail(String email);
    User findUserByEmail(String email);

* Return type은 List, Set, Object등의 여러 타입으로 할 수 있으며 JPA가 데이터를 읽어오고 return type에 맞춰서 데이터를 return해준다.
단 데이터가 여러개인데 User와 같이 단일 객체로 return하면 오류가 발생한다.

 

First, Top

  • First, Top은 둘 다 데이터에서 출력할 데이터의 수를 정해서 리턴하게 하는 keyword이다.
  • 기본적으로 keyword뒤에 숫자를 붙이지 않으면 가장 상위 데이터 하나가 리턴되며(Default = 1) 이름 뒤에 출력할 데이터의 수를 붙이면 해당 수만큼 리턴한다.
    List<User> findFirst1ByName(String name);	// 상위 1개의 데이터 return
    List<User> findTop2ByName(String name);   // 상위 2개의 데이터 return
    List<User> findLast1ByName(String name); // Last filter는 없다

 

And, Or

Query문에서 and, or을 사용하고 싶은 경우 method안에 And or을 넣어준다

    List<User> findByNameAndEmail(String name, String email);
    List<User> findByNameOrEmail(String name, String email);

 

After, Before, GreaterThan, LessThan, Between

  • After, Before, GreaterThan, LessThan은 값 비교를 해주는 keyword이다.
    After, GreaterThan은 특정 날짜/값 이후(또는 큰것)에 발생한 것을 조회하게 해주는 keyword이다.
    Before, LessThan은 특정 날짜/값 이전(또는 작은것)을 조회하게 해주는 keyword이다.
  • After, GreaterThan과 Before, LessThan은 서로 같은 기능을 하는 필터지만 가독성을 위해 After, Before은 날짜에만 사용하는 것을 추천한다.
  • 또한 조건을 걸다보면 초과, 미만 뿐만 아니라 이상, 이하의 조건도 필요할 것이다. 그럴 경우에는 GreaterThanEqual과 같이 Equal을 붙여주면 된다.
  • ~이상 ~이하의 의미를 갖는 Between keyword도 존재한다. Between은 parameter를 2개를 받으며 Between은 위의 After, Before, GreaterThan, LessThan과 다르게 해당 값들도 포함한다.(이상, 이하를 의미한다.)
   List<User> findByCreatedAtAfter (LocalDateTime lastDay);  
   // CreatedAt이 lastDay이후인 데이터들 return (yesterDay미포함)
   List<User> findByIdAfter(Long id);  
   // input id보다 큰 id를 가진 데이터들을 return (id 미포함)
    
    List<User> findByCreatedAtGreaterThan (LocalDateTime yesterday);  
    // CreatedAt이 lastDay이후인 데이터들 return (yesterDay미포함)
    List<User> findByCreatedAtGreaterThanEqual (LocalDateTime yesterday); 
    // CreatedAt이 lastDay이후인 데이터들 return (yesterDay포함)


    List<User> findByCreatedAtBetween(LocalDateTime yesterday, LocalDateTime tomorrow); 
    // CreatedAt이 lastDay와 tomorrow사이 값인 데이터들 return (yesterDay, tomorrow포함)
    List<User> findByIdBetween(Long id1, Long id2); 
    // id가 id1이상, id2이하인 데이터들 return

 

* After, Before, GreaterThan, LessThan은 초과 미만을 의미하며 Between은 이상, 이하를 의미하는 것을 헷갈리면 안된다!!!

 

is(Not)Empty, is(Not)Null

  • isNull은 해당 값에 Null값이 있는지 체크하는 keyword이다.
  • NotEmpty는 String과 같은 문자열이 비어있는지 체크가 아닌 Collection type의 변수가 not empty(비어있는지)를 체크한다.
    List<User> findByIdIsNotNull();  // Id값에 Null값이 없는지?
    List<User> findByAddressIsNotEmpty();

 

In

  • In절은 query문에서 in절이기 때문에 parameter로 iterater type인 list가 들어가게 된다. generic type이 들어가야하는 <>에는 검색하는 column의 data type을 넣는다.
  • 일반적으로 in절은 다른 query의 결과값을 다시 query에 넣어야 할 때 사용한다!
  • in절을 사용할 때는 과부하 걸리는 것을 예방하기 위해서 다른 query문의 결과로 얼마나 많은 데이터들이 나올지 사전에 검토를 하고 사용하는게 좋다.
 List<User> findByNameIn(List<String> name);

 

StartingWith/EndingWith/Contains

  • 해당 keyword들은 문자열에 사용하며 해당 문자열로 시작하는지, 끝나는지, 포함하는지를 filtering할 때 사용한다.
  • contains("rti")와 like("%rti%")는 같은 것이다
    List<User> findByNameStartingWith(String name);
    List<User> findByNameEndingWith(String name);
    List<User> findByNameContains(String name);
    List<User> findByNameLike(String name);

 

Is, Equals

  • Is는 해당 값을 가진 data를 찾는 keyword로 Is, Equals, 또는 아무런 키워드를 넣지 않으면 Is로 보게된다.
  • 아래 3개의 method는 모두 parameter의 name과 동등한 이름의 데이터들을 출력하는 method이다.
    Set<User> findUserByNameIs(String name);
    Set<User> findUserByName(String name);
    Set<User> findUserByNameEquals(String name);

 

Sorting

  • Sorting은 조건에 따라 데이터의 정렬을 해주는 Keyword이다.
  • Desc/ Asc로 정렬한다.
  • 여러개의 조건으로 find하는 경우는 And를 사용하였으나 정렬 조건으로 여러개의 값을 사용하는 경우는 And를 사용하지 않고 조건을 이어서 붙인다.
  • Sort method를 사용하여 정렬시킬 수도 있다.
    List<User> findTop1ByNameOrderByIdDesc(String name); 
    // Id로 내림차순으로 정렬 후 입력 name과 같은 것의 맨 위의 있는 값을 뽑아온다.
    
    List<User> findFirst2ByNameOrderByIdDescEmailAsc(String name);
    // 여러개의 조건으로 find하는 경우는 And를 사용하였으나 정렬 조건으로 여러개의 값을 사용하는 경우는 And를 사용하지 않고 조건을 이어서 붙인다.

    List<User> findFirstByName(String name, Sort sort);

 

* Sort Parameter를 사용하여 정렬하는 경우

  • Sort Parameter를 사용하지 않는 방법으로 Sorting을 하면 조건이 많아질수록 메소드의 이름 길이도 길어지며 코드의 가독성에도 그리 좋지 않다.
  • 또한 조건에 따라 여러 method를 만들어야하는데 Sort method를 사용할 경우 하나의 method로 여러 sort조건을 줘서 사용할 수 있다. 그래서 아래와 같이 Sorting Method를 사용하는 것을 추천한다.
  • 하지만 반대로 같은 Sorting방식으로 사용되는 경우가 많은 경우 위의 Keyword를 사용한 method를 사용하는게 가독성 측면에서 더 좋을 수 있다.
  • 개발자는 같은 결과를 내더라도 여러 코딩 방식이 있다는 것을 인지하고 어떤 것이 가독성이 더 좋을지 생각하며 코딩해야한다.

 

쿼리 메서드 필터 조건

KeywordSampleJPQL snippet

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 ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection 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)

 

 

Reference

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods

https://velog.io/@seongwon97/Spring-Boot-Query-Method