이전에 BooleanBuiler와 where 절 을 통해 동적쿼리를 만드는 방법을 알아 봤었는데
실제 repository에서 동적 쿼리와 Dto 조회를 함께 사용하는 방법을 알아보자
일단 두가지 방법에 공통적으로 사용될 검색 키워드를 담을 SearchCond 라는 Dto와
반환되는 데이터를 담을 MemberTeamDto를 만들었다
MemberTeamDto
@Data
public class MemberTeamDto{
private Long memberId;
private int age;
private String username;
private String teamName;
@QueryProjection
public MemberTeamDto(Long memberId, int age, String username, String teamName) {
this.memberId = memberId;
this.age = age;
this.username = username;
this.teamName = teamName;
}
}
- @QueryProjection을 통해서 Dto 조회를 하려한다 . Dto 생성후 gradle에서 compileQueryDsl로 Q타입 객체가 생성되 도록 해야한다 .
SearchCond
package com.querydsl.study.dto;
import lombok.Data;
@Data
public class SearchCond {
private String username;
private String teamName;
private Integer ageLoe;
private Integer ageGoe;
}
-검색 조건을 담는 Dto이다 각 필드가 조건의 파라미터가 된다 . 만약 필드에 값이 null 이라면 해당 조건은 무시된다.
1. BooleanBuilder 를 통한 동적 쿼리
MemberJpaRepository
public List<MemberTeamDto> findByCondition(SearchCond cond){
BooleanBuilder builder = new BooleanBuilder();
if(cond.getUsername() != null){
builder.and(member.username.eq(cond.getUsername()));
}
if(cond.getTeamName() != null){
builder.and(team.name.eq(cond.getTeamName()));
}
if (cond.getAgeGoe() != null){
builder.and(member.age.goe(cond.getAgeGoe()));
}
if (cond.getAgeLoe() != null){
builder.and(member.age.loe(cond.getAgeLoe()));
}
return queryFactory.select(
new QMemberTeamDto(
member.id.as("memberId"),
member.age,
member.username,
team.name.as("teamName")
))
.from(member)
.leftJoin(member.team , team)
.where(builder)
.fetch();
}
BooleanBuilder 생성 후에 조건이 존재하는지 체크해주고 있다면 해당 조건을 builder에 쌓는다
where 절에 만들어진 builder를 넣어주면 된다.
테스트 해보자
@BeforeEach
public void initDB(){
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
void searchByBuilder(){
SearchCond searchCond = new SearchCond();
searchCond.setAgeGoe(40);
searchCond.setAgeLoe(40);
searchCond.setUsername("member4");
searchCond.setTeamName("teamB");
List<MemberTeamDto> byCondition = memberJpaRepository.findByCondition(searchCond);
for (MemberTeamDto memberTeamDto : byCondition) {
System.out.println("memberTeamDto = " + memberTeamDto);
}
assertThat(byCondition.size()).isEqualTo(1);
assertThat(byCondition).extracting("username").containsExactly("member4");
}
먼저 @BeforeEach로 test데이터가 채워 지도록했다.
SearchCond를 생성하여 검색조건을 채우고 findByCondition의 인자로 넘기겼다.
검색조건은 40살 이상 40살이하 이름이 member4이고 소속팀의 이름이 teamB인 멤버를 찾는다.
반환되는 것을 출력하고
검색되는 리스트의 크기와 , 유저이름을 추출해 예상하는 멤버(member4)를 포함하는지 테스트 해봤다.
테스트는 통과 되었고
출력되는 쿼리와 출력된 데이터를 살펴보자
//JPQL
/* select
member1.id as memberId,
member1.age,
member1.username,
team.name as teamName
from
Member member1
left join
member1.team as team
where
member1.username = ?1
and team.name = ?2
and member1.age >= ?3
and member1.age <= ?3 */
//SQL
select
member0_.member_id as col_0_0_,
member0_.age as col_1_0_,
member0_.username as col_2_0_,
team1_.name as col_3_0_
from
member member0_
left outer join
team team1_
on member0_.team_id=team1_.team_id
where
member0_.username=?
and team1_.name=?
and member0_.age>=?
and member0_.age<=?
//검색 데이터
memberTeamDto = MemberTeamDto(memberId=6, age=40, username=member4, teamName=teamB)
검색 조건이 잘들어가 JPQL과 SQL 이 나갔다 . 검색데이터도 검색조건에 맞는 데이터가 출력됬다 .
2. where 절 파라미터를 통한 동적쿼리
MemberJpaRepository
public List<MemberTeamDto> searchByWhere(SearchCond cond){
return queryFactory.select(
new QMemberTeamDto(
member.id.as("memberId"),
member.age,
member.username,
team.name.as("teamName")
))
.from(member)
.leftJoin(member.team , team)
.where(
usernameEq(cond.getUsername()),
teamNameEq(cond.getTeamName()),
ageGoe(cond.getAgeGoe()),
ageLoe(cond.getAgeLoe())
)
.fetch();
}
private BooleanExpression ageLoe(Integer ageLoe) {
return ageLoe != null ? member.age.loe(ageLoe) : null;
}
private BooleanExpression ageGoe(Integer ageGoe) {
return ageGoe != null ? member.age.goe(ageGoe) : null;
}
private BooleanExpression teamNameEq(String teamName) {
return hasText(teamName) ? team.name.eq(teamName) : null;
}
private BooleanExpression usernameEq(String username) {
return hasText(username) ? member.username.eq(username) : null;
}
private BooleanExpression ageBetween(Integer ageGoe, Integer ageLoe){
if (ageGoe == null || ageLoe == null){
return null;
}
return member.age.goe(ageGoe).and(member.age.loe(ageLoe));
}
Builder로 만든 방식과 다르게 where 절에 직접 만든 메서드를 통해 컨디션을 추가한다 .
메서드에서는 컨디션의 null 여부를 따져 검색조건을 반환한다.
BooleanExpression 으로 리턴타입을 하면 ageBetween 처럼 여러개의 메서드를 조합하여 사용할 수도 있다 .
이떄 해당 파라미터에 대한 null 체크가 필요하다 .
'JPA > QueryDSL' 카테고리의 다른 글
QueryDSL 공부 11 - Spring data JPA 와 Querydsl 페이징 연동 (1) | 2021.06.20 |
---|---|
QueryDSL 공부 10 - Spring data JPA 에서 QueryDsl활용 (0) | 2021.06.20 |
QueryDSL 공부 8 - Repository에서 활용 (0) | 2021.06.20 |
QueryDSL 공부 7 - 수정 , 삭제 벌크성 쿼리 (0) | 2021.06.19 |
QueryDSL 공부 6 - 동적쿼리 (booleanBuilder) (0) | 2021.06.19 |