JPA 정적쿼리, 동적쿼리(feat. JpaSpecificationExecutor)
by 볼빵빵오춘기정적쿼리(JPA문법 사용)
- 정적 쿼리는 조건이 고정된 쿼리로, 쿼리 메서드를 통해 정의된다.
- 정적쿼리 사용 방법은 단순하고 직관적이지만, 조건이 고정되어 있어 동적인 요구사항을 처리하기 어렵다.
- 모든 조합의 메서드를 만들어야 하므로, 조건이 많아지면 관리가 복잡해진다.
public interface ProtectRepository extends JpaRepository<ProtectEntity, Long> { Page<ProtectEntity> findByKindAndGenderAndDate(String kind, String gender, String date, Pageable pageable); }
동적쿼리(JpaSpecificationExecutor 사용)
- 동적 쿼리는 조건을 런타임에 결정할 수 있으며, Specification 인터페이스를 사용하여 정의된다.
- 여러 조건을 조합하거나 조건을 선택적으로 적용할 수 있어 유연하다.
- 동적쿼리 사용 방법은 조건이 많고 다양하게 조합되어야 하는 경우에 유리하므로 코드를 재사용할 수 있어 유지보수가 용이하다.
public interface ProtectRepository extends JpaRepository<ProtectEntity, Long>, JpaSpecificationExecutor<ProtectEntity> { }
정적쿼리, 동적쿼리 장단점
정적쿼리 | 동적쿼리 | |
장점 | - 단순함: 조건이 몇 개 없고, 조합이 많지 않을 때는 직관적으로 작성할 수 있다. - 명시성: 어떤 쿼리가 실행되는지 명확하게 알 수 있다. |
- 유연성: 여러 조건을 동적으로 조합할 수 있다. - 재사용성: 조건을 정의하는 코드의 재사용성이 높다. - 유지보수 용이: 새로운 조건이 추가되더라도 기존 코드를 크게 변경하지 않고 추가할 수 있다. |
단점 | - 유연성 부족: 조건이 많아지면 메서드의 조합이 기하급수적으로 늘어날 수 있다. - 유지보수 어려움: 조건이 추가되거나 변경될 때마다 새로운 메서드를 추가해야 한다. |
- 복잡도: 처음 사용 시 이해하고 설정하는 과정이 조금 더 복잡할 수 있다. - 추상화: 쿼리가 추상화되어 있어, 어떤 SQL이 실행되는지 바로 알기 어려울 수 있다. |
결론 | 조건이 단순하고 고정된 경우, 메서드 명만으로 충분히 표현 가능한 경우에 적합하다. | 조건이 다양하고, 동적으로 조합해야 하는 경우, 그리고 유지보수와 확장성이 중요한 경우에 적합하다. |
JpaSpecificationExecutor
- Spring Data JPA에서 제공하는 인터페이스로, JPA Criteria API를 사용하여 동적 쿼리를 생성할 수 있게 해준다.
- 이 인터페이스를 사용하면 복잡한 검색 조건을 쉽게 정의하고, 동적으로 쿼리를 생성하여 실행할 수 있다.
- 주요 기능으로는 동적쿼리를 생성하고 검색 조건 쿼리를 작성할 때 유연하다.
동적 쿼리 사용방법
엔티티 클래스
@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String firstName; private String lastName; private int age; // getters and setters }
리포지토리 인터페이스
JpaSpecificationExecutor 인터페이스를 상속받는 리포지토리를 정의한다.
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> { }
스펙 정의
Specification을 사용하여 동적 쿼리를 정의한다.
public class UserSpecifications { public static Specification<User> hasFirstName(String firstName) { return (Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> { return cb.equal(root.get("firstName"), firstName); }; } public static Specification<User> hasLastName(String lastName) { return (Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> { return cb.equal(root.get("lastName"), lastName); }; } public static Specification<User> isOlderThan(int age) { return (Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> { return cb.greaterThan(root.get("age"), age); }; } }
서비스 클래스
서비스 클래스에서 JpaSpecificationExecutor를 사용하여 동적 쿼리를 실행한다.
@Service public class UserService { @Autowired private UserRepository userRepository; public List<User> findUsers(String firstName, String lastName, int age) { Specification<User> spec = Specification.where(null); if (firstName != null) { spec = spec.and(UserSpecifications.hasFirstName(firstName)); } if (lastName != null) { spec = spec.and(UserSpecifications.hasLastName(lastName)); } if (age > 0) { spec = spec.and(UserSpecifications.isOlderThan(age)); } return userRepository.findAll(spec); } }
정리
- 두 방법을 상황에 맞게 혼용하여 사용해야할 것 같다.
- 조건이 많고 복잡한 경우 JpaSpecificationExecutor를 사용하고, 조건이 단순한 경우 JPA의 정적 쿼리를 사용하면 될 것 같다.
- 프로젝트의 요구사항과 복잡도에 따라 적절한 방법을 선택하면 될 듯 하다.
- 간단하게 동적쿼리를 사용해 보긴 했는데 처음 쓰는거라 그런지 정신은 없다.

블로그의 정보
Hello 춘기's world
볼빵빵오춘기