반응형
250x250
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
Tags
- 자바의 정석
- Spring data JPA
- 서브쿼리
- 페치조인
- 정렬
- TCP/IP
- 값 타입
- @MappedSuperclass
- 데이터모델링
- 페이징
- fetch join
- 스프링
- 오라클
- DTO
- INDEX SCAN
- 스프링 컨테이너
- SQL 튜닝
- 스프링 데이터 JPA
- index
- JPQL
- 친절한 SQL 튜닝
- 엔티티
- 데이터베이스
- JPA
- Spring
- 컬렉션 조회 최적화
- 그룹함수
- querydsl
- SQL
- 성능최적화
Archives
- Today
- Total
nu_s
[QueryDSL] 스프링 데이터 JPA가 제공하는 QueryDSL 기능 🦋 본문
728x90
반응형
스프링 데이터 JPA가 제공하는 Querydsl 기능
- 여기서 소개하는 기능은 제약이 커서 복잡한 실무 환경에서 사용하기에는 많이 부족하다.
- 스프링 데이터에서 제공하는 기능이지만 왜 부족한지 알아보자.
1. 인터페이스 지원 - QuerydslPredicateExecutor
QuerydslPredicateExecutor 인터페이스
public interface QuerydslPredicateExecutor<T> {
Optional<T> findById(Predicate predicate);
Iterable<T> findAll(Predicate predicate);
long count(Predicate predicate);
boolean exists(Predicate predicate);
// _more functionality omitted.
}
리포지토리에 적용
interface MemberRepository extends JpaRepository<Member, Long>, QuerydslPredicateExecutor<Member> {}
Iterable result = memberRepository.findAll(
member.age.between(10, 40)
.and(member.username.eq("member1"))
);
한계점
- 조인 X (묵시적 조인은 가능하지만 left join 불가능)
- 클라이언트가 Querydsl에 의존해야 한다. 서비스 클래스가 Querydsl이라는 구현 기술에 의존해야 한다.
- 복잡한 실무환경에서 사용하기에는 한계가 명확하다.
- 참고로 QuerydslPredicateExecutor는 Pageable, Sort를 모두 지원하고 정상 동작한다.
2. Querydsl Web 지원
공식 URL : https://docs.spring.io/spring-data/jpa/docs/2.2.3.RELEASE/reference/html/#core.web.type-safe
한계점
- 단순한 조건만 가능
- 조건을 커스텀하는 기능이 복잡하고 명시적이지 않다.
- 컨트롤러가 Querydsl에 의존한다.
- 복잡한 실무환경에서 사용하기에는 한계가 명확하다.
3. 리포지토리 지원 - QuerydslRepositorySupport
장점
- getQuerydsl().applyPagination() 스프링 데이터가 제공하는 페이징을 Querydsl로 편리하게 변환 가능 (Sort는 오류 발생)
- from()으로 시작 가능하다.
- EntityManager를 제공한다.
한계
- Querydsl 3.X 버전을 대상으로 만듦
- Querydsl 4.X에 나온 JPAQueryFactory로 시작할 수 없다.
- select로 시작할 수 없음 (from 시작)
- QueryFactory를 제공하지 않는다.
- 스프링 데이터 Sort 기능이 정상 동작하지 않는다.
4. Querydsl 지원 클래스 직접 만들기
- 스프링 데이터가 제공하는 QuerydslRepositorySupport가 지닌 한계를 극복하기 위해 직접 Querydsl 지원 클래스를 만들어보자.
장점
- 스프링 데이터가 제공하는 페이징을 편리하게 변환
- 페이징과 카운트 쿼리 분리 가능
- 스프링 데이터 Sort 지원
- select(), selectFrom()으로 시작 가능
- EntityManager, QueryFactory 제공
Querydsl4RepositorySupport
@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);
}
}
Querydsl4RepositorySupport 사용 코드
@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> applyPagenation(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> applyPagenation2(MemberSearchCondition condition, Pageable pageable) {
return applyPagination(pageable,
contentQuery -> contentQuery
.selectFrom(member)
.where(usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
),
countQuery -> countQuery
.select(member.count())
.from(member)
.leftJoin(member.team, team)
.where(usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
)
);
}
private Predicate usernameEq(String username) {
return hasText(username) ? member.username.eq(username) : null;
}
private Predicate teamNameEq(String teamName) {
return hasText(teamName) ? team.name.eq(teamName) : null;
}
private Predicate ageGoe(Integer ageGoe) {
return ageGoe != null ? member.age.goe(ageGoe) : null;
}
private Predicate ageLoe(Integer ageLoe) {
return ageLoe != null ? member.age.loe(ageLoe) : null;
}
}
출처 : 인프런 실전! Querydsl
728x90
반응형
'QueryDSL' 카테고리의 다른 글
[QueryDSL] 스프링 데이터 JPA & QueryDSL 🦋 (0) | 2024.02.17 |
---|---|
[QueryDSL] 순수 JPA & QueryDSL 🦋 (0) | 2024.02.08 |
[QueryDSL] 벌크 연산 & SQL function 🦋 (0) | 2024.02.03 |
[QueryDSL] 동적 쿼리 🦋 (0) | 2024.02.01 |
[QueryDSL] 프로젝션과 결과 반환 🦋 (0) | 2024.01.30 |