어쨌든 Hibernate는 구현체이다
📌 개요
하나만 짚고 넘어가자. JPA
의 쿼리 언어는 JPQL
이다. JPA
의 구현체 중 하나는 Hibernate
이며, Hibernate의 쿼리 언어는 HQL
이다. 사실 하나가 아니고 세 개다.
내가 ‘구현’이라고 하지 않았는가? JPA라는 인터페이스를 구현한 것이 Hibernate이다. 이는 곧 Hibernate가 독자적인 기능을 가질 수 있다는 것을 의미한다. JPQL에는 없으나 HQL에는 있는 기능과 문법에 대해 알아보자.
📌 LIMIT / OFFSET
표준 JPQL은 LIMIT
, OFFSET
문법이 없다. 대신 동일한 동작을 수행하는 메서드가 존재한다.
1
2
3
Query q = em.createQuery("SELECT m FROM Member m ORDER BY m.id");
q.setFirstResult(0);
q.setMaxResults(10);
setFirstResult
는 쿼리 결과 중 몇 번째 엔티티부터 시작할지를 지정하며, OFFSET
과 동일한 동작을 수행한다.
setMaxResults
는 쿼리 결과 중 최대 몇 개의 결과만 가져올지를 지정하며, LIMIT
과 동일한 동작을 수행한다.
1
@Query("SELECT m FROM Member m ORDER BY m.id LIMIT 10 OFFSET 20")
반면 HQL은 LIMIT
과 OFFSET
을 쿼리 문자열에 사용할 수 있다.
1
2
3
Optional<ChatMessage> findFirstByChatRoomIdOrderByCreatedAtDesc(long chatRoomId);
List<ChatMessage> findTop3ByChatRoomIdOrderByCreatedAtDesc(long chatRoomId);
다만 LIMIT
은 JPA의 메서드를 통해 구현할 수 있다. findFirstXXX
는 LIMIT 1
, findTopNXXX
은 LIMIT N
과 동일한 동작을 수행한다.
OFFSET
은 그런 거 없다. Pageable
을 사용해야 한다.
📌 CONCAT
1
@Query("SELECT CONCAT(m.firstName, m.lastName) FROM Member m")
JPQL은 두 문자열을 붙이기 위해 CONCAT
메서드를 사용한다.
다만 이 방법은 두 문자열만 붙일 수 있다는 단점이 있으며, 여러 문자열을 붙이기 위해 CONCAT
을 중첩하여 사용해야 한다.
1
2
3
@Query("SELECT m.firstName || ' ' || m.lastName FROM Member m")
@Query("SELECT concat(m.firstName, ' ', m.lastName, '!', m.email) FROM Member m")
HQL은 파이프 연산(||
)을 지원하며, concat
메서드 또한 두 개 이상의 파라미터를 받을 수 있다.
📌 INSERT
JPQL은 기본적으로 INSERT
연산을 지원하지 않는다. JPQL은 일반적으로 SELECT
, UPDATE
, DELETE
연산만 지원한다.
1
@Query("INSERT INTO EntityA (field1, field2) SELECT b.fieldX, b.fieldY FROM EntityB b WHERE ..")
HQL는 INSERT INTO ... SELECT ...
문법을 지원한다. 대량의 데이터를 한 번에 삽입할 수 있다.
다만 일반적인 SQL처럼 INSERT INTO ... VALUES ...
는 불가능하다.
📌 결론
어쨌든 Hibernate는 구현체이다.
구현체의 의존성이 강해지면 다른 구현체로 변경 시 문제가 발생할 수 있다. JPA 명세를 따르도록 작성하는 것이 좋을 것 같다.