JPQL로 member1을 찾으려면?
@Test
public void startJPQL() {
Member result = em.createQuery("select m from Member m where m.username = :username", Member.class)
.setParameter("username", "member1")
.getSingleResult();
assertThat(result.getUsername()).isEqualTo("member1");
}
QueryDsl로 member1을 찾으려면?
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
@Test
public void startQueryDsl() {
QMember m = QMember.member;
Member member = queryFactory
.select(m)
.from(m)
.where(m.username.eq("member1"))
.fetchOne();
assertThat(member.getUsername())
.isEqualTo("member1");
}
QueryDsl 을 사용하면 직접 쿼리를 문자로 작성할 필요 없이 자바 코드로 작성할 수 있도록 도와준다.
이를 통해 동적 쿼리를 편리하게 작성할 수 있으며, 컴파일 시점에 SQL 문법 오류를 검증할 수 있다는 큰 장점이 있다.
위 코드에서 살펴보았듯이 QueryDsl 을 사용하려면 엔티티의 Q-Type이 필요하다.
Q-Type 생성 방법
new 키워드를 사용하여 새롭게 이름을 할당하여 인스턴스를 생성한다.
기존에 QueryDsl 에서 생성해둔 Q-Type을 사용한다.
2번 방식을 사용하자.
불필요한 인스턴스 생성을 방지하여 성능면에서 이점이 있다.
static import를 사용하여 위 코드를 간단하고 보기 좋게 작성할 수 있다.
@Test
public void startQuery() {
Member result = queryFactory
.select(member)
.from(member)
.where(member.username.eq("member1"))
.fetchOne();
assertThat(result.getUsername())
.isEqualTo("member1");
}
1번 방식은 언제?
Q-Type에 이름을 할당한다는 것은 SQL을 날릴 때, 해당 테이블에 대한 별칭을 무엇으로 할 지를 지정하는 효과가 있다. 따라서, 같은 테이블을 조인해야할 때, 별칭을 따로 사용해야하므로 유용하게 사용될 수 있다.
QueryDsl 은 Where 절에 사용할 수 있는 수많은 검색 조건을 자바 코드상에서 지원한다.
예시)
@Test
public void search() {
Member result = queryFactory
.selectFrom(member)
.where(member.username.eq("member1").and(member.age.eq(10)))
.fetchOne();
assertThat(result.getUsername()).isEqualTo("member1");
assertThat(result.getAge()).isEqualTo(10);
}
and 조건 처리하기
QueryDsl 에서 and를 사용하는 방법은 2가지가 존재한다.
method chain으로 사용하기 (위 방법)
parameter로 사용하기 (where() 조건 안에 파라미터로 넘기는 방법이다.)
and 조건을 parameter로 사용할 경우 null 조건은 무시된다.
이 특징을 활용하면 동적쿼리를 간단하게 처리할 수 있다.
fetch(): 리스트 조회, 없으면 빈 리스트
fetchOne(): 단 건 조회
결과가 없으면 null
결과가 둘 이상이면: NonUniqueResultException
fetchFirst(): limit(1).fetchOne()
fetchResults(): 페이징 정보 포함, total count 쿼리 추가 실행
fetchCount(): count 쿼리로 변경하여 count 수 조회
.orderBy() 에 파라미터로 정렬 조건을 넘겨 사용할 수 있다.
예시)
@Test
public void sort() {
Member member1 = new Member("A", 100);
Member member2 = new Member("B", 100);
Member member3 = new Member(null, 100);
em.persist(member1);
em.persist(member2);
em.persist(member3);
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.eq(100))
.orderBy(member.age.desc(), member.username.asc().nullsLast())
.fetch();
assertThat(result).containsExactly(member1, member2, member3);
}
orderBy 로 age 는 내림차순, username은 오름차순으로 정렬하였다.
username 이 null 일 경우 마지막에 오도록 nullsLast 조건을 추가로 넣어 정렬하였다.
.offset() 에 몇번째 데이터부터 가져올 것인지 지정할 수 있다.
.limit() 에 몇번째 데이터까지 가져올 것인지 지정할 수 있다.
fetchResults 를 사용하면 getTotal, getLimit, getOffset 등 페이징과 관련된 정보들도 가져올 수 있다.
참고.
실무에서 조인이 복잡해져 페이징과 카운트 쿼리를 분리해야할 수 있다.