Querydsl

김민지·2022년 11월 15일
0

JPA

목록 보기
19/27
post-custom-banner

스프링 데이터 JPA가 제공하는 Querydsl 기능

QuerydslPredicateExecutor사용

  • QuerydslPredicateExecutor을 상속받으면
    중간에 predicate(조건)를 통해서 결과를 필터링하는게 가능하다
  • but 조인이 안됨
  • repository에 predicate 파라미터를 넘겨줘야한다 -> 강한결합(강한 의존성)이 일어남

Querydsl Web 지원

  • Querydsl Web 지원은 컨트롤러 레벨에서 Predicate 를 받을 수 있도록 QuerydslPredicateArgumentResolver 룰 지원해준다.

동적쿼리는 BooleanExpression 사용

BooleanBuilder의 단점

  • 쿼리예측하기 어려움

where절의 장점

  • 재사용가능
  • 가독성 좋음

exist 메소드 사용하지 않기

exist는 언제써?

댓글에 특정 단어가 있는 게시물을 검색하는 쿼리이다.

FROM Board A
WHERE EXISTS (SELECT 1 FROM Comment WHERE no = A.no AND content LIKE '%내용%');

Cross Join 회피하기

  • 묵시적 조인하면 크로스 조인이 나가게 됨 -> 필요없는거까지 조회하기때문에 성능상 단점이 있음

cross join 발생 예제

queryFactory
            .selectFrom(member)
            .where(member.team.id.gt(member.team.leader.id))
            .fetch();

조회할땐 Entity 보다는 Dto 를 우선적으로 가져오기

entity로 조회해올때의 단점

  • 영속성 컨택스트의 1차 캐시 기능을 사용(업데이트되지 않았을 수도 있어서 단점인가?)
  • 불필요한 칼럼을 조회

이해하기

@Transactional(readOnly = true)
public List<AdBond> createAdBond(LocalDate startDate, LocalDate endDate, List<String> orderTypes) {
    return queryFactory
            .select(Projections.fields(AdBond.class,
                    adItem.amount.sum().as("amount"),
                    adItem.txDate,
                    adItem.orderType,
                    adItem.customer) // AdBond의 customer 를 바로 지정
            )
            .from(adItem)
            .where(adItem.orderType.in(orderTypes)
                    .and(adItem.txDate.between(startDate, endDate)))
            .groupBy(adItem.orderType, adItem.txDate, adItem.customer)
            .fetch();
}
  • 이상황에서 customer에 접근했기때문에 묵시적조인을하게된다
  • 그런데 어차피 나는 customer의 id만 필요해서 조인이 필요가 없다
  • 아래와 같이 코드를 바꾸자
  • 그러면 훨씬 성능좋은 쿼리를 짤 수 있다


1. entity -> dto로 변경
2. customer객체 대신 customer.id 로 변경

근데 문제가 하나 더있다. 이렇게 dto를 만들고 나서 이 dto에 대한 entity를 다시 저장해주어야한다!
가장 쉬운방법은 다음과 같이 매번 조회하는거겠지만..

@Transactional
public void createAdBond2 (LocalDate startDate, LocalDate endDate, List<String> orderTypes) {
    List<AdBondDto> adBondDtos = queryRepository.createAdBondDto(startDate, endDate, orderTypes);
    List<AdBond> adBonds = new ArrayList<>();
    for (AdBondDto dto : adBondDtos) {
        Customer customer = customerRepository.findById(dto.getCustomerId()).get(); // customerId로 Customer Entity 조회 후,

        adBonds.add(AdBond.builder()
                .amount(dto.getAmount())
                .txDate(dto.getTxDate())
                .orderType(dto.getOrderType())
                .customer(customer) // AdBond Entity와 관계를 맺음
                .build());
    }

    adBondRepository.saveAll(adBonds);
}

이러면 쿼리성능향상을 위해 쿼리가 더 나가게된다
그럼 어떻게 하면 좋을까?
결국 AdBond의 테이블 에는 Customer의 Id만 들어가게 되는데요. 신규 등록하는 AdBond 에서는 ID만 가진 Customer Entity와 관계만 맺기만 하면 그 목적을 달성할 수 있습니다.

@Transactional
public void createAdBond3 (LocalDate startDate, LocalDate endDate, List<String> orderTypes) {
    List<AdBondDto> adBondDtos = queryRepository.createAdBondDto(startDate, endDate, orderTypes);
    List<AdBond> adBonds = adBondDtos.stream()
            .map(adBondDto -> AdBond.builder()
                    .amount(adBondDto.getAmount())
                    .txDate(adBondDto.getTxDate())
                    .orderType(adBondDto.getOrderType())
                    .customer(new Customer(adBondDto.getCustomerId())) // customerId만 가진 Customer Entity를 사용
                    .build())
            .collect(Collectors.toList());

    adBondRepository.saveAll(adBonds);
}
@Getter
@NoArgsConstructor
public class AdBondDto {
    ...

    public AdBond toEntity() {
        return AdBond.builder()
                .amount(amount)
                .txDate(txDate)
                .orderType(orderType)
                .customer(new Customer(customerId))
                .build();
    }
}
@Transactional
public void createAdBond4 (LocalDate startDate, LocalDate endDate, List<String> orderTypes) {
    List<AdBondDto> adBondDtos = queryRepository.createAdBondDto(startDate, endDate, orderTypes);
    List<AdBond> adBonds = adBondDtos.stream()
            .map(AdBondDto::toEntity) // dto.toEntity로 한번에 해결
            .collect(Collectors.toList());

    adBondRepository.saveAll(adBonds);
}

출처
https://github.com/Youngerjesus/Querydsl/blob/master/docs/woowahwan.md
https://jojoldu.tistory.com/518

profile
안녕하세요!
post-custom-banner

0개의 댓글