JPA 쿼리메소드(QueryMethod)

이종윤·2022년 2월 4일
1

Spring JPA

목록 보기
5/23

🤔 실제로 서비스에서는 WHERE 조건이 복잡할탠데...어떻하지?

QueryMethod 활용

JPA 사용할 때 QueryMethod가 많은 해택을 받는다. 레파지토리에 네이밍베이스로 선언만 하면 Query를 불러올수있다. 하지만 먼저 QueryMethod가 어떻게 만들어 지고 작동하는지 알아야한다.

TYPE


✅ 먼저 UserRepository에 메서드를 만들고

public interface UserRepository extends JpaRepository<User, Long> {

    User findByName(String name); //이름을 통해 유저 가져오는 메서드.
}

✅ Test 해보자

    @Test
    void select() {
        System.out.println(userRepository.findByName("martin"));
    }

😈 에러가 나는 것은 당연하다 검색한 martin은 2명이기 때문이다.

✅ 리스트 타입으로 바꾸고 해보자.

List<User> findByName(String name); //이름을 통해 유저 가져오는 메서드.

✅ 잘나왔다.

[User(id=1, name=martin, email=martin@fast.com, createdAt=2022-02-04T00:58:41.588, updatedAt=2022-02-04T00:58:41.588), 
User(id=5, name=martin, email=james@another.com, createdAt=2022-02-04T00:58:41.603, updatedAt=2022-02-04T00:58:41.603)]

🤔✅👉😎🤚😮

🤚 JpaRepository에서, 즉 쿼리메서드에서는 리턴타입을 고정해야하는것이 아니라 리턴이 1개 or N개에 따라 자동으로 리턴타입을을 제공해준다. 어떤 타입들을 제공하는지 살펴보자.

😮 편리하게 제공하는 장점도 있지만 런타임(실행)할 때 오류가 날수 있으니 정확하게 지정해주는것이 좋다.

🤔 NAMEING 함수 이름은 어떻게 지을까?

JPA에서는 가독성 있는 프로그래밍을 지향해 어떤이름으로 해도 함수를 지정해도된다.

😈 하지만 엔티티에 타입이 명시되어 있으면 findUserByName getUsersByID 같은 접두사 앞에 User라는 명시는 필요없다.
😈 또한 findByByName 이라고 쳐도 Test는 통과하지만 실행중에 오류가 나오니 왠만하면 메서드 이름을 위의 사진대로 하는 것이 좋다. 그리고 꼭 Test 해보는 것을 추천한다.

✅ select문 한번 만들어보자.

    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);       // 비추. User는 이미 명시되어있다.
    User findSomethingByEmail(String email);  // 이렇게 해도 되지만, 비추. 명시적으로 하자
    @Test
    void select() {
        System.out.println(userRepository.findByName("martin"));
        System.out.println("findByEmail : " + userRepository.findByEmail("martin@fast.com"));
        System.out.println("getByEmail : " + userRepository.getByEmail("martin@fast.com"));
        System.out.println("readByEmail : " + userRepository.readByEmail("martin@fast.com"));
        System.out.println("queryByEmail : " + userRepository.queryByEmail("martin@fast.com"));
        System.out.println("searchByEmail : " + userRepository.searchByEmail("martin@fast.com"));
        System.out.println("streamByEmail : " + userRepository.streamByEmail("martin@fast.com"));
        System.out.println("findUserByEmail : " + userRepository.findUserByEmail("martin@fast.com"));
        System.out.println("findSomethingByEmail : " + userRepository.findSomethingByEmail("martin@fast.com"));

    }

결과를 보면 함수이름은 달라도 쿼리가 같다.

✅ 조금 더 실습해 보자. First,Top을 사용해보자. 그리고 Last도 한번 실행해보자.

UserRepository
    List<User> findFirst2ByName(String name);

    List<User> findTop2ByName(String name);

    List<User> findLast1ByName(String name);
UserRepositoryTest
        System.out.println("findTop2ByName : " + userRepository.findTop2ByName("martin"));
        System.out.println("findFirst2ByName : " + userRepository.findFirst2ByName("martin"));
        System.out.println("findLast1ByName : " + userRepository.findLast1ByName("martin"));

👉 martin을 조건으로 Limit를 걸어서 2명을 출력한다. 굿굿!!

🤚 하지만 findLast1ByName은 정상적으로 작동했을까?

👉 Test는 통과하였지만 정상 작동은 아니다. Last 라는 Keyword는 JPA에서 감지하지 못하기 때문이다. 그래서 일반 select쿼리를 뱉어냈다.

🤚 참고사항으로... Last를 뽑으려면 OrderBy를 사용하여 역순정렬하고 first1을 사용하면 된다.


🤔 Where 조건들이 간단하지 않을텐데....

그래서 아래 사진의 키워드를 이용하면 된다.

✅ 실습해보자

👉 findByEmailAndName

// AND
List<User> findByEmailAndName(String email, String name);

// OR
List<User> findByEmailOrName(String email, String name);

// After('>',~보다 큰것), Before('<',~보다 작은 것)
List<User> findByCreatedAtAfter(LocalDateTime yesterday);
List<User> findByIdAfter(Long id);

// GreaterThan ==  (After Before와 같지만, 좀 더 범용적으로 사용 가능 모든 숫자 값 날짜 값 사용가능하다.)
List<User> findByCreatedAtGreaterThan(LocalDateTime yesterday);

// GreaterThanEqual('이상','>=') (참고로 Before,After는 초과,미만 만 가능하다.)
List<User> findByCreatedAtGreaterThanEqual(LocalDateTime yesterday);

// Between (~와 ~사이) (양끝의 값을 포함한다!!!!)
List<User> findByCreatedAtBetween(LocalDateTime yesterday, LocalDateTime tomorrow);
List<User> findByIdBetween(Long id1, Long id2);

// (Between을 풀어서 쓴 것) GreaterThanEqual And LessThanEqual (~이상 그리고 ~이하) 
List<User> findByIdGreaterThanEqualAndIdLessThanEqual(Long id1, Long id2);

// IsNotNull (빈 값이 아닌 것 찾기) (IsNull은 빈 값 찾기)
List<User> findByIdIsNotNull();

// In ('문자'를 가지고있는 스키마 찾기) (or조건과 비슷하다)
List<User> findByNameIn(List<String> names);

// StartingWith ('?' 으로 시작하는 문자를 가지고있는 스키마 찾기)
List<User> findByNameStartingWith(String name);
// EndingWith ('?' 으로 끝나는 문자를 가지고있는 스키마 찾기)
List<User> findByNameEndingWith(String name);
// Contains ('?' 글자를 포함하는 문자를 가지고있는 스키마 찾기)
List<User> findByNameContains(String name);

// Like ( 위 3개 쿼리를 복합적으로 사용 , %A [A로 끝] , A% [A로 시작] , %A% [A포함]
List<User> findByNameLike(String name);

// Is ,Equals (파라미터와 일치하는 쿼리)(코드 가독성을 높혀준다.)
Set<User> findUserByNameIs(String name);
Set<User> findUserByNameEquals(String name);
System.out.println("findByEmailAndName : " + userRepository.findByEmailAndName("martin@fast.com", "martin"));
System.out.println("findByEmailOrName : " + userRepository.findByEmailOrName("martin@fast.com", "dennis"));
System.out.println("findByCreatedAtAfter : " + userRepository.findByCreatedAtAfter(LocalDateTime.now().minusDays(1L)));
System.out.println("findByIdAfter : " + userRepository.findByIdAfter(4L));
System.out.println("findByCreatedAtGreaterThan : " + userRepository.findByCreatedAtGreaterThan(LocalDateTime.now().minusDays(1L)));
System.out.println("findByCreatedAtGreaterThanEqual : " + userRepository.findByCreatedAtGreaterThanEqual(LocalDateTime.now().minusDays(1L)));
System.out.println("findByCreatedAtBetween : " + userRepository.findByCreatedAtBetween(LocalDateTime.now().minusDays(1L), LocalDateTime.now().plusDays(1L)));
System.out.println("findByIdBetween : " + userRepository.findByIdBetween(1L, 3L));
System.out.println("findByIdGreaterThanEqualAndIdLessThanEqual : " + userRepository.findByIdGreaterThanEqualAndIdLessThanEqual(1L, 3L));
System.out.println("findByIdIsNotNull : " + userRepository.findByIdIsNotNull());
System.out.println("findByNameIn : " + userRepository.findByNameIn(Lists.newArrayList("martin", "dennis")));
System.out.println("findByNameStartingWith : " + userRepository.findByNameStartingWith("mar"));
System.out.println("findByNameEndingWith : " + userRepository.findByNameEndingWith("tin"));
System.out.println("findByNameContains : " + userRepository.findByNameContains("art"));
System.out.println("findByNameLike : " + userRepository.findByNameLike("%" + "art" + "%"));
profile
OK가자

0개의 댓글