querydsl의 제일 좋은 점은 타입체크가 가능해서 컴파일 에러를 띄울 수 있단거다. 이겟 무슨 말이냐면 일반 sql은 문자로 치게 되는게 문자로는 타입 에러를 띄울 수 없으니 런타임 에러로만 발견할 수 있고, 이러다보니 실제 사용자가 문제 발견해서 퇴근하다 돌아오고 하는 문제가 발생한다.
그럼 전까지 봤던 ORM이 자동으로 sql을 만들어서 하는건 알겠는데, 자동으로 작성하는 방법이 자기도 sql 문자로 변환해서 하던가, 아주 타입을 아주 어렵게 지정해서 하는 예전 EJB급이 있다.
그래서 이건 아니다 싶어 querydsl이 나왔다.
얘도 다양한 DB들을 같은 함수로 처리하고자 나왔다.
가장 큰 장점은 아까 말했듯 JPA, MongoDB, SQL 같은 기술들을 위해 type-safe SQL을 만드는 프레임워크다.
아까 엄청나게 복잡한 API 코드를 봤었는데 그것도 이유가 type-safe를 위해서였다. 얘도 그러기 위해 @Entity가 붙어 DB에 저장한다고 알린 클래스는 QClass라는 클래스를 만들어 type-safe를 지원하는 방식으로 한다. 그래서 설정이 조금 복잡함.
얘도 작동 방식은 sql문을 작성하는 원리라 QueryDsl -> JPQL -> SQL로 차례대로 생성한다.
그냥 JPA만으로는 부족했던 동적 쿼리, 더 복잡한 쿼리를 해결해줄 수 있고 조인, 페이지, 정렬등도 지원한다.
설정방법이 좀 복잡한데 일단 종속성을 추가하자. 종속성을 뭐를 추가하는지도 매번 달라지는 듯.
clean은 이제 저 src/main/generated에 querydsl이 타입용으로 참고하려고 만든 클래스 이름 앞에 Q가 붇은 QClass들이 있는데 이걸 제거하려고 설정에 붙인거다. gradle로 설정하면 어차피 build 밑에 있기때문에 gitignore은 무시해도 된다.
난 gradle로 실행하도록 해놔서 build 밑에 QClass가 생성됨.
이제 이걸 사용하는 레퍼지토리 코드를 작성하자. 최종 코드는 이거임.
package hello.itemservice.repository.jpa;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import hello.itemservice.domain.Item;
import hello.itemservice.repository.ItemRepository;
import hello.itemservice.repository.ItemSearchCond;
import hello.itemservice.repository.ItemUpdateDto;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;
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);
}
public List<Item> findAllold(ItemSearchCond cond) {
String itemName = cond.getItemName();
Integer maxPrice = cond.getMaxPrice();
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();
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) {
if (StringUtils.hasText(itemName)) {
return item.itemName.like("%" + itemName + "%");
}
return null;
}
}
여기에서 생성자는 EntityManager로 em을 생성하는데 JPAQueryFactory라는게 있다. 이게 결국 querdsl도 jpql로 변환해서 하는거라 이 변환기라고 함.
조금씩 축약하는 방법을 보면..
처음엔 이렇게 있다("i"는 무시해도 됨). 생성자에서 query를 정의해서 여기에 sql문 함수가 다 있고 item 객체의 타입, 속성들은 QClass로 대체하여 해준다.
여기서 QItem은 자기 자신을 객체로 반환하는 static 함수가 있다. 이걸 사용하면 new 하는 부분 줄일 수 있고, static import 하면 클래스 이름도 줄일 수 있다.
그리고 동적 쿼리는 빌더로 해결할 수 있다.
테스트로 실행해보면 hibernate sql 문이 나오는데, 결국은 jpa가 했던 것처럼 sql문을 작성하는걸 알 수 있다.
근데 저 동적쿼리용 코드를 더 줄일 수 있다. builder를 정의해서 and로 추가하는 대신 BooleanExpression을 사용해도 된다.
querydsl의 장점은 역시 오타같은 컴파일 에러가 가능하다는 것.
'CS > 김영한 스프링 강의' 카테고리의 다른 글
스프링 DB 2편 - 데이터 접근 핵심 원리 - 섹션9. 스프링 트랜잭션 이해 (0) | 2023.09.02 |
---|---|
스프링 DB 2편 - 데이터 접근 핵심 원리 - 섹션8. 데이터 접근 기술 - 활용 방안 (0) | 2023.08.28 |
스프링 DB 2편 - 데이터 접근 핵심 원리 - 섹션6. 데이터 접근 기술 - 스프링 데이터 JPA (0) | 2023.08.23 |
스프링 DB 2편 - 데이터 접근 핵심 원리 - 섹션5. 데이터 접근 기술 - JPA (0) | 2023.08.22 |
스프링 DB 2편 - 데이터 접근 핵심 원리 - 섹션4. 데이터 접근 기술 - MyBatis (0) | 2023.08.20 |