JPA활용 1 - 동적 쿼리처리 방법

JIWOO YUN·2023년 8월 28일
0

JPA활용1

목록 보기
2/5
post-custom-banner
    public List<Order> findAll(OrderSearch orderSearch) {
        return em.createQuery("select o from Order o join o.member"
                + "where o.status = :status "
                + "and m.name like :name", Order.class)
                .setParameter("status",orderSearch.getOrderStatus())
                .setParameter("name",orderSearch.getMemeberName())
                .setMaxResults(1000)
                .getResultList();
    }

값이 다있을 경우에는 이렇게 해도됨. but, 값이 전부 있는 경우가 아닌 경우에는 이렇게 짤 경우 오류가 발생해서 안된다. -> 동적 쿼리로 짜야한다.

위의 쿼리를 예시로 보면 orderSearch에 만약 name 파라미터의 값이 없을 경우 오류가 발생한다.

  • name 파라미터에 값이 없다고 해도 쿼리를 통해서 검색은 진행이 되야하기 때문에 동적 쿼리로 짜야한다.

동적 쿼리를 짜는 방법

  1. 무식하게 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;
                //그다음은 and로 이어지기 때문에
            }else {
                jpql += "and";
            }
            jpql += "o.status = :status";
        }

        if(StringUtils.hasText(orderSearch.getMemeberName())){
            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.getMemeberName())){
            query = query.setParameter("name",orderSearch.getMemeberName());
        }

        return query.getResultList();

    }

진짜 그대로 문자로 생성해서 짜는 방식이다.

  • 번거롭고 실수로 인한 버그가 충분이 많이 발생할 수 있는 문제가 있다.
  1. JPA Criteria를 사용해서 하는 방법(권장 X)

  • JPA에서 표준으로 주어지는 방법이라 하지만 강의에서도 지속적으로 이건 실무에서 사용하지 않는 방법이라고 여러번 강조해서 말씀하셨다.

    • 치명적인 단점이 존재 : 유지 보수성이 0로 수렴한다. 이 코드들을 보면서 어떻게 유도되는지가 머리에서 잘 정리가 안되는 큰 문제가 발생한다.
  •     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.getMemeberName())){
                Predicate name = cb.like(m.get("name"),"%"+orderSearch.getMemeberName()+"%");
                criteria.add(name);
            }
    
            cq.where(cb.and(criteria.toArray(new Predicate[criteria.size()])));
            TypedQuery<Order> query = em.createQuery(cq).setMaxResults(1000);
            return query.getResultList();
        }
  • 사실 따라치면서도 잘 모르겠다.

  • 좀 더 알고싶으면 JPA책을 보면 알 수 있다고합니다.


  1. QueryDsl 이용

이 2가지 방법은 너무 어렵거나 버그가 생겨서 고민이 많았고 그에 대한 해결책으로 알려주는데 QueryDsl이다. -> 현재는 간단하게 설치와 코드 정도만 하고 나중에 좀더 자세히 알아보자. -> JPA 활용 2편에서 설명해준다.

따로 강의가 존재하기 때문에 현재 강의에서는 간단히만 말하고 넘어갔다.

  • QueryDsl을 사용하려면 설정을 추가해줘야한다.

  • gradle에 밑의 걸 추가해주어 업데이트를 진행해야함.

  • 	// Querydsl 추가
    	implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
    	annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
    	annotationProcessor "jakarta.annotation:jakarta.annotation-api"
    	annotationProcessor "jakarta.persistence:jakarta.persistence-api"
  • gradle을 추가하고 다운로드를 받은 후에 실행하게되면 자동으로 generated폴더에 QEntity가 생성된다.

밑의 코드와 같이 쿼리를 작성할 수 있게 된다.

    public List<Order> findAll(OrderSearch orderSearch){
        QOrder order = QOrder.order;
        QMember member = QMember.member;

        JPAQueryFactory query = new JPAQueryFactory(em);
        return query
                .select(order)
                .from(order)
                .join(order.member,member)
                .where(statusEq(orderSearch.getOrderStatus()),nameLike(orderSearch.getMemeberName()))
                .limit(1000)
                .fetch();
    }

    private BooleanExpression statusEq(OrderStatus statusCond){
        if(statusCond == null){
            return null;
        }

        return QOrder.order.orderStatus.eq(statusCond);
    }

    private BooleanExpression nameLike(String nameCond){
        if(!StringUtils.hasText(nameCond)){
            return null;
        }
        return QMember.member.name.like(nameCond);
    }
  • 자바 코드기 때문에 컴파일 시점에서 오타를 잡아내고 코드 어시스턴스도 제대로 작동하기 때문에 오류를 많이 잡아주고 도움을 많이 준다.
  • generate부분은 gitignore에서 빼줘도된다. -> build될때 자동으로 생성되기 때문에.
profile
열심히하자
post-custom-banner

0개의 댓글