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 파라미터의 값이 없을 경우 오류가 발생한다.
동적 쿼리를 짜는 방법
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();
}
진짜 그대로 문자로 생성해서 짜는 방식이다.
JPA에서 표준으로 주어지는 방법이라 하지만 강의에서도 지속적으로 이건 실무에서 사용하지 않는 방법이라고 여러번 강조해서 말씀하셨다.
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책을 보면 알 수 있다고합니다.
이 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);
}