[SpringDB2] 데이터 접근 기술 - Querydsl
Querydsl 설정
build.gradle
...
//Querydsl 추가
implementation 'com.querydsl:querydsl-jpa'
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
...
//Querydsl 추가, 자동 생성된 Q클래스 gradle clean으로 제거
clean {
delete file('src/main/generated')
}
Q타입 생성
Gradle - Q타입 생성
Gradle -> Tasks -> build -> clean
Gradle -> Tasks -> other -> compileJava
Q타입 생성 확인
build -> generated -> sources -> annotationProcessor -> java/main
하위에 Q 타입이 생성되어 있어야 한다.
[참고]
Q타입은 컴파일 시점에 자동 생성되므로 버전관리(GIT)에 포함되지 않는 것이 좋다.
gradle 옵션을 선택하면 Q타입은gradle build
폴더 아래에 생성되기 때문에 여기를 포함하지 않아야 한다. 대부분gradle build
폴더를 git에 포함하지 않기 때문에 이 부분은 자연스럽게 해결된다.
IntelliJ IDEA - Q타입 생성
Build -> Build Project
또는
Build -> Rebuild
또는
main()
, 또는 테스트를 실행하면 된다.
src/main/generated
하위에 Q타입이 생성되어야 한다.
[참고]
IntelliJ IDEA 옵션을 선택하면 Q타입은src/main/generated
폴더 아래에 생성되기 때문에 버전관리에 이곳은 포함하지 않는 것이 좋다.
Querydsl 적용
JpaItemRepositoryV3
@Repository
@Transactional
public class JpaItemRepositoryV3 implements ItemRepository {
private final EntityManager em;
private final JPAQueryFactory query;
public JpaItemRepositoryV3(EntityManager em) {
this.em = em;
this.query = new JPAQueryFactory(em);
}
@Override
public Item save(Item item) {
em.persist(item);
return item;
}
@Override
public void update(Long itemId, ItemUpdateDto updateParam) {
Item findItem = findById(itemId).orElseThrow();
findItem.setItemName(updateParam.getItemName());
findItem.setPrice(updateParam.getPrice());
findItem.setQuantity(updateParam.getQuantity());
}
@Override
public Optional<Item> findById(Long id) {
Item item = em.find(Item.class, id);
return Optional.ofNullable(item);
}
public List<Item> findAllOld(ItemSearchCond itemSearch) {
String itemName = itemSearch.getItemName();
Integer maxPrice = itemSearch.getMaxPrice();
QItem item = QItem.item;
BooleanBuilder builder = new BooleanBuilder();
if (StringUtils.hasText(itemName)) {
builder.and(item.itemName.like("%" + itemName + "%"));
}
if (maxPrice != null) {
builder.and(item.price.loe(maxPrice));
}
List<Item> result = query
.select(item)
.from(item)
.where(builder)
.fetch();
return result;
}
@Override
public List<Item> findAll(ItemSearchCond cond) {
String itemName = cond.getItemName();
Integer maxPrice = cond.getMaxPrice();
List<Item> result = query
.select(item)
.from(item)
.where(likeItemName(itemName), maxPrice(maxPrice))
.fetch();
return result;
}
private BooleanExpression likeItemName(String itemName) {
if (StringUtils.hasText(itemName)) {
return item.itemName.like("%" + itemName + "%");
}
return null;
}
private BooleanExpression maxPrice(Integer maxPrice) {
if (maxPrice != null) {
return item.price.loe(maxPrice);
}
return null;
}
}
공통
- Querydsl을 사용하려면
JPAQueryFactory
가 필요하다.JPAQueryFactory
는 JPA 쿼리인 JPQL을 만들기 때문에EntityManager
가 필요하다. - 설정 방식은
JdbcTemplate
을 설정하는 것과 유사하다. - 참고로
JPAQueryFactory
를 스프링 빈으로 등록해서 사용해도 된다.
findAllOld
Querydsl을 사용해서 동적 쿼리 문제를 해결한다.
BooleanBuilder
를 사용해서 원하는 where
조건을 넣어주면 된다.
findAll
앞서 findAllOld
에서 작성한 코드를 리팩토링 했다.
List<Item> result = query
.select(item)
.from(item)
.where(likeItemName(itemName), maxPrice(maxPrice))
.fetch();
- Querydsl에서
where(A,B)
에 다양한 조건들을 직접 넣을 수 있는데, 이렇게 넣으면 AND 조건으로 처리된다. 참고로where()
에null
을 입력하면 해당 조건은 무시한다. - 이 코드의 또 다른 장점은
likeItemName()
,maxPrice()
를 다른 쿼리에서 재사용 할 수 있다는 점이다.(쿼리 조건을 부분적으로 모듈화)
정리
Querydsl 장점
- Querydsl 덕분에 동적 쿼리를 매우 깔끔하게 사용할 수 있다.
- 쿼리 문장에 오타가 있어도 컴파일 시점에 오류를 막을 수 있다.
- 메서드 추출을 통해서 코드를 재사용할 수 있다.
댓글남기기