package jpabook.jpashop.repository;
import jpabook.jpashop.domain.OrderStatus;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class OrderSearch {
private String memberName; //회원이름
private OrderStatus orderStatus; //주문상태[ORDER, CANCEL]
}
finalAll(OrderSearch orderSearch)
메서드 : 검색 조건에 동적으로 쿼리를 생성해서 주문 엔티티를 조회
public List<Order> findAll(OrderSearch orderSearch){
return em.createQuery("select o from Order o join o.member m" +
" where o.status = :status" +
" and m.name like :name", Order.class)
.setParameter("status", orderSearch.getOrderStatus())
.setParameter("name", orderSearch.getMemberName())
.setMaxResults(1000) //최대 1000건
.getResultList();
}
Order
랑 Order와 연관된 member
를 join
JPQL
로 처리 public List<Order> findAll(OrderSearch orderSearch){
String jpql = "select o from Order o join o.member m";
boolean isFirstCondition = true;
//주문 상태 검색
if(orderSearch.getOrderStatus() != null){
if(isFirstCondition){
jpql += " where";
isFirstCondition = false;
} else{
jpql += " and";
}
jpql += " o.status = :status";
}
//회원 이름 검색
if(StringUtils.hasText(orderSearch.getMemberName())){
if(isFirstCondition){
jpql += " where";
isFirstCondition = false;
} else{
jpql += " and";
}
jpql += " m.name like :name";
}
TypedQuery<Order> query = em.createQuery(jpql, Order.class)
.setMaxResults(1000);
if(orderSearch.getOrderStatus() != null){
query = query.setParameter("status", orderSearch.getOrderStatus());
}
if(StringUtils.hasText(orderSearch.getMemberName())){
query = query.setParameter("name", orderSearch.getMemberName());
}
return query.getResultList();
}
jpql
을 문자로 생성하는 것은 수동으로 수행을 해야하는 작업이기 때문에, 번거롭고 실수로 인한 버그 발생 가능성이 높은 방식JPA Criteria
로 처리 /*
JPA Criteria
*/
public List<Order> findAllByCriteria(OrderSearch orderSearch) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Order> cq = cb.createQuery(Order.class);
Root<Order> o = cq.from(Order.class);
Join<Order, Member> m = o.join("member", JoinType.INNER); //회원과 조인
// 동적 쿼리에 대한 컨디션 조합을 Predicate을 활용해서 깔끔하게 만들 수 있음
List<Predicate> criteria = new ArrayList<>();
//주문 상태 검색
if (orderSearch.getOrderStatus() != null) {
Predicate status = cb.equal(o.get("status"),
orderSearch.getOrderStatus());
criteria.add(status);
}
//회원 이름 검색
if (StringUtils.hasText(orderSearch.getMemberName())) {
Predicate name =
cb.like(m.<String>get("name"), "%" +
orderSearch.getMemberName() + "%");
criteria.add(name);
}
cq.where(cb.and(criteria.toArray(new Predicate[criteria.size()])));
TypedQuery<Order> query = em.createQuery(cq).setMaxResults(1000); //최대 1000건
return query.getResultList();
}
jpql
을 자바 코드로 작성할 수 있도록 JPA에서 표준으로 제공하는 방식
동적 쿼리에 대한 컨디션 조합을 Predicate
을 활용해서 깔끔하게 만들 수 있음
Predicate
자체가 조건이 됨하지만 치명적인 단점이 존재함
Querydsl
JPA Criteria
는 JPA 표준 스펙이지만 실무에서 사용하기에 너무 복잡함- 가장 좋은 해결책은
Querydsl
이 제시함
/*
Querydsl
*/
public List<Order> findAll(OrderSearch orderSearch){
QOrder order = QOrder.order;
QMember member = QMember.member;
return query
.select(order)
.from(order)
.join(order.member, member)
.where(statusEq(orderSearch.getOrderStatus()),
nameLike(orderSearch.getMemberName()))
.limit(1000)
.fetch();
}
private BooleanExpression statusEq(OrderStatus statusCond){
if(statusCond == null){
return null;
}
return order.status.eq(statusCond);
}
private BooleanExpression nameLike(String nameCond){
if(!StringUtils.hasText(nameCond)){
return null;
}
return member.name.like(nameCond);
}
동적 쿼리 뿐만 아니라 복잡한 정적 쿼리도 Querydsl
로 작성하면 좋음
컴파일 시점에서 오류를 잡아낼 수 있음
위 코드는 완성된 코드는 아니며, 추후 Querydsl
에 대한 내용을 추가로 수강하여 코드를 완성할 예정