List<Tuple> result = queryFactory
.select(
member.count(),
member.age.sum(),
member.age.avg(),
member.age.max(),
member.age.min()
)
.from(member)
.fetch();
List<Tuple> result = queryFactory
.select(team.name, member.age.avg())
.from(member)
.join(member.team, team)
.groupBy(team.name)
.fetch();
}
having 절도 사용 가능하다.
List<Member> result = queryFactory
.selectFrom(member)
.join(member.team, team)
.where(team.name.eq("teamA"))
.fetch();
}
세타 조인이란? 연관 관계가 없는 엔티티를 조인하는 것이다.
세타 조인을 사용할 경우, left outer join 이나 right outer join 이 불가능하다.
on 을 사용하면 가능하다.
List<Member> result = queryFactory
.select(member)
.from(member, team)
.where(member.username.eq(team.name))
.fetch();
inner join 일 경우, where 절을 이용해도 결과가 동일하다.
inner join 이면 where 절을 이용하고, outer join 일 경우 on 절을 이용하자.
List<Tuple> result = queryFactory
.select(member, team)
.from(member)
.leftJoin(member.team, team).on(team.name.eq("teamA"))
.fetch();
하이버네이트 5.1부터 on 을 이용해 서로 관계가 없는 필드로 외부 조인이 가능하게 되었다.
일반 조인과 다르게 leftjoin 부분에 엔티티가 하나만 들어간다
List<Tuple> result = queryFactory
.select(member, team)
.from(member)
.leftJoin(team).on(member.username.eq(team.name))
.fetch();
Member findMember = queryFactory
.selectFrom(member)
.join(member.team, team).fetchJoin()
.where(member.username.eq("member1"))
.fetchOne();
<참고>엔티티가 로딩되었는지 여부는 EntityManagerFactory를 이용하면 알 수 있다.
@PersistenceUnit
EntityManagerFactory emf;
// 이미 로딩됬으면 True 아니면 False
boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
assertThat(loaded).as("페이 조인 미적용").isFalse();
com.querydsl.jpa.JPAExpressions를 사용하면 된다.
(static import를 이용해 더 간결하게 만드는 게 가능하다!)
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.eq(
JPAExpressions
.select(memberSub.age.max())
.from(memberSub)
)
).fetch();
List<Tuple> result = queryFactory
.select(member.username,
JPAExpressions
.select(memberSub.age.avg())
.from(memberSub)
).from(member)
.fetch();
JPA JPQL 서브쿼리의 한계점으로 from 절에서 서브쿼리를 사용할 수 없다. 따라서 Querydsl 에서도 from 절에서 서브쿼리를 사용할 수 없다.
원래 select 절에도 서브 쿼리는 사용할 수 없다. 그러나 하이버네이트 구현체를 사용하는 경우, 서브 쿼리를 사용할 수 있다.
이런 로직은 가능하면 DB에서 처리하기 보다는, 애플리케이션 로직을 통해 처리하는 편이 더 바람직하다.
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();
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();
상수가 필요할 땐 Expressions.constant() 를 사용한다.
List<Tuple> result = queryFactory
.select(member.username, Expressions.constant("A"))
.from(member)
.fetch();
문자가 아닌 다른 타입들은 stringValue() 를 이용하면 된다. 특히 Enum 을 처리할 때 자주 사용한다.
//{username}_{age}
List<String> result = queryFactory
.select(member.username.concat("_ ").concat(member.age.stringValue()))
.from(member)
.where(member.username.eq("member1"))
.fetch();