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"
dependencies 스코프 밖에 아래 코드 추가
//Querydsl 추가, 자동 생성된 Q클래스 gradle clean으로 제거
clean {
delete file('src/main/generated')
}
검증 - Q 타입 생성 확인 방법
옵션 선택1 - Gradle - Q타입 생성 확인 방법
Gradle IntelliJ 사용법
Gradle 콘솔 사용법
./gradlew clean compileJava
참고
Q타입은 컴파일 시점에 자동 생성되므로 버전관리(GIT)에 포함하지 않는 것이 좋다.
gradle 옵션을 선택하면 Q타입은 gradle build 폴더 아래에 생성되기 때문에 여기를 포함하지 않아야 한다.
대부분 gradle build 폴더를 git에 포함하지 않기 때문에 이 부분은 자연스럽게 해결된다.
Q타입 삭제
gradle clean 을 수행하면 build 폴더 자체가 삭제된다. 따라서 별도의 설정은 없어도 된다
옵션 선택2 - IntelliJ IDEA - Q타입 생성 확인 방법
Build -> Build Project
또는
Build -> Rebuild
또는
main() ,
또는
테스트를 실행하면 된다.
src/main/generated
하위에
hello.itemservice.domain.QItem 이 생성되었다.
참고
Q타입은 컴파일 시점에 자동 생성되므로 버전관리(GIT)에 포함하지 않는 것이 좋다.
IntelliJ IDEA 옵션을 선택하면 Q타입은 src/main/generated 폴더 아래에 생성되기 때문에
여기를 포함하지 않는 것이 좋다.
[git ignore를 이용해서]
Q타입 삭제
직접 파일 삭제를 하던지
혹은
아까 build.gradle에 추가한 코드
//Querydsl 추가, 자동 생성된 Q클래스 gradle clean으로 제거
clean {
delete file('src/main/generated')
}
덕분에
참고
Querydsl은 이렇게 설정하는 부분이 사용하면서 조금 귀찮은 부분인데,
IntelliJ가 버전업 하거나 Querydsl의 Gradle 설정이 버전업 하면서 적용 방법이 조금씩 달라지기도 한다.
그리고 본인의 환경에 따라서 잘 동작하지 않기도 한다.
공식 메뉴얼에 소개 되어 있는 부분이 아니기 때문에, 설정에 수고로움이 있지만 querydsl gradle 로 검색하면
본인 환경에 맞는 대안을 금방 찾을 수 있을 것이다.
Querydsl 적용
JpaItemRepositoryV3
@Repository
@Transactional
// 서비스에서 Transactional을 해야하지만 , 여기서는 서비스없이 바로 리포지토리를 테스트하기 위해 사용 + jpa사용하려면 트랜잭션 필요
public class JpaItemRepositoryV3 implements ItemRepository {
private final EntityManager em;
private final JPAQueryFactory query;
/*
queryDsl 사용하려면 JPAQueryFactory 필요
JPAQueryFactory 는 queryDsl 것이다. queryDsl 은 결과적으로 jap 의 jpql 을 만들어주는 빌더역할을 한다.
-> JPAQueryFactory 는 jpa 쿼리 즉 jpql 을 만들어준다.
*/
public JpaItemRepositoryV3(EntityManager em) {
this.em = em;
this.query = new JPAQueryFactory(em);
//JPAQueryFactory 를 밖에서 스프링빈으로 등록 후 주입 받아도 된다.
}
@Override
public Item save(Item item) {
//jpa 사용
em.persist(item);
return item;
}
@Override
public void update(Long itemId, ItemUpdateDto updateParam) {
//jpa 사용
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) {
//jpa 사용
Item item = em.find(Item.class, id); // 찾으려는 엔티티 클래스,primary key를 넣어준다.
return Optional.ofNullable(item); //혹시 null일수도 있으니까 Optional.ofNullable() 사용
}
@Override
public List<Item> findAll(ItemSearchCond cond) {
//QueryDsl 사용
String itemName = cond.getItemName();
Integer maxPrice = cond.getMaxPrice();
BooleanBuilder builder = new BooleanBuilder(); //조건을 넣을곳
if (StringUtils.hasText(itemName)) {
//itemName 에 텍스트가 존재한다면
builder.and(item.itemName.like("%" + itemName + "%"));
}
if (maxPrice != null) { //loe -> lower or equal 작거나 같다.
builder.and(item.price.loe(maxPrice));
}
// QItem item = new QItem("i"); //매개변수가 alias(별칭)이 된다, 이렇게 꺼내도 되지만
//QItem.class 내부에 생성되어있는 객체를 사용해도된다. QItem.item 처럼사용
//하지만 그것도 길수있으므로 on-demand static import 해서 item 으로 사용
List<Item> result = query.select(item)
.from(item)
.where(builder)
.fetch();
/*
fetch()를 해주면 list 가 반환된다.
*/
return result;
}
}
공통기능
Querydsl을 사용하려면 JPAQueryFactory 가 필요하다.
JPAQueryFactory 는 JPA 쿼리인 JPQL을 만들기 때문에 EntityManager 가 필요하다.
설정 방식은 JdbcTemplate 을 설정하는 것과 유사하다.
참고로 JPAQueryFactory 를 스프링 빈으로 등록해서 사용해도 된다.
save(), update(), findById()
기본 기능들은 JPA가 제공하는 기본 기능을 사용한다
findAllOld ( 위 코드의 findAll()을 얘기하는것)
Querydsl을 사용해서 동적 쿼리 문제를 해결한다.
BooleanBuilder 를 사용해서 원하는 where 조건들을 넣어주면 된다.
이 모든 것을 자바 코드로 작성하기 때문에 동적 쿼리를 매우 편리하게 작성할 수 있다.
리팩토링한 findAll()
앞서 findAllOld 에서 작성한 코드를 깔끔하게 리팩토링 했다.
다음 코드는 누가 봐도 쉽게 이해할 수 있을 것이다.
@Override
public List<Item> findAll(ItemSearchCond cond) {
//리팩토링한 버전
String itemName = cond.getItemName();
Integer maxPrice = cond.getMaxPrice();
return query.select(item)
.from(item)
.where(LikeItemName(itemName), maxPrice(maxPrice))
.fetch();
}
private BooleanExpression maxPrice(Integer maxPrice) {
if (maxPrice != null) {
return item.price.loe(maxPrice);
}
return null;
}
private BooleanExpression LikeItemName(String itemName) {
//BooleanExpression 을 ibatis 에서 제공하는걸 사용 x
if (StringUtils.hasText(itemName)) {
return item.itemName.like("%" + itemName + "%");
}
return null;
}
Querydsl에서 where(A,B) 에 다양한 조건들을 직접 넣을 수 있는데, 이렇게 넣으면 AND 조건으로 처리된다.
참고로 where() 에 null 을 입력하면 해당 조건은 무시한다.
이 코드의 또 다른 장점은 likeItemName() , maxPrice() 를 다른 쿼리를 작성할 때 재사용 할 수 있다는 점이다.
쉽게 이야기해서 쿼리 조건을 부분적으로 모듈화 할 수 있다.
자바 코드로 개발하기 때문에 얻을 수 있는 큰 장점이다.
QuerydslConfig
@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);
}
}
테스트 실행 시 잘 동작
예외 변환
Querydsl 은 별도의 스프링 예외 추상화를 지원하지 않는다.
대신에 JPA에서 학습한 것 처럼 @Repository 에서 스프링 예외 추상화를 처리해준다.
정리
Querydsl 장점
Querydsl 덕분에 동적 쿼리를 매우 깔끔하게 사용할 수 있다.
@Override
public List<Item> findAll(ItemSearchCond cond) {
//리팩토링한 버전
String itemName = cond.getItemName();
Integer maxPrice = cond.getMaxPrice();
return query.select(item)
.from(item)
.where(LikeItemName(itemName), maxPrice(maxPrice))
.fetch();
}
private BooleanExpression maxPrice(Integer maxPrice) {
if (maxPrice != null) {
return item.price.loe(maxPrice);
}
return null;
}
private BooleanExpression LikeItemName(String itemName) {
//BooleanExpression 을 ibatis 에서 제공하는걸 사용 x
if (StringUtils.hasText(itemName)) {
return item.itemName.like("%" + itemName + "%");
}
return null;
}
쿼리 문장에 오타가 있어도 컴파일 시점에 오류를 막을 수 있다.
메서드 추출을 통해서 코드를 재사용할 수 있다.
예를 들어서 여기서 만든 likeItemName(itemName) , maxPrice(maxPrice) 메서드를
다른 쿼리에서도 함께 사용할 수 있다.
Querydsl을 사용해서 자바 코드로 쿼리를 작성하는 장점을 느껴보았을 것이다.
그리고 동적 쿼리 문제도 깔끔하게 해결해보았다.
Querydsl은 이 외에도 수 많은 편리한 기능을 제공한다.
예를 들어서
(최적의 쿼리 결과를 만들기 위해서 DTO로 편리하게 조회하는 기능은 실무에서 자주 사용하는 기능이다.)
(또는 페이징이라던지)
JPA를 사용한다면 스프링 데이터 JPA와 Querydsl은 실무의 다양한 문제를 편리하게 해결하기 위해
선택하는 기본 기술이라 생각한다.
[Querydsl에 대한 자세한 내용은 실전! Querydsl 강의를 참고하자.]
'인프런 > 스프링 DB 2편' 카테고리의 다른 글
9) 데이터 접근 기술 - 활용 방안 (0) | 2024.02.15 |
---|---|
7) 데이터 접근 기술 - 스프링 데이터 JPA (0) | 2024.02.15 |
6) 데이터 접근기술 - JPA (0) | 2024.02.08 |
5)데이터 접근 기술 - MyBatis (0) | 2023.03.08 |
4) 데이터 접근 기술 - 테스트 (0) | 2023.03.07 |
댓글