-
Querydsl (1) - 기본 문법Web/QueryDSL 2024. 1. 24. 15:08
1. QueryDSL 기본 사용법
public void startQuerydsl() { //member1을 찾아라. JPAQueryFactory queryFactory = new JPAQueryFactory(em); QMember m = new QMember("m"); Member findMember = queryFactory .select(m) .from(m) .where(m.username.eq("member1"))//파라미터 바인딩 처리 .fetchOne(); assertThat(findMember.getUsername()).isEqualTo("member1"); }
- QMember는 querydsl 라이브러리 추가하고, 엔티티를 한번 빌드하면 생긴다.
(별칭은 jpql에서 사용할 별칭 -> 내부조인하는거아니면 잘 사용하지 않는다)
- JPAQueryFactory에 EntityManager를 넣고 사용한다. (엔티티 메니저는 빈으로 등록해서 사용해도 된다!)
2. 기본 Q-Type 활용
QMember qMember = new QMember("m"); //별칭 직접 지정 QMember qMember = QMember.member; //기본 인스턴스 사용
Member findMember = queryFactory .select(member) .from(member) .where(member.username.eq("member1")) .fetchOne(); //querydsl은 결국 jpql을 자동으로 만들어주는 라이브러리이다!
2.2 검색 조건 쿼리
Member findMember = queryFactory .selectFrom(member) .where(member.username.eq("member1") .and(member.age.eq(10))) .fetchOne();
- where뒤에 and(조건),or(조건)으로 연결할 수 있다.
- where()안에 and조건을 ,로 구분해서 넣을 수도 있다! (null이면 자동으로 무시)
//jpql의 모든 검색 조건을 제공한다! 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%’ 검색
2.3 결과 조회
- fetch() : 리스트 조회, 데이터 없으면 빈 리스트 반환
- fetchOne() : 단 건 조회 (결과 없으면 null, 결과 둘 이상이면 NonUniqueResultException)
- fetchFirst(): limit(1).fetchOne()과 같다.
- fetchResults() : 페이징 정보 포함, total count 쿼리 추가 실행
- fetchCount() : count 쿼리로 변경해서 count 조회
2.4 정렬
List<Member> result = queryFactory .selectFrom(member) .where(member.age.eq(100)) .orderBy(member.age.desc(), member.username.asc().nullsLast()) .fetch();
- desc(), asc() : 정렬
- nullsLast(), nullsFirst() : null 데이터 순서 부여
3. 페이징
List<Member> result = queryFactory .selectFrom(member) .orderBy(member.username.desc()) .offset(1) //0부터 시작(zero index) 앞에 건너뛰고 싶은 갯수 .limit(2) //최대 2건 조회 페이지당 갯수 -> limit기준으로 page 나온다. .fetch();
* 데이터를 조회하는 쿼리는 여러 테이블 조인한다. count쿼리는 조인이 필요없는 경우도 있다.
하지만, 자동화된 count쿼리는 원본 쿼리와 모두 조인하기때문에 성능이 안나올 수 있다. count에 최적화 필요시 count전용 필드 만들자.
4. sum,avg,max,min등의 함수
List<Tuple> result = queryFactory .select(member.count(), member.age.sum(), member.age.avg(), member.age.max(), member.age.min()) .from(member) .fetch();
-결과가 튜플 자료형이다. tuple.get(member.count()), tuple.get(member.age.sum())으로 가져올 수 있다.
5. GroupBy
List<Tuple> result = queryFactory .select(team.name, member.age.avg()) .from(member) .join(member.team, team) .groupBy(team.name) .fetch();
- having도 사용가능하다!
6. 조인
6.1 기본 조인
- >내부 조인
기본 저인은 첫 번째 파라미터에 조인 대상 지정하고, 두번째 파라미터에 별칭으로 사용할 Q타입 지정하면됨
QMember member = QMember.member; QTeam team = QTeam.team; List<Member> result = queryFactory .selectFrom(member) .join(member.team, team) .where(team.name.eq("teamA")) .fetch();
- 기본적으로 fk를 사용해서 조인해준다!
- join(), innerJoin(), leftJoin(),rightJoin() 성능 최적화를 위한 fetch조인 모두 제공
-> on절의 활용
List<Tuple> result = queryFactory .select(member, team) .from(member) .leftJoin(member.team, team).on(team.name.eq("teamA")) .fetch();
-> on절을 통해 join할 테이블의 값에 where 조건을 걸 수 있다. 만약 inner 조인이면 그냥 where 조건문에서 필터링하자
6.2 연관 관계 없는 필드 조인
-> 연관관계 없는 필드로 조인 (inner) 세타조인
List<Member> result = queryFactory .select(member) .from(member, team) .where(member.username.eq(team.name)) .fetch();
- from절을 사용해서 조인한다.
-> 연관관계 없는 엔티티 외부 조인
List<Tuple> result = queryFactory .select(member, team) .from(member) .leftJoin(team).on(member.username.eq(team.name)) .fetch();
- leftJoin, rightJoin (조인할 대상만 Q타입으로 가져옴 -> 내 필드에 값 사용 x 어차피 연관관계 없음)
- on으로 필터 조건을 건다.
6.3 패치조인
- 패치조인은 SQL에서 제공하는 기능이 아니다. SQL조인을 활용해서 연관된 엔티티를 한번에 조회하는 최적화 기능
Member findMember = queryFactory .selectFrom(member) .join(member.team, team).fetchJoin() .where(member.username.eq("member1")) .fetchOne();
7. 서브 쿼리
7.1 where 절에서 사용
QMember memberSub = new QMember("memberSub"); List<Member> result = queryFactory .selectFrom(member) .where(member.age.eq( JPAExpressions .select(memberSub.age.max()) .from(memberSub) )) .fetch(); List<Member> result = queryFactory .selectFrom(member) .where(member.age.goe( JPAExpressions .select(memberSub.age.avg()) .from(memberSub) )) .fetch(); List<Member> result = queryFactory .selectFrom(member) .where(member.age.in( JPAExpressions .select(memberSub.age) .from(memberSub) .where(memberSub.age.gt(10)) )) .fetch();
- JPAExpressions를 한번 사용해줘야한다.
7.2 select절에서 사용
List<Tuple> fetch = queryFactory .select(member.username, JPAExpressions .select(memberSub.age.avg()) .from(memberSub) ).from(member) .fetch();
* from절에서 서브쿼리 사용하는 인라인뷰는 지원하지 않는다.
> 조인으로 해결한다. 어플리케이션에서 쿼리를 2번 분리한다. nativeSQL을 사용한다!
> 보통 프롬절 서브쿼리는 화면에 보여지는 거 때문에 복잡해지는 경우가 많다. 화면에 맞춘 쿼리를 날리면 재사용성이 떨어진다. 데이터는 딱 필요한 것만 퍼올리고, 프레젠 테이션 로직은 프레젠테이션에서 끝내는게 맞다.
꼭 한방 쿼리로 가져와야한다고 생각하지말자!
8. Case문
List<String> result = queryFactory .select(member.age .when(10).then("열살") .when(20).then("스무살") .otherwise("기타")) .from(member) .fetch(); List<String> result = queryFactory .select(new CaseBuilder() .when(member.age.between(0, 20)).then("0~20살") .when(member.age.between(21, 30)).then("21~30살") .otherwise("기타")) .from(member) .fetch();
- when안에서 조건을 쉽게 넣을 수 있다!
* 만약 임의의 순서로 회원을 출력하고 싶다면?
NumberExpression<Integer> rankPath = new CaseBuilder() .when(member.age.between(0, 20)).then(2) .when(member.age.between(21, 30)).then(1) .otherwise(3); List<Tuple> result = queryFactory .select(member.username, member.age, rankPath) .from(member) .orderBy(rankPath.desc()) .fetch();
- NumberExpression을 통해 rankPath처럼 복잡한 변수를 만들어서 select, orderBy절에서 함께 사용할 수 있다.
9. 상수 문자 더하기
Tuple result = queryFactory .select(member.username, Expressions.constant("A")) .from(member) .fetchFirst();
- 이때 그냥 상수라면, 쿼리에 포함 x 그냥 결과에 붙여준다.
9.1 문자 더하기 concat
String result = queryFactory .select(member.username.concat("_").concat(member.age.stringValue())) .from(member) .where(member.username.eq("member1")) .fetchOne();
- .concat(더하기 문자열)하면된다.
- stringValue()로 문자로 직접 변환 가능하다. ENUM처리시 자주 사용함
'Web > QueryDSL' 카테고리의 다른 글
Querydsl (4) - 스프링 데이터 JPA + Querydsl (0) 2024.01.24 Querydsl (3) - 순수 JPA와 Querydsl (0) 2024.01.24