728x90
1. QueryDSL(Query Domain-Specific Language)
1.1 💡QueryDSL 이란?
SQL, JPQL
같은 쿼리를Java
코드로 작성할 수 있게 해주는 타입 안전한 ORM 기반 쿼리 빌더SQL
을Java
코드로 작성하면서, 가독성과 유지보수성을 대폭 개선할 수 있는 도구
1.2 ☝️ 왜 사용해야할까
1.2.1 SQL/JPQL의 한계 해결
- 문법 오류 - 쿼리의 오류를 런타임에서만 확인 가능
- 필드 이름 변경 - 엔티티 필드가 변경되면 문자열 기반 쿼리에서 이를 수동으로 수정해야 함.
- 동적 쿼리 작성의 어려움 - 조건에 따라 쿼리를 유연하게 작성하려면 코드 복잡도 향상
- 코드 비교
// JPQL
// 필터가 많아질수록 코드가 복잡
public List<User> findUsersDynamicJPQL(EntityManager em, Integer age, String name) {
StringBuilder jpql = new StringBuilder("SELECT u FROM User u WHERE 1=1");
if (age != null) {
jpql.append(" AND u.age > :age");
}
if (name != null) {
jpql.append(" AND u.name = :name");
}
TypedQuery<User> query = em.createQuery(jpql.toString(), User.class);
if (age != null) query.setParameter("age", age);
if (name != null) query.setParameter("name", name);
return query.getResultList();
}
// QueryDSL
// BooleanBuilder 사용
public List<User> findUsersDynamicQueryDSL(JPAQueryFactory queryFactory, Integer age, String name) {
QUser user = QUser.user;
BooleanBuilder builder = new BooleanBuilder(); // 동적 조건 빌더
if (age != null) {
builder.and(user.age.gt(age));
}
if (name != null) {
builder.and(user.name.eq(name));
}
return queryFactory
.selectFrom(user)
.where(builder) // 동적 조건 적용
.fetch();
}
1.2.2 복잡한 쿼리 요구사항 처리
- 조인 - 여러 테이블 데이터를 조합해서 처리.
- 서브쿼리 - 쿼리 안에 또 다른 쿼리를 포함.
- 그룹핑 및 집계 - 데이터를 그룹화하거나 통계 처리.
1.2.3 장/단점
- 장점
- 타입 안전한 쿼리 작성
- 가독성 UP, 유지보수성 UP
- 동적 쿼리 작성의 유연함
- 다양한 쿼리 지원
- 단점
- 초기 세팅의 복잡성, 의존성 관리
- Q 클래스 생성 필요
- 러닝 커브
1.3 💁 언제 사용해야할까?
1.3.1 🙆 사용해야하는 경우
- 동적 쿼리가 많은 프로젝트
- 복잡한 쿼리를 자주 작성해야 하는 대규모 애플리케이션
- 타입 안전성과 코드 가독성을 중시하는 팀 환경
1.3.2 🙅 사용하지 말아야 하는 경우
- 프로젝트가 간단한 CRUD 중심인 경우
- 네이티브 SQL을 주로 사용하는 환경
1.4 ✈️ 대안
1.4.1 JPA Criteria API
- JPA 표준 스펙에 포함된 쿼리 빌더 API
✅ 장점
- 표준 API: JPA 구현체(Hibernate 등)와 상관없이 동작.
- 별도의 외부 라이브러리 없이 사용 가능.
❌ 단점
- 가독성 부족: 코드가 복잡하고 장황해지기 쉬움.
- 유지보수가 어려움.
- 예제 코드
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);
Predicate agePredicate = cb.gt(user.get("age"), 18);
Predicate statusPredicate = cb.equal(user.get("status"), "ACTIVE");
query.select(user).where(cb.and(agePredicate, statusPredicate));
List<User> result = em.createQuery(query).getResultList();
1.4.2 Spring Data JPA Specification
- Spring Data JPA에서 제공하는 Specification 인터페이스를 활용하면 동적 쿼리 구현
✅ 장점
- Spring Data JPA와 통합: Spring 프로젝트와 자연스럽게 연동.
- 동적 조건 작성이 비교적 간단.
❌ 단점
- QueryDSL에 비해 표현력이 제한적.
- 복잡한 쿼리 작성에는 적합하지 않음.
- 예제코드
public class UserSpecifications {
public static Specification<User> hasAgeGreaterThan(int age) {
return (root, query, cb) -> cb.gt(root.get("age"), age);
}
public static Specification<User> hasStatus(String status) {
return (root, query, cb) -> cb.equal(root.get("status"), status);
}
}
// 사용
Specification<User> spec = Specification.where(UserSpecifications.hasAgeGreaterThan(18))
.and(UserSpecifications.hasStatus("ACTIVE"));
List<User> users = userRepository.findAll(spec);
1.4.3 네이티브 SQL
✅ 장점
- 유연성: 데이터베이스에 특화된 쿼리를 작성 가능
- 성능 최적화에 유리
❌ 단점
- 타입 안전성 부족: 문자열 기반으로 작성
- 데이터베이스 종속적 → 이식성이 낮음
- 유지보수가 어려움
- 예제 코드
@Query(value = "SELECT * FROM users WHERE age > :age AND status = :status", nativeQuery = true)
List<User> findUsersNative(@Param("age") int age, @Param("status") String status);
1.4.4 jOOQ (Java Object Oriented Querying)
- jOOQ는 SQL을 Java 코드로 작성하면서도, 데이터베이스와 밀접하게 통합하여 고급 기능을 사용할 수 있는 강력한 도구
✅ 장점
- SQL 친화적 설계: SQL의 고급 기능(윈도우 함수, CTE 등)을 Java 코드로 직접 활용 가능.
- 타입 안전성: SQL 컬럼과 Java 필드 간의 타입을 컴파일 타임에 검증.
- DBMS 특화 기능 지원: 데이터베이스에 따라 최적화된 SQL 생성 가능.
- 코드 자동 생성: 테이블 스키마 기반으로 Java 코드를 생성하여, 변경 사항을 자동 반영.
❌ 단점
- SQL 종속성: 데이터베이스 변경 시 쿼리 코드 수정이 필요.
- 학습 난이도: SQL에 익숙하지 않은 개발자에게는 진입 장벽이 높음.
- ORM 부족: 객체 지향적 추상화가 부족하고, SQL 중심의 설계에 초점.
- 예제 코드
// 동적 조건 추가
public List<User> findUsersDynamicJooq(DSLContext context, Integer age, String name) {
Condition condition = DSL.trueCondition();
if (age != null) {
condition = condition.and(USER.AGE.gt(age));
}
if (name != null) {
condition = condition.and(USER.NAME.eq(name));
}
return context.selectFrom(USER)
.where(condition)
.fetchInto(User.class);
}
라이브러리 | 특징 | 장점 | 단점 |
QueryDSL | - 타입 안전한 동적 쿼리 작성을 지원 - Q클래스를 통해 메서드 체인 방식으로 쿼리를 작성 |
- 타입 안전성(Type Safety) 보장 - 가독성 높은 코드 작성 가능 - IDE 자동완성 지원 |
- 초기 설정 복잡 - Q클래스 생성 필요 |
Spring Data JPA Specifications | - JPA의 Criteria API를 기반으로 동적 쿼리를 작성 - Specification 인터페이스를 사용 |
- 추가 라이브러리 없이 Spring Data JPA와 자연스러운 통합 - 동적 쿼리 작성 가능 |
- 코드가 장황하고 가독성이 떨어질 수 있음 |
JOOQ | - SQL 빌더 라이브러리로, SQL 문법을 코드로 표현 - 고급 SQL 기능을 지원 |
- SQL 문법을 코드로 직관적으로 표현 가능 - 복잡한 쿼리와 고급 SQL 기능 활용 가능 |
- JPA와 통합하려면 추가 설정 필요 |
Criteria API (JPA 기본) | - JPA 표준 사양 - 메서드 체인 방식을 통해 동적 쿼리를 작성 |
- 모든 JPA 구현체에서 사용 가능 - 추가 의존성 필요 없음 |
- 코드가 복잡하고 가독성이 떨어짐 |
Native QSL | - SQL 문법그대로 쿼리 작성 | - 데이터베이스에 특화된 쿼리를 작성 가능 - 성능 최적화에 유리 |
- 데이터베이스 종속적 → 이식성이 낮음 - 유지보수가 어려움 |
반응형
'Study > Spring' 카테고리의 다른 글
[Spring] Spring Security + OAuth 2.0 소셜 로그인 (1) | 2025.01.16 |
---|---|
[Spring Boot] DB Lock (0) | 2025.01.10 |
[Spring] @InitBinder (0) | 2021.08.08 |
[Spring] @Data 어노테이션 (0) | 2021.06.30 |
[Spring] 영속성 컨텍스트 (Persistence Context) (0) | 2021.06.30 |