기존의 QueryDsl을 편하게 사용하기 위해 구현된 QuerydslRepositorySupport
를, 직접 구현하여 더 강력한 기능을 가진 Querydsl4RepositorySupport
를 제작하자.
QuerydslRepositorySupport 기본 abstract 클래스란?
@Repository
public abstract class Querydsl4RepositorySupport
- Impl 객체들에 QueryDsl 사용을 서포팅 해주는
추상클래스
이다.
QuerydslRepositorySupport 기본 적용법
public class MemberTestRepository extends QuerydslRepositorySupport {
public MemberTestRepository() {
super(Member.class);
}
}
- 구현체에 extends QuerydslRepositorySupport 추가
- super(알맞은.class) 해주기
QuerydslRepositorySupport 개선하여 추상 클래스 직접 만들기
- 기존 QuerydslRepositorySupport -> custom한 abstract class 개선점
- Paging을 편리하게 변환 (offset,limit 체인을 제거)
- Paging, count 쿼리 분리 (람다로 그냥 연속적으로 chain으로 받게 함)
- 스프링 데이터 Sort 사용가능 (기존에 안되는거)
- from() 시작문을 -> select(), selectFrom() 시작문으로 변경
- QueryFactory는 빠져서 추가로 의존성 주입 받던거 기본 지원으로 수정
Querydsl4RepositorySupport 분석
전체 코드
@Repository
public abstract class Querydsl4RepositorySupport {
private final Class domainClass;
private Querydsl querydsl;
private EntityManager entityManager;
private JPAQueryFactory queryFactory;
//1. 도메인 class 받기 (ex)Member.class)
public Querydsl4RepositorySupport(Class<?> domainClass) {
Assert.notNull(domainClass, "Domain class must not be null!");
this.domainClass = domainClass;
}
//2. EntityManager 주입받기
@Autowired
public void setEntityManager(EntityManager entityManager) {
Assert.notNull(entityManager, "EntityManager must not be null!");
//2-1. Path를 잡아야 Sort 명령시에 오류가 안난다.
JpaEntityInformation entityInformation =
JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager);
SimpleEntityPathResolver resolver = SimpleEntityPathResolver.INSTANCE;
EntityPath path = resolver.createPath(entityInformation.getJavaType());
//2-2. entityManager 주입 받은 것으로 querydsl, queryFactory 생성
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!");
}
//3. getter로 가져다 쓸 순 있지만, 이후에 그냥 queryFactory를 호출된 형태의 method를 가져다 쓸 수 있게 해준다.
protected JPAQueryFactory getQueryFactory() {
return queryFactory;
}
protected Querydsl getQuerydsl() {
return querydsl;
}
protected EntityManager getEntityManager() {
return entityManager;
}
사용법과 코드 분석
- 기존 from() -> select(),selectFrom() 변경, + queryFactory 생략
- queryFactory.select() -> select()로 간소화 적용 모습
사용 모습
public List<Member> basicSelect() {
return select(member)
.from(member)
.fetch();
}
코드 분석
이미 내부에서 queryFactory를 불러서 select(표현식) 받아 처리하는 모습
protected <T> JPAQuery<T> select(Expression<T> expr) {
return getQueryFactory().select(expr);
}
protected <T> JPAQuery<T> selectFrom(EntityPath<T> from) {
return getQueryFactory().selectFrom(from);
}
- Paging을 편리하게 변환 (offset,limit 체인을 제거)
- Paging, count 쿼리 분리 (람다로 그냥 연속적으로 chain으로 받게 함)
사용 모습
// count, content 분리
public Page<Member> applyPagination2(MemberSearchCondition condition, Pageable pageable) {
return applyPagination(pageable, countQuery ->countQuery
.selectFrom(member)
.leftJoin(member.team,team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
)//content 쿼리
,countQuery -> countQuery
.select(member.count())
.from(member)
.leftJoin(member.team,team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
)//count 쿼리
);
}
코드 분석
- fetchResult, fetchCount 등이 사장되어서 그냥 fetchOne 버전으로 countquery까지 chain으로 받는식으로 수정했다.
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<Long> countResult = countQuery.apply(getQueryFactory());
return org.springframework.data.support.PageableExecutionUtils.getPage(content, pageable,
countResult::fetchOne);
}
출처
실전! Querydsl - 김영한 강사님
https://www.inflearn.com/course/Querydsl-%EC%8B%A4%EC%A0%84