인프런 - 스프링 부트와 JPA 활용1 by 김영한 을 기반으로 작성된 글입니다.
실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
package jpabook.jpashop.domain;
@Getter @Setter
public class OrderSearch {
private String memberName; //회원 이름
private OrderStatus orderStatus; //주문상태[ORDER, CANCEL]
}
JPA에서 동적 쿼리 해결하기
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();
}
만약 ❓ Ordersearch에 name, status 파라미터가 없다면
name 과 status 를 필터링 조건으로 사용하지 말고 주문 내역을 모두 가지고 와라
라고 한다면 아래 코드 처럼 되어야 한다
public List<Order> findAll(OrderSearch orderSearch) {
return em.createQuery("select o from Order o join o.member m",
.setMaxResults(1000) //최대 1000건
.getResultList();
}
이것을 동적 쿼리로 처리해주어야 한다
//주문 상태 검색
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 쿼리를 문자로 생성하기가 번거롭고 실수로 인한 버그가 충분히 발생할 수 있다
❗ MyBatis, iBatis를 사용하면 동적쿼리를 생성하기 편리하다
/**
* 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<Object, Object> m = o.join("member", JoinType.INNER);
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);
return query.getResultList();
}
❗ 결국 다른 대안이 필요!
동적 쿼리, JPQL 을 위한 해결책이 필요하다고 고민해 나온 라이브러리가 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);
}
강의에서는 다뤄보지 않고 코드만 확인해보겠다