Programming/JPA

[Spring Data JPA] Query를 사용하는 여러가지 방법

빈쿵바라기 2022. 8. 11. 23:52

Spring Data JPA에서 Query를 사용하는 방법은 여러가지가 있습니다.

1. Query Method
2. @Query 어노테이션
3. Query by Example

이제 각각 쿼리 사용 방법의 예제 코드를 보면서 어떻게 사용하는지 알아보겠습니다. 


1. Query Method

일반적으로 JPA에서는 쿼리 메소드를 사용하여 쿼리를 만들 수 있습니다.  우리는 앞서 DB에 쿼리 요청을 하기 위해서 Repository 인터페이스를 만들었습니다. Repository에 메소드를 작성함으로써 정의된 메소드에 따라 쿼리를 자동으로 만들어서 요청 할 수 있게 됩니다.

public interface MemberRepository extends JpaRepository<Member, Integer> {

    // MEMBER 테이블의 name column에 검색하는 쿼리
    List<Member> findByName(String name);
    // MEMBER 테이블의 eamil_address column과 name column에 and 조건으로 검색 하는 쿼리
    List<Member> findByEmailAddressAndName(String emailAddress, String name);
}

findByName을 호출하게 되면, JPA에서 다음과 같이 쿼리를 만들어서 요청하게 됩니다.

select m from member m where m.name = ?1

두개 이상의 column에 and 조건으로 조회하고 싶을 때는 column명 사이에 and를 사용하여 메소드를 작성하면 됩니다.

findByEmailAddressAndName을 호출하게 되면 아래와 같이 쿼리를 요청합니다.

select m from member m where m.email_address = ?1 and m.name = ?2

이렇게 정해진 표현식에 맞게 메소드를 작성하면 JPA에서 쿼리를 자동으로 만들어서 요청을 할 수 있습니다. 다음 표에서 JPA에 지원되는 표현식과 해당 표현식이 무엇으로 변화하는지 알아보겠습니다.

예약어 샘플 쿼리
Distinct fnidDistinctByLastnameAndFirstname select distinct ... where x.lastname = ?1 x.firstname = ?2
And findByLastnameAndFirstname ... where x.lastname = ?1 and x.firstname = ?2
Or fintByLastnameOrFirstName ... where x.lastname = ?1 or x.firstname = ?2
Between firstByStartDateBetween ... 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
IsNull findByAgeIsNull ... where x.age is null
IsNotNull findByAgeIsNotNull ... where x.age not null
Like findByFirstnameLike ... where x.firstname like ?1
NotLike findByFirstnameNotLike ... where x.firstname not like ?1
OrderBy findByAgeOrderByLastnameDesc ... where x.age = ?1 order by x.lastname desc
Not fintByLastnameNot ... where x.lastname <> ?1
In findByAgeIn(Collection<> ages) ... where x.age in ?1
NotIn findByAgeNotIn(Collection<> ages) ... where x.age not in ?1

 


2. @Query 어노테이션

조금 복잡한 쿼리를 사용해야할 경우 정해진 표현식을 사용하는 Query Method는 적합하지 않을 수 있습니다. 이럴 때는  @Query 어노테이션이 좋은 대안이 될 수 있습니다. 

@Query 어노테이션은 실행할 메소드 위에 정적 쿼리를 작성하면 됩니다. 이 때 작성하는 쿼리는 JPQL이라는 쿼리로 작성을 해야합니다. Java Persistence Query Language 인 JPQL은 JPA의 특성인 엔티티 객체를 중심으로 작성하는 쿼리 입니다. 그래서 특정 db sql에 의존하지 않는 장점이 있습니다. JPQL 문법의 전체 구조는 SQL과 유사합니다.

public interface MemberRepository extends JpaRepository<Member, Integer> {

    @Query("select m from member m where m.name = ?1 and m.age = ?2")
    User findMethodA(String name, int age);
    
    @Query("select m from member m where m.name = :name and m.age = :age")
    User findMethodB(@Param("name") String param1, @Param("age") int param2)
}

@Query 의 파라미터 바인딩에는 두가지가 있습니다. 첫번째는 위치기반 방법,  두번째는 이름기반 방법입니다.

위치기반 바인딩쿼리의 ? 뒤에 숫자와 메소드 파라미터 위치 순서와 일치하면 바인딩을 합니다. 예제코드에서 findMethodA() 코드의 첫번째 파라미터인 String name ?1에 바인딩, 두번째 파라미터인 int age?2에 바인딩이 되는 것입니다.

이름기반 바인딩@Param("")어노테이션안에 작성된 이름과 쿼리의 : 뒤에 오는 이름과 바인딩이 됩니다. 예제에서는 findMethodB()의 @Param("name")으로 지정된 파마리터 String param1:name과 바인딩이 됩니다.


3. Query by Example

QBE(Query by Exmaple)은 JPA에서 생성한 엔티티를 활용하여 쿼리를 요청하는 방식입니다. 먼저 사용할 엔티티 예제 코드와 QBE 샘플 코드입니다.

@Entity
public class Member {
    @Id
    Integer id;
    String name;
    Integer age;
    // getter, setter..
}
public class MemberService {

    @Autowired MemberRepository memberRepository;

    public List<User> findMember() {
        Member member = new Member();
        member.setName("Son");
        member.setAge(28);

        Example<Member> example = Example.of(member);
        return memberRepository.findAll(example);
    }
}

Member 엔티티에 name과 age에 값을 할당하고, Example.of()를 통해 find 메소드를 날리면, 아래와 같이 쿼리를 요청하게 됩니다.

select m from member m where m.name = ?1 and m.age= ?2

여기까지 간략하게 Spring Data JPA에서 Query를 다루는 방법을 알아보았는데, 기초 내용만 담았습니다. 더 자세한 내용은 Spring Data JPA - Reference Documentation을 읽어보는게 좋을 것 같습니다.