김영한님의 실전! querydsl 강의 내용을 정리한 노트입니다. 블로그에 있는 자료를 사용하실 때에는 꼭 김영한님 강의 링크를 남겨주세요!
@Test
public void aggregation() {
//아래의 예시처럼 내가 원하는 column으로 선택 시 Tuple이라는 것으로 조회하게 됨
List<Tuple> result = queryFactory
.select(
member.count(),
member.age.sum(),
member.age.avg(),
member.age.max(),
member.age.min()
)
.from(member)
.fetch();
Tuple tuple = result.get(0);
Assertions.assertThat(tuple.get(member.count())).isEqualTo(4);
Assertions.assertThat(tuple.get(member.age.sum())).isEqualTo(100);
Assertions.assertThat(tuple.get(member.age.avg())).isEqualTo(25);
Assertions.assertThat(tuple.get(member.age.max())).isEqualTo(40);
Assertions.assertThat(tuple.get(member.age.min())).isEqualTo(10);
}
F2
/**
* 팀의 이름과 각 팀의 평균 연령을 구해라.
*/
@Test
public void group() {
List<Tuple> result = queryFactory
.select(team.name, member.age.avg())
.from(member)
.join(member.team, team)
.groupBy(team.name)
.fetch();
Tuple teamA = result.get(0);
Tuple teamB = result.get(1);
assertThat(teamA.get(team.name)).isEqualTo("teamA");
assertThat(teamA.get(member.age.avg())).isEqualTo(15); //(10 + 20) / 2
assertThat(teamB.get(team.name)).isEqualTo("teamB");
assertThat(teamB.get(member.age.avg())).isEqualTo(35); //(30 + 40) / 2
}
/**
* 팀 A에 소속된 모든 회원 찾기
*/
@Test
public void join() {
List<Member> result = queryFactory
.selectFrom(member)
// .join(member.team, QTeam.team) 와 같음
// .leftJoin(member.team, team)으로도 쓸 수 있음
.join(member.team, team)
.where(team.name.eq("teamA"))
.fetch();
assertThat(result)
.extracting("username") //column 명 username에
.containsExactly("member1", "member2"); //member1과 member2가 포함되어 있는가
}
/**
* 회원의 이름이 팀 이름과 같은 회원 조회
*/
@Test
public void theta_join() {
em.persist(new Member("teamA"));
em.persist(new Member("teamB"));
em.persist(new Member("teamC"));
List<Member> result = queryFactory
.select(member)
.from(member, team)
.where(member.username.eq(team.name))
.fetch();
//모든 member와 모든 team을 조회 후 한꺼번에 조인 -> where절에서 필터링 진행 -> 이걸 theta join이라고 부름
//물론 db마다 성능 최적화는 진행해줌
assertThat(result)
.extracting("username")
.containsExactly("teamA", "teamB");
}
/**
* 예) 회원과 팀을 조인하면서, 팀 이름이 teamA인 팀만 조인, 회원은 모두 조회
* JPQL : select m, t from Member m left join m.team t on t.name = 'teamA';
*/
@Test
public void join_on_filtering() {
List<Tuple> result = queryFactory
.select(member, team)
.from(member)
.leftJoin(member.team, team)
.on(team.name.eq("teamA"))
.fetch();
for (Tuple tuple : result) {
System.out.println("tuple = " + tuple);
}
}
결과
left join이기 때문에 member data는 다 가져오되 teamA에 소속된 member들은 team data를 가지고 있음 → teamB인 member는 team data가 null
on절로 조인 딱 한다음 and로 team.name에 대한 정보를 조건 조회. 즉 and (team1_.name=?)
요 부분이 추가됨
on 절에을 활용해 조인 대상을 필터링 할 때 외부조인이 아니라 내부조인(inner join)을 사용하면 where 절에서 필터링 하는 것과 기능이 동일
n절을 활용한 조인 대상 필터링을 사용할 때 내부 조인이면 익숙한 where절로 해결, 정말 외부 조인이 필요한 경우에만 이 기능을 사용하자
내부 조인을 쓰면 이렇게 바꿀 수 있음
List<Tuple> result = queryFactory
.select(member, team)
.from(member)
.join(member.team, team)
.where(team.name.eq("teamA"))
.fetch();
//같은 결과를 내놓음
/**
* 연관 관계가 없는 외부 엔티티 조인
* 회원의 이름이 팀 이름과 같은 대상 외부 조인
*/
@Test
public void join_on_no_relation() {
em.persist(new Member("teamA"));
em.persist(new Member("teamB"));
em.persist(new Member("teamC"));
List<Tuple> result = queryFactory
.select(member, team)
.from(member)
.leftJoin(team) //기존에는 leftJoin(member.team, team)으로 썼는데 파라미터로 team 하나만 넘겨줌
.on(member.username.eq("teamA"))
.fetch();
for (Tuple tuple : result) {
System.out.println("tuple : " + tuple);
}
}
leftJoin(member.team, team)
, on조인 : from(member).leftJoin(team).on(조건)
@PersistenceUnit
EntityManagerFactory emf;
@Test
public void fetchJoinNo() {
//페치 조인 시 올바른 결과를 보려면 영속성 컨텍스트를 비워줘야 함
em.flush();
em.clear();
Member findMember = queryFactory
.selectFrom(member)
.where(member.username.eq("member1"))
.fetchOne();
//EntityManagerFactory emf의 isLoaded는 해당 엔티티가 이미 로딩된 엔티티인지, 초기화가 안된 엔티티인지 가르쳐 주는 녀석
boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
assertThat(loaded).as("패치 조인 미적용").isFalse();
}
@Test
public void fetchJoinUse() {
//페치 조인 시 올바른 결과를 보려면 영속성 컨텍스트를 비워줘야 함
em.flush();
em.clear();
Member findMember = queryFactory
.selectFrom(member)
.join(member.team, team).fetchJoin() //join을 해주되 fetchJoin을 싸악 추가하면 됨
.where(member.username.eq("member1"))
.fetchOne();
boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
assertThat(loaded).as("패치 조인 적용").isTrue();
}
com.querydsl.jpa.JPAExpressions
을 사용/**
* 나이가 가장 많은 회원 조회
*/
@Test
public void subQuery() {
//sub query 안에 들어가는 큐타입은 본 쿼리에서 사용하는 큐타입과 겹치면 안되기 때문에 QMember를 새로 하나 만들어줌
//sql에서 alias(별칭)가 곂치면 안되는 거랑 같음
QMember memberSub = new QMember("memberSub");
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.eq(
JPAExpressions
.select(memberSub.age.max())
.from(memberSub)
))
.fetch();
assertThat(result).extracting("age")
.containsExactly(40);
}
/**
* 나이가 평균 이상인 회원 조회
*/
@Test
public void subQueryGoe() {
QMember memberSub = new QMember("memberSub");
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.goe(
JPAExpressions
.select(memberSub.age.avg())
.from(memberSub)
))
.fetch();
assertThat(result).extracting("age")
.containsExactly(30, 40);
}
@Test
public void subQueryIn() {
QMember memberSub = new QMember("memberSub");
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.in(
JPAExpressions
.select(memberSub.age)
.from(memberSub)
.where(memberSub.age.gt(10))
))
.fetch();
assertThat(result).extracting("age")
.containsExactly(20, 30, 40);
}
@Test
public void selectSubQuery() {
QMember memberSub = new QMember("memberSub");
List<Tuple> result = queryFactory
.select(member.username,
select(memberSub.age.avg())
.from(memberSub))
.from(member)
.fetch();
for (Tuple tuple : result) {
System.out.println("tuple : " + tuple);
}
}
@Test
public void basicCase() {
List<String> result = queryFactory
.select(member.age
.when(10).then("10살")
.when(20).then("20살")
.otherwise("기타")
)
.from(member)
.fetch();
for (String s : result) {
System.out.println("s : " + s);
}
}
@Test
public void complexCase() {
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();
for (String s : result) {
System.out.println(" s : " + s);
}
}
@Test
public void constant() {
List<Tuple> result = queryFactory
.select(member.username, Expressions.constant("A"))
.from(member)
.fetch();
for (Tuple tuple : result) {
System.out.println("tuple : " + tuple);
}
}
@Test
public void concat() {
//username_age 구조를 만들어보자
List<String> result = queryFactory
.select(member.username.concat("_").concat(member.age.stringValue()))
.from(member)
.where(member.username.eq("member1"))
.fetch();
for (String s : result) {
System.out.println(" s : " + result);
}
}