@Test
public void startJPQL() {
// member1 찾기
String qlString = "select m from Member m " +
"where m.username = :username";
Member findMember = em.createQuery(qlString, Member.class)
.setParameter("username", "member1")
.getSingleResult();
assertThat(findMember.getUsername()).isEqualTo("member1");
}
JPQL은 쿼리문에서 "usernameeeee" 이라는 오타를 내어도 실행시키기 전까지는 알 수 없다. (최악의 에러)
@Test
public void startQuerydsl() {
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
QMember m = new QMember("m"); // 어떤 QMember인지 구분하는 이름을 줌
Member findMember = queryFactory
.select(m)
.from(m)
.where(m.username.eq("member1"))
.fetchOne();
assertThat(findMember.getUsername()).isEqualTo("member1");
}
JPQL과 차이점은 파라미터 바인딩이 필요없다는 것이다.
그리고 JPQL과 다르게 "usernameee"이라는 오타를 내면 컴파일 시점에 바로 잡을 수 있기 때문에 좋다.
EntityManager
로 JPAQueryFactory
생성JPQL
: 문자(실행 시점 오류를 알 수 있음), Querydsl
: 코드(컴파일 시점 오류를 알 수 있음)JPQL
: 파라미터 바인딩을 직접, Querydsl
: 파라미터 바인딩 자동 처리⚠️ 참고로 JPAQueryFactory를 필드로 제공하면 동시성 문제는 어떻게 되나?
동시성 문제는 JPAQueryFactory를 생성할 때 제공하는 EntityManager(em)에 달려있다.
스프링 프레임워크는 여러 쓰레드에서 동시에 같은 EntityManager에 접근해도, 트랜잭션마다 별도의 영속성 컨텍스트를 제공하기 때문에, 동시성 문제는 걱정하지 않아도 된다.
QMember qMember = new QMember("m");
: 별칭 직접 지정QMember qMember = QMember.member;
: 기본 인스턴스 사용(querydsl은 결국 JPQL의 빌더 역할을 하는 것이라 querydsl은 곧 jpql이기 때문에)
위처럼 실행되는 JPQL을 보기 위해서는 아래와 같은 코드를 application.yml에 추가해주면 된다.
spring.jpa.properties.hibernate.use_sql_comments: true
⚠️ 참고로, 같은 테이블을 조인해야 하는 경우가 아니라면 기본 인스턴스를 사용하도록!
// 기본 검색 쿼리
@Test
public void search() {
Member findMember = queryFactory
.selectFrom(member)
.where(member.username.eq("member1")
.and(member.age.eq(10))) // 이름이 member1이면서 나이가 10살인 사람을 조회해라
.fetchOne();
assertThat(findMember.getUsername()).isEqualTo("member1");
}
검색 조건은 .and()
, .or()
를 메소드 체인으로 연결할 수 있다.
또한, select
, from
을 selectFrom
으로 합칠 수 있다.
이외에도 등등,,
강사님은 깔끔해서 이 방식을 더 좋아하심🙃
Member findMember = queryFactory
.selectFrom(member)
.where( // 여기 안에서 여러개를 넘기면 걔네가 다 .and로 엮임
member.username.eq("member1"),
member.age.eq(10)
) // 이름이 member1이면서 나이가 10살인 사람을 조회해라
.fetchOne();
where()
에 파라미터로 검색 조건을 추가하면 AND
조건이 추가된다.
(이 경우 null 값은 무시되며 메소드 호출을 활용해 동적 쿼리를 깔끔하게 만들 수 있다.)
@Test
public void resultFetch() {
// List<Member> fetch = queryFactory
// .selectFrom(member)
// .fetch();
//
// Member fetchOne = queryFactory
// .selectFrom(QMember.member)
// .fetchOne(); // 단건조회
//
// Member fetchFirst = queryFactory
// .selectFrom(member)
// .fetchFirst(); // limit(1) + fetchOne()
QueryResults<Member> results = queryFactory
.selectFrom(member)
.fetchResults(); // 페이징 정보 포함, total count 쿼리 추가 실행
results.getTotal();
List<Member> content = results.getResults();
long total = queryFactory
.selectFrom(member)
.fetchCount();
}
fetch()
: 리스트 조회, 데이터 없으면 빈 리스트를 반환fetchOne()
: 단건 조회null
com.querydsl.core.NonUniqueResultException
fetchFirst()
: = limit(1).fetchOne()
fetchResults()
: 페이징 정보 포함, total count 쿼리 추가 실행fetchCount()
: count 쿼리로 변경해 count 수 조회