아래 세가지 지원 기능은 제약이 커서 복잡한 실무 환경에서의 사용은 선택적으로!
1. 인터페이스 지원 - QuerydslPredicateExecutor
- Pageable, Sort 지원
// 리포지토리에 적용
public interface MemberRepository extends JpaRepository<Member, Long>, CustomMemberRepository, QuerydslPredicateExecutor<Member> {
...
}
// 서비스에서 사용
Iterable result = memberRepository.findAll(
member.age.between(10, 40)
.and(member.username.eq("1")));
- 한계
- 외부 조인 불가능 (묵시적 조인 가능)
- 클라이언트가 Querydsl 구현기술에 의존
2. Querydsl Web 지원
- 한계
- 기본적으로는 eq, contains, in만 가능
- 기능 추가를 위한 커스텀이 복잡하고 명시적이지 않음
- 컨트롤러가 Querydsl에 의존
3. 리포지토리 지원 - QuerydslRepositorySupport
// 추상 클래스 상속
public class MemberRepositoryImpl extends QuerydslRepositorySupport implements CustomMemberRepository {
//private final JPAQueryFactory queryFactory;
public MemberRepositoryImpl(EntityManager em) {
super(Member.class);
//this.queryFactory = new JPAQueryFactory(em);
}
...
}
// 사용
JPQLQuery<MemberTeamDto> jpaQuery = from(member)
.leftJoin(member.team, team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe()))
.select(new QMemberTeamDto(
member.id,
member.username,
member.age,
team.id,
team.name
));
JPQLQuery<MemberTeamDto> resultQuery = getQuerydsl().applyPagination(pageable, jpaQuery);
resultQuery.fetch();
- 장점
- EntityManager 제공
- getQuerydsl().applyPagination() 으로 스프링 데이터가 제공하는 페이징을 Querydsl로 편리하게 변환 가능
- 쿼리에 offset, limit 없애도 됨
- 한계
- Querydsl 3.X 버전을 대상으로 만들어서 Querydsl 4.X에 나온 JPAQueryFactory로 시작 불가능
- 쿼리가 from 부터 시작
- 스프링 데이터 Sort 기능이 정상 작동하지 않음
- Querydsl 3.X 버전을 대상으로 만들어서 Querydsl 4.X에 나온 JPAQueryFactory로 시작 불가능
Querydsl 지원 클래스 직접 만들기
- QuerydslRepositorySupport의 한계를 보완하는 Querydsl 지원 클래스 직접 생성
- Java의 Function을 활용해 람다 함수를 파라미터로 받아 쿼리 수행
- 장점
- 스프링 데이터 페이징 기능을 편리하게 변환
- 페이징과 카운트 쿼리 분리 가능
- 스프링 데이터 Sort 정상 지원
- select(), selectFrom()으로 시작 가능
- EntityManager, JPAQueryFactory 제공
/**
* Querydsl 4.x 버전에 맞춘 Querydsl 지원 라이브러리
*
* @author Younghan Kim
* @see org.springframework.data.jpa.repository.support.QuerydslRepositorySupport
*/
@Repository
public abstract class Querydsl4RepositorySupport {
private final Class domainClass;
private Querydsl querydsl;
private EntityManager entityManager;
private JPAQueryFactory queryFactory;
public Querydsl4RepositorySupport(Class<?> domainClass) {
Assert.notNull(domainClass, "Domain class must not be null!");
this.domainClass = domainClass;
}
@Autowired
public void setEntityManager(EntityManager entityManager) {
Assert.notNull(entityManager, "EntityManager must not be null!");
JpaEntityInformation entityInformation = JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager);
SimpleEntityPathResolver resolver = SimpleEntityPathResolver.INSTANCE;
EntityPath path = resolver.createPath(entityInformation.getJavaType());
this.entityManager = entityManager;
this.querydsl = new Querydsl(entityManager, new PathBuilder<>(path.getType(), path.getMetadata()));
this.queryFactory = new JPAQueryFactory(entityManager);
}
@PostConstruct
public void validate() {
Assert.notNull(entityManager, "EntityManager must not be null!");
Assert.notNull(querydsl, "Querydsl must not be null!");
Assert.notNull(queryFactory, "QueryFactory must not be null!");
}
protected JPAQueryFactory getQueryFactory() {
return queryFactory;
}
protected Querydsl getQuerydsl() {
return querydsl;
}
protected EntityManager getEntityManager() {
return entityManager;
}
protected <T> JPAQuery<T> select(Expression<T> expr) {
return getQueryFactory().select(expr);
}
protected <T> JPAQuery<T> selectFrom(EntityPath<T> from) {
return getQueryFactory().selectFrom(from);
}
protected <T> Page<T> applyPagination(Pageable pageable, Function<JPAQueryFactory, JPAQuery> contentQuery) {
JPAQuery jpaQuery = contentQuery.apply(getQueryFactory());
List<T> content = getQuerydsl().applyPagination(pageable, jpaQuery).fetch();
return PageableExecutionUtils.getPage(content, pageable, jpaQuery::fetchCount);
}
protected <T> Page<T> applyPagination(Pageable pageable,
Function<JPAQueryFactory, JPAQuery> contentQuery, Function<JPAQueryFactory, JPAQuery> countQuery) {
JPAQuery jpaContentQuery = contentQuery.apply(getQueryFactory());
List<T> content = getQuerydsl().applyPagination(pageable, jpaContentQuery).fetch();
JPAQuery countResult = countQuery.apply(getQueryFactory());
return PageableExecutionUtils.getPage(content, pageable, countResult::fetchCount);
}
}
- 지원 추상 클래스 상속
@Repository
public class MemberTestRepository extends Querydsl4RepositorySupport {
public MemberTestRepository() {
super(Member.class);
}
public List<Member> basicSelect() {
return select(member)
.from(member)
.fetch();
}
public List<Member> basicSelectFrom() {
return selectFrom(member)
.fetch();
}
//기존 코드
public Page<Member> searchPageByApplyPage(MemberSearchCondition condition, Pageable pageable) {
JPAQuery<Member> query = selectFrom(member)
.leftJoin(member.team, team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe()));
List<Member> content = getQuerydsl().applyPagination(pageable, query)
.fetch();
return PageableExecutionUtils.getPage(content, pageable, query::fetchCount);
}
//public Page<Member> applySimplePagination(MemberSearchCondition condition, Pageable pageable) {
// return applyPagination(pageable, query -> query
// .selectFrom(member)
// .leftJoin(member.team, team)
// .where(usernameEq(condition.getUsername()),
// teamNameEq(condition.getTeamName()),
// ageGoe(condition.getAgeGoe()),
// ageLoe(condition.getAgeLoe())));
//}
// 커스텀 클래스 적용 코드
public Page<Member> applyComplexPagination(MemberSearchCondition condition, Pageable pageable) {
return applyPagination(pageable, contentQuery -> contentQuery
.selectFrom(member)
.leftJoin(member.team, team)
.where(usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
), countQuery -> countQuery
.selectFrom(member)
.leftJoin(member.team, team)
.where(usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe()))
);
}
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;
}
}
'course > inflearn' 카테고리의 다른 글
[실전! Querydsl] 실무 활용 - 스프링 데이터 JPA와 Querydsl (0) | 2022.11.24 |
---|---|
[실전! Querydsl] 실무 활용 - 순수 JPA와 Querydsl (0) | 2022.11.23 |
[실전! Querydsl] 중급 문법 (0) | 2022.11.23 |
[실전! Querydsl] 기본 문법 (1) | 2022.11.22 |
[JPA 프로그래밍 기본편] JPQL - 중급 문법 (0) | 2022.11.10 |