Querydsl 소개
- 동적 쿼리 문제를 완벽하게 해결한다.
- 쿼리 문장에 오타가 있어도 컴파일 시점에 오류를 막을 수 있다.
- 메서드 추출을 통해 코드를 재사용할 수 있다.
초기 설정
build.gradle 파일에 다음 설정을 추가해 준다.
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:$ {dependencyManagement.importedProperties['querydsl.version']}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api
clean {
file('src/main/generated')
}
자동 생성된 Q 클래스를 gradle clean 으로 제거한다.
Q 타입 생성 확인 방법
build 를 Gradle 로 한 경우 > Gradle 에서 clean 버튼을 누른다. (or `./gradlew clean compileJava`)
그러면 `build/generated/sources/annotationProcessor/java/main` 하위에 `QItem` 생성
build 를 IntelliJ IDEA 로 한 경우 > Build 에서 `Build Project` or `Rebuild` or `main() 실행`
그러면 `src/main/generated` 하위에 `QItem` 생성
Querydsl (+ JPA) 사용 예시
@Repository
@Transactional
public class JpaItemRepositoryV3 implements ItemRepository {
private final EntityManager em;
private final JPAQueryFactory query;
public JpaItemRepository(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);
}
@Override
public List<Item> findAll(ItemSearchCond cond) {
String itemName = cond.getItemName();
Integer maxPrice = cond.getMaxPrice();
List<Item> result = query.select(QItem.item)
.from(QItem.item)
.where(likeItemName(itemName), maxPrice(maxPrice))
.fetch();
return result;
}
private BooleanExpression likeItemName(String itemName) {
if (StringUtils.hasText(itemName)) {
return QItem.item.itemName.like("%" + itemName + "%");
}
return null;
}
private BooleanExpression maxPrice(Integer maxPrice) {
if (maxPrice != null) {
return QItem.item.price.loe(maxPrice);
}
return null;
}
}
- JPAQueryFactory 가 필요한데, 이는 JPA 쿼리인 JPQL 을 만들기 때문에 EntityManager 가 필요하다.
- likeItemName 과 maxPrice 같은 메서드로 사용하기 때문에 다른 쿼리를 작성할 때 재사용할 수 있다.
동적 쿼리
List<Item> result = query
.select(item)
.from(item)
.where(likeItemName(itemName), maxPrice(maxPrice))
.fetch();
- where 에 다양한 조건을 직접 넣을 수 있다.
Querydsl 예외 추상화
스프링 예외 추상화를 지원하지 않는다.
따라서 스프링이 @Repository가 붙은 클래스에 대해 JPA 예외를 스프링 예외 추상화인 DataAccessException 으로 변환해 준다.
1. 스프링과 JPA 를 함께 사용하는 경우 스프링이 JPA 예외 변환기인 PersistenceExceptionTranslator 인터페이스를 자동으로 등록한다.
2. 또한 @Repository 가 붙은 클래스에 AOP 프록시를 생성하고, 예외 변환 어드바이저를 등록하는 PersistenceExceptionTranslationPostProcesser 빈 후처리기를 자동으로 등록한다.
(따라서 단순히 @Repository 만 붙여도 예외 변환이 적용된다.)
3. 예외가 발생하면, AOP 프록시가 해당 예외를 감지해 스프링 예외 추상화로 변환한다.
출처 | 스프링 DB 2(김영한) - 인프런
'💠프로그래밍 언어 > Spring' 카테고리의 다른 글
(Spring Security 기반) OAuth2 vs 일반 로그인 / 세션 vs JWT 정리 (0) | 2025.05.17 |
---|---|
[Spring] 트랜잭션 옵션과 전파에 대하여, 그리고 트랜잭션 주의사항까지 (0) | 2025.05.02 |
[Spring] 스프링 데이터 JPA 사용하기 (0) | 2025.05.02 |
[Spring] JPA 소개 및 사용 예시 (0) | 2025.05.02 |
[Spring] MyBatis 사용법 익히기 ! (1) | 2025.05.01 |