@SpringBootTest
@Transactional
public class QuerydslBasicTest {
/**
* 팀A에 소속된 모든 회원
*/
@Test
public void join() throws Exception {
QMember member = QMember.member;
QTeam team = QTeam.team;
List<Member> result = queryFactory
.selectFrom(member)
// 멤버와 팀을 조인한다.
.join(member.team, team)
.where(team.name.eq("teamA"))
.fetch();
assertThat(result).extracting("username")
}
}
join(조인 대상, 별칭으로 사용할 Q타입)
select member1
from Member member1
inner join
member1.team as team
where team.name = ?1
join(), innerJoin()
inner join
leftJoin()
left outer join
rightJoin()
right outer join
JPQL의 on과 성능 최적화를 위한 fetch join도 제공한다.
세타 조인
연관 관계가 없는 필드로 조인한다.
@SpringBootTest
@Transactional
public class QuerydslBasicTest {
/**
* 세타 조인(연관 관계가 없는 필드로 조인)
* 회원의 이름이 팀 이름과 같은 회원 조회
*/
@Test
public void theta_join() throws Exception {
em.persist(new Member("teamA"));
em.persist(new Member("teamB"));
List<Member> result = queryFactory
.select(member)
// 일반 조인은 멤버와 연관이 있는 팀을 지정했지만
// 세타 조인은 그냥 필요한 엔티티를 나열한다.
.from(member, team)
.where(member.username.eq(team.name))
.fetch();
assertThat(result)
.extracting("username")
.containsExactly("teamA", "teamB");
}
}
from에 여러 엔티티를 선택해서 세타 조인 한다.
outer join은 불가하다.
on을 사용하면 outer join이 가능해진다.
on절
조인 대상 필터링
@SpringBootTest
@Transactional
public class QuerydslBasicTest {
@Test
public void join_on_filtering() throws Exception {
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);
}
}
}
SELECT m.*, t.*
FROM Member m
LEFT JOIN Team t ON m.TEAM_ID = t.id and
t.name = 'teamA'
fetch join은 SQL이 제공하는건 아니고 SQL 조인을 활용해 한 방에 조회하는 기능이다.
성능 최적화에서 주로 사용한다.
before
@SpringBootTest
@Transactional
public class QuerydslBasicTest {
@PersistenceUnit
EntityManagerFactory emf;
@Test
public void fetchJoinNo() throws Exception {
em.flush();
em.clear();
// team이 지연 로딩이기 때문에 데이터가 비어있다.
Member findMember = queryFactory
.selectFrom(member)
.where(member.username.eq("member1"))
.fetchOne();
// 해당 데이터가 이미 로딩된 엔티티인지 확인한다.
boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
assertThat(loaded).as("페치 조인 미적용").isFalse();
}
}
select member1
from Member member1
where member1.username = ?1
after
@SpringBootTest
@Transactional
public class QuerydslBasicTest {
@Test
public void fetchJoinUse() throws Exception {
em.flush();
em.clear();
Member findMember = queryFactory
.selectFrom(member)
.join(member.team, team)
// fetch join 적용
.fetchJoin()
.where(member.username.eq("member1"))
.fetchOne();
// 연관 관계가 같이 로딩된다.
boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
assertThat(loaded).as("페치 조인 적용").isTrue();
}
}
select member0_.member_id as member_i1_1_0_,
team1_.team_id as team_id1_2_1_,
member0_.age as age2_1_0_,
member0_.team_id as team_id4_1_0_,
member0_.username as username3_1_0_,
team1_.name as name2_2_1_
from member member0_
inner join
team team1_ on member0_.team_id = team1_.team_id
where member0_.username = ?