기존 JPA방식 Repository
public void save(Member member){
em.persist(member);
}
public Optional<Member> findById(Long id){
Member findMember = em.find(Member.class, id);
return Optional.ofNullable(findMember);
}
public List<Member> findAll(){
return em.createQuery("select m from Member m",Member.class)
.getResultList();
}
public List<Member> findByUserName(String username){
return em.createQuery("select m from Member m where m.username = :username",Member.class)
.setParameter("username",username)
.getResultList();
}
querydsl 방식
public List<Member> findAll_Querydsl(){
return queryFactory
.selectFrom(member)
.fetch();
}
public List<Member> findByUserName_Querydsl(String username){
return queryFactory
.selectFrom(member)
.where(member.username.eq(username))
.fetch();
}
조회를 하기위한 DTO를 생성해준다. 그 후에 querydsl을 사용하기 위해서 QMemberTeamDto를 만들어 준다.@QueryProjection 을 사용하면 해당 DTO가 Querydsl을 의존하게 된다. 이런 의존이 싫으면, 해당 에노테이션을 제거하고, Projection.bean(), fields(), constructor() 을 사용하면 된다.
MemberTeamDto
@Data
public class MemberTeamDto {
private Long memberId;
private String username;
private int age;
private Long teamId;
private String teamName;
@QueryProjection
public MemberTeamDto(Long memberId, String username, int age, Long teamId, String teamName) {
this.memberId = memberId;
this.username = username;
this.age = age;
this.teamId = teamId;
this.teamName = teamName;
}
}
builder방식 조회 동적 쿼리
public List<MemberTeamDto> searchByBuilder(MemberSearchCondition condition){
BooleanBuilder builder = new BooleanBuilder();
if (hasText(condition.getUsername())) {
builder.and(member.username.eq(condition.getUsername()));
}
if(hasText(condition.getTeamName())){
builder.and(team.name.eq(condition.getTeamName()));
}
if(condition.getAgeGoe() != null) {
builder.and(member.age.goe(condition.getAgeGoe()));
}
if (condition.getAgeLoe() != null) {
builder.and(member.age.loe(condition.getAgeLoe()));
}
return queryFactory
.select(new QMemberTeamDto(
member.id,
member.username,
member.age,
team.id,
team.name
))
.from(member)
.leftJoin(member.team, team)
.where(builder)
.fetch();
}
where문에 파라미터를 사용한 예제 : 이름과 팀명, 그리고 나이를 ageGoe보다 크고 ageLoe보다 낮은 회원 검색. 정보가 null일 수도 있다.
파라미터를 이용한 조회 동적 쿼리
public List<MemberTeamDto> search(MemberSearchCondition condition){
return queryFactory
.select(new QMemberTeamDto(
member.id,
member.username,
member.age,
team.id,
team.name
))
.from(member)
.leftJoin(member.team, team)
.where(usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
)
.fetch();
}
usernameEq,teamNameEq,ageGoe,ageLoe 파라미터 메서드
private BooleanExpression usernameEq(String username) {
return hasText(username) ? member.username.eq(username) : null;
}
private BooleanExpression teamNameEq(String teamName) {
return hasText(teamName) ? team.name.eq(teamName) : null;
}
private BooleanExpression ageGoe(Integer ageGoe) {
return ageGoe != null ? member.age.goe(ageGoe) : null;
}
private BooleanExpression ageLoe(Integer ageLoe) {
return ageLoe != null ? member.age.loe(ageLoe) : null;
}
이런식으로 파라미터 병합도 가능하다.
public List<Member> searchMember(MemberSearchCondition condition){
return queryFactory
.selectFrom(member)
.from(member)
.leftJoin(member.team, team)
.where(usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageBetween(condition.getAgeLoe(),condition.getAgeLoe()))
.fetch();
}
병합 메서드
private BooleanExpression ageBetween(int ageLoe, int ageGoe){
return ageGoe(ageLoe).and(ageGoe(ageGoe));
}
샘플 데이터 추가
@Profile("local")
@Component
@RequiredArgsConstructor
public class initMember {
private final InitMemberService initMemberService;
@PostConstruct
public void init(){
initMemberService.init();
}
@Component
static class InitMemberService{
@PersistenceContext
private EntityManager em;
@Transactional
public void init(){
Team teamA = new Team("teamA");
Team teamB = new Team("teamB");
em.persist(teamA);
em.persist(teamB);
for(int i=0; i<100; i++){
Team selectedTeam = i%2 ==0 ? teamA : teamB;
em.persist(new Member("member"+1,i,selectedTeam));
}
}
}
}
동적 쿼리 조회 컨트롤러 및 테스트 결과
@GetMapping("/v1/members")
public List<MemberTeamDto> searchMemberV1(MemberSearchCondition condition) {
return memberJpaRepository.search(condition);
}
api test툴 postman으로 조회를 해보면 teamB이면서 31살보다 많고 40살보다 적은 조건(파라미터)을 추가했을때 결과가 잘 나온다