서론
이전 글에 나왔던 키워드 중 JPQL에 대해 조금이라도 알아보기 위해 정리하는 글
JPQL(Java Persitence Query Language)이란?
엔티티 객체를 대상으로 하는 객체지향 쿼리로 SQL을 추상화한 객체지향 쿼리이며, 작성된 JPQL은 SQL로 변환된다.
기존 JPA의 메서드 호출만으로는 섬세한 쿼리 작성이 어렵다는 문제를 해결하기 위해 JPQL이 나타나게 되었으며 SQL을 추상화했기 때문에 특정 데이터베이스 SQL에 의존하지 않는다는 장점이 있다.
SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN을 지원한다.
예시
Member 객체를 대상으로 이름에 "bazzi"가 포함된 모든 회원을 검색하는 쿼리
String jpql = "select m From Member m where m.name like ‘%bazzi%'";
List<Member> result = em.createQuery(jpql, Member.class).getResultList();
for (Member member : result) {
System.out.println("Member = " + member);
}
SQL과의 차이점
1. 대소문자 구분
엔티티와 필드는 대소문자를 구분한다.
위의 예시에서는, Member와 name은 대소문자를 반드시 구분해주어야 한다.
select, from, as와 같은 키워드들은 대소문자를 구분하지 않아도 된다.
2. 엔티티 이름
Member은 Entity 이름으로, @Entity(name = "Menber")로 설정 가능하며, 생략 시 기본 값으로 클래스 이름을 사용한다.
3. 별칭(Alias)
JPQL에서 Alias는 필수로 명시해야 하며, AS 키워드는 생략 가능하다.
구현 방법
구현 방법에는 TypedQuery, Query가 있다.
TypedQuery는 변환 타입이 명확할 때, 아래처럼 타입 정보를 작성하여 리턴받을 수 있다.
TypedQuery<Member> query1 = em.createQuery("select m from Member m", Member.class);
TypedQuery<String> query2 = em.createQuery("select m.username from Member m", String.class);
기본적으로 엔티티 타입을 기입했고, query2와 같이 리턴 타입이 username과 같이 명확할 경우 타입 정보를 기입할 수 있다.
Query는 데이터 검색 결과의 타입을 명시하지 않는다.
Query query3 = em.createQuery("select m.username, m.age from Member m");
조회한 결과는 getResultList()혹은 getSingleResult()와 같은 API를 사용하여 List 혹은 단일 객체를 반환한다.
TypedQuery<Member> query1 = em.createQuery("select m from Member m", Member.class);
List<Member> resultList = query1.getResultList();
for (Member m : resultList) {
System.out.println("m = " + m.getUsername());
}
TypedQuery<Member> query2 = em.createQuery("select m from Member m where m.id = 1L", Member.class);
Member singleResult = query2.getSingleResult();
System.out.println("singleResult = " + singleResult.getUsername());
파라미터 바인딩
이름 기준 파라미터
Member result = em.createQuery("select m from Member m where m.username = :username", Member.class)
.setParameter("username", "member1")
.getSingleResult();
System.out.println("result = " + result.getUsername());
이름을 기준으로 파라미터를 바인딩하며, 콜론(:)을 사용해 데이터가 추가될 곳을 지정하며
query.setParameter()을 호출하여 데이터를 동적으로 바인딩한다.
위치 기준 파라미터
Member result = em.createQuery("select m from Member m where m.username = ?1", Member.class)
.setParameter(1, "member1")
.getSingleResult();
System.out.println("result = " + result.getUsername());
위치 기준 파라미터를 사용하려면 ? 다음에 위치 값을 주면 된다.
위치가 변경될 가능성이 높기 때문에, 위치 기준 파라미터보다는 이름 기준 파라미터를 사용하는 것을 권장한다고 한다.
JPQL의 문제점
객체지향 쿼리이지만, 문자열을 통해 JPQL을 작성하는 것이기 때문에 오타가 발생하기 쉽고, 유지보수가 어렵다.
또한 문자열 자체의 검증이 컴파일 단계에서 불가능하기 때문에, 쿼리가 직접 실행되는 런타임에서 해당 오류를 발견할 수 있다. 물론 이는 테스트 코드에서 해소할 수 있는 부분이 있지만, 실제 운영 시에도 발생할 수 있다는 불안을 해소할 수 없다.
참조
https://dev-coco.tistory.com/141
https://ittrue.tistory.com/270
https://ittrue.tistory.com/292
https://velog.io/@soyeon207/QueryDSL-%EC%9D%B4%EB%9E%80
관련 포스팅
2023.04 ~ 백엔드 개발자의 기록
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!