@Query
어노테이션을 사용해서 리파지토리 인터페이스에 쿼리 직접 정의스프링 데이터 JPA는 메소드 이름을 분석해서 JPQL 생성
이름과 나이를 기준으로 회원을 조회하려면?
public List<Member> findByUsernameAndAgeGreaterThan(String username, int age) {
return em.createQuery("select m from Member m where m.username = :username and m.age > :age")
.setParameter("username", username)
.setParameter("age", age)
.getResultList();
}
public interface MemberRepository extends JpaRepository<Member, Long> {
List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
}
find…By
, read…By
, query…By
get…By
,
count…By
반환타입 longexists…By
반환타입 booleandelete…By
, remove…By
반환타입 longfindDistinct
, findMemberDistinctBy
findFirst
, findTop
, findTop3
참고: 이 기능은 엔티티의 필드명이 변경되면 인터페이스에 정의한 메서드 이름도 꼭 함께 변경해야 한다. 그렇지 않으면 애플리케이션을 시작하는 시점에 오류가 발생한다. 이렇게 애플리케이션 로딩 시점에 오류를 인지할 수 있는 것이 스프링 데이터 JPA의 매우 큰 장점이다.
필터링 조건 등이 많아지면 메소드 이름이 너무 길어진다. 그래서 파라미터가 2개까지는 괜찮은데 3개부터는 다른 방법을 이용할 수도?
JPA의 NamedQuery를 호출할 수 있음
@NamedQuery
어노테이션으로 Named 쿼리 정의*@NamedQuery
는 애플리케이션 로딩 시점에 쿼리 문법을 파싱해서 체크할 수 있다.
@Entity
@NamedQuery(name="Member.findByUsername",
query="select m from Member m where m.username = :username")
public class Member {
...
}
List<Member> resultList = em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", username)
.getResultList();
public interface MemberRepository extends JpaRepository<Member, Long> {
//** 여기 선언한 Member 도메인 클래스
List<Member> findByUsername(@Param("username") String username);
}
실무에서 NamedQuery 사용할 일이 없다고 함!!
대신 @Query
를 사용해서 리파지토리 메소드에 쿼리를 직접 정의한다.
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query("select m from Member m where m.username= :username and m.age = :age")
List<Member> findUser(@Param("username") String username, @Param("age") int age);
}
@org.springframework.data.jpa.repository.Query
어노테이션을 사용@Param
을 붙여줘야 한다.참고: 실무에서는 메소드 이름으로 쿼리 생성 기능은 파라미터가 증가하면 메서드 이름이 매우 지저분해진다. 따라서
@Query
기능을 자주 사용하게 된다.
@Query("select m.username from Member m")
List<String> findUsernameList();
@Embedded
)도 이 방식으로 조회할 수 있다.@Query("select new study.datajpa.dto.MemberDto(m.id, m.username, t.name) " +
"from Member m join m.team t")
List<MemberDto> findMemberDto();
@Data
public class MemberDto {
private Long id;
private String username;
private String teamName;
public MemberDto(Long id, String username, String teamName) {
this.id = id;
this.username = username;
this.teamName = teamName;
}
}
select m from Member m where m.username = ?0 //위치 기반
select m from Member m where m.username = :name //이름 기반
import org.springframework.data.repository.query.Param
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query("select m from Member m where m.username = :name")
Member findMembers(@Param("name") String username);
}
@Query("select m from Member m where m.username in :names")
List<Member> findByNames(@Param("names") List<String> names);
List<Member> findByUsername(String name); //컬렉션
Member findByUsername(String name); //단건
Optional<Member> findByUsername(String name); //단건 Optional
조회 결과가 많거나 없으면?
javax.persistence.NonUniqueResultException
예외 발생참고: 단건으로 지정한 메서드를 호출하면 스프링 데이터 JPA는 내부에서 JPQL의
Query.getSingleResult()
메서드를 호출한다. 이 메서드를 호출했을 때 조회 결과가 없으면javax.persistence.NoResultException
예외가 발생하는데 개발자 입장에서 다루기가 상당히 불편하다. 스프링 데이터 JPA는 단건을 조회할 때 이 예외가 발생하면 예외를 무시하고 대신에 null을 반환한다.
단건 조회에서 결과가 2건 이상일 때 JPA가
javax.persistence.NonUniqueResultException
을 발생 시키지만 스프링은 이 예외를 잡아서IncorrectResultSizeDataAccessException
으로 변환 시켜 던진다. (JPA를 쓰든 안 쓰든 클라이언트 코드(service계층 등)에서 일관되게 예외를 다루기 위해서)