김영한님의 실전! querydsl 강의 내용을 정리한 노트입니다. 블로그에 있는 자료를 사용하실 때에는 꼭 김영한님 강의 링크를 남겨주세요!
※assertions 검증 결과는 어떻게 확인할까?
== ‘member2’
), actual : 실제 들고 있는 값(result.getUsername() ==
) @SpringBootTest
@Transactional
public class QuerydslBasicTest {
@Autowired
EntityManager em;
@BeforeEach //테스트 실행 전 데이터를 넣는 작업 진행
public void before() {
Team teamA = new Team("teamA");
Team teamB = new Team("teamB");
em.persist(teamA);
em.persist(teamB);
Member member1 = new Member("member1", 10, teamA);
Member member2 = new Member("member2", 20, teamA);
Member member3 = new Member("member3", 30, teamB);
Member member4 = new Member("member4", 40, teamB);
em.persist(member1);
em.persist(member2);
em.persist(member3);
em.persist(member4);
}
@Test
public void startJPQL() {
//memeber 1 찾기
Member findMember = em.createQuery("select m from Member m where m.username = :username", Member.class)
.setParameter("username", "member1")
.getSingleResult();
Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
}
@Test
public void startQuerydsl() {
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
QMember m = new QMember("m");
Member findMember = queryFactory
.select(m)
.from(m)
.where(m.username.eq("member1"))
.fetchOne();
Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
}
}
@Test
public void startQuerydsl() {
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
QMember m = new QMember("m"); //QMember의 인자에는 별칭을 넣어줌 -> 크게 중요하지 않음. 앞으로 안 쓸 거임ㅋㅋ
// 파라미터 바인딩 없이 사용 가능
//m.username.eq("member1") 으로 써도 prepare statement로 파라미터 바인딩이 자동으로 진행
//db 입장에서도 성능면에서 유리
Member findMember = queryFactory
.select(m)
.from(m)
.where(m.username.eq("member1"))
.fetchOne();
Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
}
JPAQueryFactory를 생성할 때 EntityManager를 인자로 넘겨준다
별도의 파라미터 바인딩이 없음 → prepare statement로 파라미터 바인딩이 자동으로 진행, 이러면 db 입장에서는 성능면에서도 유리함
요 JPAQueryFactory는 필드로 뺄 수 있음 → 그런데 동시성 문제가 있을텐데???? spring에서 제공하는 Manager들은 멀티 스레드에 맞게 설계가 되어 있기 때문에 동시성 문제에 대해 크게 고민하지 않아도 됨. 트렌젝션에 바인딩 되도록 분배해줌
@SpringBootTest
@Transactional
public class QuerydslBasicTest {
@Autowired
EntityManager em;
JPAQueryFactory queryFactory;
@BeforeEach //테스트 실행 전 데이터를 넣는 작업 진행
public void before() {
queryFactory = new JPAQueryFactory(em);
//db에 값 넣어주는 로직 ...
}
@Test
public void startQuerydsl() {
QMember m = new QMember("m");
Member findMember = queryFactory
.select(m)
.from(m)
.where(m.username.eq("member1"))
.fetchOne();
Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
}
}
QMember qMember = new QMember("m"); //별칭 직접 지정
QMember qMember = QMember.member; //기본 인스턴스 사용
alt
+ enter
후 static import 선택@Test
public void startQuerydsl() {
Member findMember = queryFactory
.select(member)
.from(member)
.where(member.username.eq("member1"))
.fetchOne();
Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
}
spring.jpa.properties.hibernate.use_sql_comments: true
설정을 추가하면 됨@Test
public void search() {
//selectFrom = select + from을 합친 것
//이름이 member1이면서(and) 나이가 10인 사람을 조회해
//조건의 체인은 and, or 다 걸 수 있음
Member findMember = queryFactory
.selectFrom(member)
.where(member.username.eq("member1").and(member.age.eq(10)))
.fetchOne();
Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
}
member.username.eq("member1") // username = 'member1'
member.username.ne("member1") //username != 'member1'
member.username.eq("member1").not() // username != 'member1'
member.username.isNotNull() //이름이 is not null
member.age.in(10, 20) // age in (10,20)
member.age.notIn(10, 20) // age not in (10, 20)
member.age.between(10,30) //between 10, 30
member.age.goe(30) // age >= 30
member.age.gt(30) // age > 30
member.age.loe(30) // age <= 30
member.age.lt(30) // age < 30
member.username.like("member%") //like 검색
member.username.contains("member") // like ‘%member%’ 검색
member.username.startsWith("member") //like ‘member%’ 검색
where(member.username.eq("member1", member.age.eq(10), null)
//and 조건은 쉼표로 끊어 갈 수 있음
//where의 파라미터를 여러 개 넘기면 걔가 and로 붙음
Member findMember = queryFactory
.selectFrom(member)
.where(
member.username.eq("member1"),
member.age.eq(10)
)
.fetchOne()
fetch() : 리스트 조회 → 가장 많이 사용(데이터가 없다면 빈 리스트를 반환)
fetchOne() : 단 건 조회 → 결과가 없으면 null, 결과가 2개 이상이면 com.querydsl.core.NonUniqueResultException
fetchFirst() : limit(1).fetchOne()와 같음
fetchResults() : 페이징 정보 포함, total count 쿼리 추가 실행 → 실제 나가는 쿼리는 2개
fetchCount() : count 수 조회
사용 코드는 다음과 같음
@Test
public void resultFetch() {
List<Member> fetch = queryFactory
.selectFrom(member)
.fetch();
Member fetchOne = queryFactory
.selectFrom(member)
.fetchOne();
Member fetchFirst = queryFactory
.selectFrom(member)
.fetchFirst();
QueryResults<Member> results = queryFactory
.selectFrom(member)
.fetchResults();
long total = results.getTotal();
List<Member> content = results.getResults();
long totalCount = queryFactory
.selectFrom(member)
.fetchCount();
}
/**
* 회원 정렬 순서
* 1. 회원 나이 내림차순(desc)
* 2. 회원 이름 올림차순(asc)
* 단, 2에서 회원 이름이 없으면 마지막에 출력(nulls last)
*/
@Test
public void sort() {
//예제를 위해 데이터를 더 추가하는 작업
em.persist(new Member(null, 100));
em.persist(new Member("member5", 100));
em.persist(new Member("member6", 100));
//나이 조건은 다 100살로 맞췄기 때문에 실질적으로는 회원 이름의 올림차순만 적용
List<Member> results = queryFactory
.selectFrom(member)
.where(member.age.eq(100))
.orderBy(member.age.desc(), member.username.asc().nullsLast())
.fetch();
Member member5 = results.get(0);
Member member6 = results.get(1);
Member memberNull = results.get(2);
Assertions.assertThat(member5.getUsername()).isEqualTo("member5");
Assertions.assertThat(member6.getUsername()).isEqualTo("member56");
Assertions.assertThat(memberNull.getUsername()).isNull();
}
@Test
public void paging1() {
List<Member> result = queryFactory
.selectFrom(member)
.orderBy(member.username.desc())
.offset(1)
.limit(2)
.fetch();
Assertions.assertThat(result.size()).isEqualTo(2);
}
쿼리는 요렇게 나감