지금 소개할 기능들은 제약이 크기 때문에 복잡한 실무 환경에는 부적합하다.
그래도 스프링 데이터에서 제공하는 기능이니 알고 지나가자.
QuerydslPredicateExecutor
이란 인터페이스를 지원하고 있다.
🔗 [공식 URL](https://docs.spring.io/spring-data/jpa/docs/2.2.3.RELEASE/reference/html/ #core.extensions.querydsl)
✔️ MemberRepository.java
public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom, QuerydslPredicateExecutor<Member> {
...
QMember member = QMember.member;
Iterable<Member> result = memberRepository.findAll(
member.age.between(10, 40)
.and(member.username.eq("member1"))
);
(나머지 코드는 위의 코드 멤버 추가하는 코드)
➡️ 복잡한 실무 환경에서 사용하기에는 한계가 명확하다.
⚠️ QuerydslPredicateExecutor
는 Pageable
, Sort
를 모두 지원하고 정상 동작한다.
(하지만 위의 한계점 때문에 실무에서는 개인적으로 권장하지 않는 방법)
🔗 [공식 URL](https://docs.spring.io/spring-data/jpa/docs/2.2.3.RELEASE/reference/html/ #core.web.type-safe)
코드가 한줄로 엄청 간결해질 수는 있지만 한계점 때문에 실무에서는 정작 사용할 순 없는,,
getQuerydsl().applyPagination()
스프링 데이터가 제공하는 페이징을 Querydsl로 편리하게 변환 가능(단!! Sort는 오류 발생!!)from()
으로 시작 가능 (최근에는 QueryFactory
를 사용해 select()로 시작하는 것이 더 명시적): 스프링 데이터가 제공하는 QuerydslRepositorySupport
가 지닌 한계를 극복하기 위해 직접 Querydsl 지원 클래스를 만들기
select()
, selectFrom()
으로 시작 가능EntityManager
, QueryFactory
제공✔️ Querydsl4RepositorySupport.java
/**
* 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);
}
}
✔️ MemberTestRepository.java
@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();
}
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); // query::fetchCount -> totalCount
}
public Page<Member> applyPagination(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> 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())
), countQuery -> countQuery
.select(member.id)
.from(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;
}
}