인터페이스 지원- QuerydslPredicateExecutor
- 앞으로 살펴볼 Spring Data가 제공하는 기능은 제약이 커서 복잡한 실무환경에서 사용하기에는 부족하다.
- 어떤 한계점이 있을까?
Ex) 리포지토리에 적용
- Repo
- QuerydslPredicateExecutor
<hide/>
public interface UserRepository extends JpaRepository<User, Long>, QuerydslPredicateExecutor<User> {
}
Iterable result = userRepository.findAll(
member.age.between(10, 40).and(member.username.eq("member1")));
Note) 한계점
- 묵시적 조인은 가능하나 leftjoin은 불가능하다.
- 클라이언트가 Querydsl에 의존해야한다. 서비스 클래스가 Querydsl에 의존해야한다.
- 따라서, Querydsl는 복잡한 실무에서는 적용하기 한계가 있다.
- cf) QuerydslPredicateExecutor는 Pageable, Sort를 모두 지원하고 정상 동작한다.
Repository 지원 - QuerydslRepositorySupport
장점
- getQuerydsl().applyPagination(): 스프링 데이터가 제공하는 페이징을 Querydsl로 편리하게 변환 가능하지만 Sort는 오류가 발생한다.
한계
- querydsl 3.xx 버전을 대상으로 만든다.
- querydsl 4.xx에 나온 JPAQueryFactory로 시작 불가능
- select로 시작할 수 없으며 from으로 시작한다.
- queryFactory 를 제공하지 않는다.
- Spring data의 Sort기능이 정상 작동하지 않는다.
querydsl 지원 클래스 직접 만들기
- 앞에서 봤듯이 스프링 데이터가 제공하는 QuerydslRepositorySupport는 한계점이 있다.
- 따라서, 이를 극복하기 위해 직접 Querydsl지원 클래스를 만들어보려고한다.
직접 만들었을 때, 장점
- 스프링 데이터가 제공하는 페이징을 편리하게 변환한다.
- 페이징과 카운트 쿼리를 분리 할 수 있다.
- 스프링 데이터 Sort 지원
- select(), selectFrom() 으로 시작이 가능하다.
- EntityManager, QueryFactory 제공한다.
- querydsl 4.x 대 버전에 맞춘 querydsl 지원 라이브러리
<hide/>
@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);
}
}
- 적용하기
- return 문에서 쓰이는 applyPagination은 상속한 Querydsl4RepositorySupport 클래스에 속하는 메서드
- (재귀함수가 아님)
<hide/>
@Repository
public class MemberTestRepository extends Querydsl4RepositorySupport {
public MemberTestRepository(Class<?> domainClass) {
super(Member.class);
}
public List<Member> basicSelect() {
return select(member)
.from(member)
.fetch();
}
public List<Member> basicSelectFrom() {
return selectFrom(member)
.fetch();
}
private BooleanExpression usernameEq(String username) {
return isEmpty(username) ? null : member.username.eq(username);
}
private BooleanExpression teamNameEq(String teamName) {
return isEmpty(teamName) ? null : team.name.eq(teamName);
}
private BooleanExpression ageGoe(Integer ageGoe) {
return ageGoe == null ? null : member.age.goe(ageGoe);
}
private BooleanExpression ageLoe(Integer ageLoe) {
return ageLoe == null ? null : member.age.loe(ageLoe);
}
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> applyPagination(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())
));
}
public Page<Member> applyPagination2(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())));
}
}
출처 - https://www.inflearn.com/course/querydsl-%EC%8B%A4%EC%A0%84
'Spring Framework > [인프런] 실전! QueryDSL' 카테고리의 다른 글
Chapter 05. 실무 활용 - 스프링 데이터 JPA와 Querydsl (2) | 2023.05.08 |
---|---|
Chapter 04. 실무 활용 - 순수 JPA와 Querydsl (1) | 2023.05.06 |
Chapter 03. 중급 문법 (0) | 2023.05.01 |
Chapter 02. 기본 문법 (0) | 2023.04.13 |
Chapter 01. QueryDsl 프로젝트 생성 및 환경 설정 (0) | 2023.04.09 |