- JPA와 같은 ORM을 사용하면 시스템이 커질수록 쿼리가 복잡해지는 문제가 발생합니다.
- JPQL과 QueryDSL은 이러한 문제를 해결하기 위해 고안된 기법입니다.
💡 JPQL
- JPQL은 JPA의 일부로 Query를 Table이 아닌 객체(=엔티티) 기준으로 작성하는 객체지향 쿼리 언어라고 정의할 수 있습니다.
- JPQL은 객체를 기준으로 모든 것이 움직이기 때문에 개발할 때, Table에 매핑되는 객체가 반드시 존재해야하며 검색할 때도 Table이 아닌 객체를 대상으로 검색해야 합니다.
- 전반적인 특징에 대해 간단히 다시 정리한다면 다음과 같습니다.
1. SQL을 추상화한 JPA의 객체지향 쿼리
2. Table이 아닌 Entity를 대상으로 개발
3. Entity와 속성은 대소문자를 구분 (PERSON <> person)
4. 별칭(alias) 사용 필수
- JPQL을 실제 구현하는 방식으로는 크게 두 가지 방식이 있습니다.
✔ EntityManager 활용
@Autowired
EntityManager em;
Person person = new Person();
Person.setFirstName("Sungkwon");
Person.setLastName("Cho");
userRepository.save(person); // JPA 문법 (save == INSERT문)
TypedQuery<User> tq = em.createQuery("select p from Person p where p.FirstName = :firstName and p.LastName = :lastName", Person.class);
tq.setParameter("firstName", "Sungkwon");
tq.setParameter("lastName", "Cho");
List<Person> personList = tq.getResultList();
LOGGER.info("firstName: " + personList.get(0).getFirstName());
✔ Repository Interface 활용
public interface PersonRepository extends JpaRepository<Person, Long>{
/* 변수 바인딩 시, ?시퀀스 사용하는 경우 */
@Query("select p from Person p where p.firstName = ?1 and p.lastName = ?2")
Person findPerson(String firstName, String lastName);
/* 변수 바인딩 시, :이름 사용하는 경우 */
@Query("select p from Person p where p.firstName = :firstName and p.lastName = :lastName")
Person findPerson2(@Param("firstName") String firstName, @Param("lastName") String lastName);
}
- 위 두 가지 방식은 구현하는 방법에 있어서 확연한 차이가 있습니다.
- 하지만, 결과적으로 이 둘의 공통점이자 문제점으로는 여전히 쿼리를 String 형태로 작성하고 있다는 점입니다.
- JPQL의 문제점에 대해 정리하면 다음과 같습니다.
1. JPQL은 문자열(=String) 형태이기 때문에 개발자 의존적 형태
2. Compile 단계에서 Type-Check가 불가능
3. RunTime 단계에서 오류 발견 가능 (장애 risk 상승)
💡 QueryDSL
- 앞서 언급한 JPQL의 문제점을 보완을 위해 고안된 것이 QueryDSL이라고 볼 수 있습니다.
- QueryDSL은 정적 타입을 이용해서 SQL, JPQL을 코드로 작성할 수 있도록 도와주는 오픈소스 빌더 API를 말합니다.
- QueryDSL을 사용하는 목적은 뚜렷합니다.
- 기존 방식(Mybatis, JPQL, etc..)은 모두 문자열(=String) 형태로 쿼리가 작성되었고, 이로 인해 Compile 단계에서 Type-Check가 불가능 했습니다.
- 이러한 risk를 줄이기 위해 QueryDSL이 등장했고, 이를 통해 Compile 단계에서 Type-check가 가능해진 것입니다.
- 이를 예시를 통해 확인해보겠습니다.
@PersistenceContext
EntityManager em;
public List<Person> selectPersonByNm(String firstNm, String lastNm){
JPAQueryFactory jqf = new JPAQueryFactory(em);
QPerson person = QPerson.person;
List<Person> personList = jpf
.selectFrom(person)
.where(person.firstName.eq(firstNm)
.and(person.lastName.eq(lastNm))
.fetch();
return personList;
}
- 위 코드는 QueryDSL를 활용하여 모든 쿼리에 대한 내용을 함수 형태로 작성한 코드입니다.
- 이렇게 엔티티(객체) + 함수 형태로 구성된 QueryDSL을 통해 구현된 코드는 오류가 존재할 시, Compile 단계에서 바로 확인 가능하며, 이에 따른 후속 조치가 가능하기 때문에 그만큼 risk가 줄어들게 되는 것입니다.
- 몰론 코드 라인수가 길어진다는 유일한 단점이 존재하긴 하지만 코드의 수가 조금 길어지더라도 가독성이 저하될만큼 코드가 복잡하거나 하지 않기 때문에 큰 단점이라 생각되진 않습니다.
- QueryDSL의 특징을 다시 한 번 정리하자면 다음과 같습니다.
1. 문자가 아닌 코드로 작성
2. Compile 단계에서 문법 오류 확인 가능
3. 코드 자동 완성 기능 활용 가능
4. 동적 쿼리 구현 가능
📌 References
'🥑 Web Technoloy' 카테고리의 다른 글
CSRF란? (0) | 2023.06.11 |
---|---|
URI와 URL (0) | 2023.06.06 |
JPA와 Hibernate 그리고 Spring Data JPA (0) | 2023.06.04 |
Lombok이란? (1) | 2023.06.04 |
Spring AOP(Aspect Oriented Programming) (0) | 2023.06.03 |