- 스프링 db 2편(데이터 접근 활용 기술) 시작
- 스프링 db 2편(데이터 접근 활용 기술)(2) 스프링 jdbctemplate
- 스프링 db 2편(데이터 접근 활용 기술)(3) 테스트
- 스프링 db 2편(데이터 접근 활용 기술)(4) mybatis
- 스프링 db 2편(데이터 접근 활용 기술) jpa
- 스프링 db 2편(데이터 접근 활용 기술)(2) 스프링 데이터 jpa
- 스프링 db 2편(데이터 접근 활용 기술)(3) querydsl
- 스프링 db 2편(데이터 접근 활용 기술)(4) 활용 방안
- 스프링 db 2편(데이터 접근 활용 기술)(5) 스프링 트랜잭션
- 스프링 db 2편(데이터 접근 활용 기술) 스프링 트랜잭션 전파 1 기본
- 스프링 db 2편(데이터 접근 활용 기술) 스프링 트랜잭션 전파 2 활용
스프링 db 2편(데이터 접근 활용 기술)(3) querydsl
0. 이전 데이터 접근 기술의 문제점 / Querydsl 소개
- ppt 자료 참고(Querydsl_ppt)
1. Querydsl 설정
build.gradle
-
dependencies { //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 타입 생성 확인 방법
Build Tools > Gradle 에서 Gradle 사용 시
- 각각 clean, compile
- Gradle 콘솔 사용법 :
./gradlew clean compileJava
-
Q 타입 생성 확인 : build -> generated -> sources -> annotationProcessor -> java/main 하위에 hello.itemservice.domain.QItem 이 생성되어 있어야 한다.
Q타입은 컴파일 시점에 자동 생성되므로 버전관리(GIT)에 포함하지 않는 것이 좋다. > gradle 옵션을 선택하면 Q타입은 gradle build 폴더 아래에 생성되기 때문에 여기를 포함하지 않아야 한다. 대부분 gradle build 폴더를 git에 포함하지 않기 때문에 이 부분은 자연스럽게 해결된다.
Build Tools > Gradle 에서 Intellij 사용 시
- 메인 클래스 실행 시 src > main > generated 에 hello.itemservice.domain.QItem 생성
- IntelliJ IDEA 옵션을 선택하면 src/main/generated 에 파일이 생성되고, 필요한 경우 Q파일을 직접 삭제해야 한다.
- gradle 에 해당 스크립트를 추가하면 gradle clean 명령어를 실행할 때 src/main/generated 의 파일도 함께 삭제해준다.
-
삭제는 Gradle 설정과 같음
Q타입은 컴파일 시점에 자동 생성되므로 버전관리(GIT)에 포함하지 않는 것이 좋다. > IntelliJ IDEA 옵션을 선택하면 Q타입은 src/main/generated 폴더 아래에 생성되기 때문에 여기를 포함하지 않는 것이 좋다.
2. Querydsl 적용
JpaItemRepositoryV3
-
package hello.itemservice.repository.jpa; import ...; import static hello.itemservice.domain.QItem.*; @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 = em.find(Item.class, itemId); 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(); //내부적으로 하나 가지고 있음 // QItem item = new QItem("i"); // QItem.item; //static 이라서 item 으로 변경가능 BooleanBuilder builder = new BooleanBuilder(); 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 Predicate maxPrice(Integer maxPrice){ if (maxPrice != null) { return item.price.loe(maxPrice); } return null; } }
- Querydsl을 사용하려면 JPAQueryFactory 가 필요하다. JPAQueryFactory 는 JPA 쿼리인 JPQL을 만들기 때문에 EntityManager 가 필요하다.
- 설정 방식은 JdbcTemplate 을 설정하는 것과 유사하다.
- 참고로 JPAQueryFactory 를 스프링 빈으로 등록해서 사용해도 된다.
findAll
-
@Override public List<Item> findAll(ItemSearchCond cond) { String itemName = cond.getItemName(); Integer maxPrice = cond.getMaxPrice(); //내부적으로 하나 가지고 있음 // QItem item = new QItem("i"); // QItem.item; //static 이라서 item 으로 변경가능 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 Predicate maxPrice(Integer maxPrice){ if (maxPrice != null) { return item.price.loe(maxPrice); } return null; }
-
QItem item = new QItem(“i”); 으로 QItem 을 선언해줘야 하지만 내부적으로 이미 만들어져있다.
-
public class QItem extends EntityPathBase<Item> { public static final QItem item = new QItem("item"); }
- 그래서 그냥 QItem.item 이라고 써도 되지만 Static import 로 해서 item 이라고만 쓸 수도 있다.
-
-
Querydsl에서 where(A,B) 에 다양한 조건들을 직접 넣을 수 있는데, 이렇게 넣으면 AND 조건으로 처리된다. 참고로 where() 에 null 을 입력하면 해당 조건은 무시한다.
-
이 코드의 또 다른 장점은 likeItemName() , maxPrice() 를 다른 쿼리를 작성할 때 재사용 할 수 있다는 점이다. 쉽게 이야기해서 쿼리 조건을 부분적으로 모듈화 할 수 있다. 자바 코드로 개발하기 때문에 얻을 수 있는 큰 장점이다.
-
QuerydslConfig
-
package hello.itemservice.config; import ...; @Configuration @RequiredArgsConstructor public class QuerydslConfig { private final EntityManager em; @Bean public ItemService itemService() { return new ItemServiceV1(itemRepository()); } @Bean public ItemRepository itemRepository() { return new JpaItemRepositoryV3(em); } }
앱, 테스트 실행
- ItemServiceApplication 에 @Import(QuerydslConfig.class) 로 변경 후 실행
- 특 x
예외 변환
- Querydsl 은 별도의 스프링 예외 추상화를 지원하지 않는다. 대신에 JPA에서 학습한 것 처럼 @Repository 에서 스프링 예외 추상화를 처리해준다.
정리
- Querydsl 덕분에 동적 쿼리를 매우 깔끔하게 사용할 수 있다.
- 쿼리 문장에 오타가 있어도 컴파일 시점에 오류를 막을 수 있다.
- 메서드 추출을 통해서 코드를 재사용할 수 있다. 예를 들어서 여기서 만든 likeItemName(itemName) , maxPrice(maxPrice) 메서드를 다른 쿼리에서도 함께 사용할 수 있다.
Querydsl을 사용해서 자바 코드로 쿼리를 작성하는 장점을 느껴보았을 것이다. 그리고 동적 쿼리 문제도 깔끔하게 해결해보았다. Querydsl은 이 외에도 수 많은 편리한 기능을 제공한다. 예를 들어서 최적의 쿼리 결과를 만들기 위해서 DTO로 편리하게 조회하는 기능은 실무에서 자주 사용하는 기능이다. JPA를 사용한다면 스프링 데이터 JPA와 Querydsl은 실무의 다양한 문제를 편리하게 해결하기 위해 선택하는 기본 기술이라 생각한다.
댓글남기기