스프링/QueryDsl
[QueryDsl] `JPA Repository + QueryDsl`로 동적 쿼리 & API 개발 구축 (Builder or 다중 Where 메서드)
nomoreFt
2022. 3. 21. 16:21
순수 JPA Repository를 구축하여 QueryDsl 을 추가해본다.
QueryDsl을 위한 JPAQueryFactory는 2가지 방법으로 주입 가능하다.
- @Bean으로 등록 후 @Autowired
- 생성자 주입
@Bean
JPAQueryFactory jpaQueryFactory(EntityManager em) {
return new JPAQueryFactory(em);
}
// 1. @Bean으로 등록 후 주입받기
// @Autowired
// JPAQueryFactory queryFactory;
or
// 2. 생성자 주입받기 ( new)
//public MemberJpaRepository(EntityManager em) {
// this.em = em;
// this.queryFactory = new JPAQueryFactory(em);
//}
JPA Repository 구축
일반 JPA이기 때문에 findAll, findById 등등 수동 생성
예시로 findAll만 작성했다.
Team과 Member를 함께 검색하여 받기 위한 검색 Dto 제작 (@Query)
@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;
}
}
@QueryProjection을 선언하면 추후 compile시에 지정된 경로에 QMemberTeamDto 생성자가 생긴다.
다만 class 자체가 QueryDsl에 너무도 종속적이라 추후 QueryDsl을 제거하거나 여러 변수가 있을 때, 문제가 생길 수 있다.
그럼 다른 방법으로 Projection.bean(), fields(), constructor()
중 골라서 쓰면 된다.
@Repository
public class MemberJpaRepository {
private final EntityManager em;
private final JPAQueryFactory queryFactory;
public MemberJpaRepository(EntityManager em) {
this.em = em;
this.queryFactory = new JPAQueryFactory(em);
}
//순수 JPA
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로 검색 기능하는 동적 코드 작성
- 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.as("memberId"),
member.username,
member.age,
team.id.as("teamId"),
team.name.as("teamName")))
.from(member)
.leftJoin(member.team, team)
.where(builder)
.fetch();
}
다중 where 메서드를 사용해서 만들기
public List<MemberTeamDto> search(MemberSearchCondition condition) {
return queryFactory
.select(new QMemberTeamDto(
member.id.as("memberId"),
member.username,
member.age,
team.id.as("teamId"),
team.name.as("teamName")))
.from(member)
.leftJoin(member.team, team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
//null이면 자동으로 where 조건에서 빠진다.
).fetch();
}
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;
}
private BooleanExpression ageBetween(int ageLoe, int ageGoe) {
return ageGoe(ageGoe).and(ageGoe(ageGoe));
}