본문 바로가기
JPA & Querydsl

Spring Data JPA와 QueryDSL에서 동적 쿼리 처리하기

by 창따오 2025. 3. 8.
728x90

1. JPQL을 사용하여 여러 개의 Member 삭제하기

Spring Data JPA에서 JPQL을 활용하여 여러 개의 Member 엔티티를 한 번에 삭제하려면 @Modifying@Query를 사용해야 합니다.

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
    
    @Modifying
    @Query("DELETE FROM Member m WHERE m.id IN :memberIds")
    void deleteMembersByIds(@Param("memberIds") List<Long> memberIds);
}

✅ 설명

  • @Modifying: DELETE, UPDATE 같은 변경 쿼리를 실행할 때 필수로 추가해야 합니다.
  • IN (:memberIds)를 사용하여 여러 개의 ID를 한 번에 삭제할 수 있습니다.
  • @Transactional을 함께 사용하여 트랜잭션을 관리해야 합니다.

📌 서비스 계층에서 사용 예시

@Service
@RequiredArgsConstructor
@Transactional
public class MemberService {
    private final MemberRepository memberRepository;

    public void deleteMembers(List<Long> memberIds) {
        memberRepository.deleteMembersByIds(memberIds);
    }
}

 

이제 여러 개의 Member를 효율적으로 삭제할 수 있습니다.


2. QueryDSL에서 동적 검색 조건 처리하기

QueryDSL을 사용하여 검색 조건을 동적으로 적용할 때 null 값을 어떻게 처리해야 할까요?

❌ 잘못된 코드

private BooleanBuilder searchPredicate(MemberSearchDto cond) {
    return new BooleanBuilder().and(memberNameEq(cond.getName()))
                               .and(ageGoe(cond.getMinAge()))
                               .and(ageLoe(cond.getMaxAge()))
                               .and(teamNameEq(cond.getTeamName()));
}

이 코드에서는 cond.getMinAge()null이면 ageGoe(null)이 실행되면서 Hibernate에서 예상치 못한 동작을 할 수 있습니다.

(테스트 해본 결과 잘못된 코드로 보기 어렵다. 단, MemberSearchDto에서 minAge와 maxAge의 타입을 int 원시타입이 아닌 Integer 객체 타입으로 줘야 Null 일 때, 무시된다. int 타입일 때, 쿼리가 무시되지않고, where절에서 조건을 탄다.)

✅ 올바른 해결 방법: null 체크 후 조건 추가

private BooleanExpression searchPredicate(MemberSearchDto cond) {
    return Expressions.allOf(
        memberNameEq(cond.getName()),
        ageGoeNullable(cond.getMinAge()),
        ageLoeNullable(cond.getMaxAge()),
        teamNameEq(cond.getTeamName())
    );
}

private BooleanExpression ageGoeNullable(Integer minAge) {
    return minAge != null ? member.age.goe(minAge) : null;
}

private BooleanExpression ageLoeNullable(Integer maxAge) {
    return maxAge != null ? member.age.loe(maxAge) : null;
}

이 방식의 장점은 null인 경우 자동으로 해당 조건을 제거한다는 것입니다.


🔥 결론

  • JPQL DELETE에서는 @Modifying@Query를 함께 사용해야 합니다.
  • BooleanBuilder를 사용할 때는 null 체크를 해야 불필요한 조건이 제거됩니다.
  • BooleanExpression을 활용하면 더 간결한 코드로 동적 쿼리를 구현할 수 있습니다.

이제 Spring Data JPA와 QueryDSL을 더욱 효과적으로 활용할 수 있습니다! 😊