JPQL은 Java Persistence Query Language의 약자입니다. 객체지향 쿼리이므로 특정 DB SQL에 의존적이지 않습니다. JPQL은 엔티티매니저에 의해 SQL로 변경되어 사용됩니다. 엔티티매니저는 프로젝트 기동 시 설정을 참고하여 어느 데이터베이스를 위한 Dialect를 사용해야 하는지 파악합니다.
JPQL은 문자열 형태의 SQL과 비슷한 구문형태로 작성하므로 익히는데 많은 시간이 소요되지 않습니다. JPQL 작성 시 주의할 점은 테이블에 질의하는 것이 아니라 엔티티 객체를 대상으로 질의하는 것이므로 사용해야 할 대상 키워드는 테이블명이 아니라 엔티티 클래스명이라는 점 입니다.
SQL이든 JPQL이든 쿼리를 문자열로 작성하면 그 자체로는 단순한 문자열이므로 컴파일 타임에서 구문 오류를 잡는 것은 불가능합니다. 쿼리를 데이터베이스에 질의해 보아야 정상적인 쿼리인지 확인할 수 있습니다. 오랫동안 이 문제는 개발자들의 골칫거리였습니다. 쿼리가 정상적으로 수행될 수 있는지 테스트하기 위해서는 DB가 존재해야 하고 그런 환경을 구성하는 것은 시간적으로나 비용적으로 테스트를 수월하게 수행하지 못하게 하는 요소입니다.
이에 따라 컴파일 타임에서 SQL 구문 오류를 잡아내기 위해서 Criteria, Querydsl 같은 기술이 등장했습니다. 이러한 기술은 메소드 기반 형태로 쿼리구문을 작성합니다. 쿼리를 메소드로 작성하면 컴파일 타임에서 타입 세이프 오류를 발견하는 것이 가능하게 됩니다. 이는 닷넷의 링크와 비슷한 개념입니다.
대표적인 JPA 구현체인 Hibernate는 자바표준인 JPA기술을 지원하고 부가적으로 Hibernate에서만 사용할 수 있는 추가적인 기능이 존재합니다. 이는 자바 표준 @Inject 어노테이션이 있는데 스프링이 @Autowired 어노테이션을 추가적으로 사용하는 모습과 비슷합니다. Hibernate를 직접 이용해서 개발한다고 말하는 경우 JPQL이라는 용어 대신 Hibernate Query Language라는 용어를 사용하여 구분 합니다.
JPQL 쿼리 문자열에서 "Emp"는 엔티티 클래스이고 "ename"은 필드변수명입니다. EntityManager의 createQuery 메소드의 파라미터로 JPQL 쿼리 구문과 대상 엔티티 클래스를 설정합니다. 메소드 체이닝 방식으로 연결해서 사용하는setParameter() 메소드의 파라미터로 위치 보유자 ":ename"에 "SMITH" 문자열을 대입하는 설정입니다. 돌려 받고자 하는 자료형에 따라 질의 실행 메소드 중 getResultList 를 선택해서 사용하고 있습니다.
쿼리문은 단순한 문자열이므로 이 시점에서 "Emp" 엔티티 클래스명을 사용하지 않고 테이블명 "emp"를 사용해도 컴파일 타임에서는 에러가 발견되지 않습니다. 이러한 문제를 해결하기 위해서 Criteria 기술이 등장했습니다.
2.2.2. Criteria
JPQL을 메소드 기반으로 작성할 수 있도록 도와주는 API로 JPQL을 생성하는 빌더입니다. 사용하기 복잡하여 잘 사용되지 않고 있습니다. 이는 대체 기술인 Querydsl이 존재하기 때문입니다.
크리테리아 기술은 잘 사용되지 않으므로 간단하게 샘플만 살펴보고 넘어가도록 하겠습니다.
SQL인 "select * fromm emp where ename='SMITH'" 구문을 메소드로 작성하고 있습니다. 보시는 것처럼 간단한 쿼리인데도 코드는 한 눈에 파악하기가 힘듭니다.
우리에게는 이를 대체하는 보다 편리한 기술인 Querydsl이 있으므로 크리테리아는 더 살펴보지 않고 넘어 가겠습니다.
2.2.3. Native SQL
JPQL 을 사용하지 않고 데이터베이스가 이해하는 SQL 쿼리를 개발자가 직접 작성해서 사용하는 방식입니다. JPA에서 지원하지 않는 쿼리를 사용해야 할 때 SQL구문을 직접 작성해서 사용해야 할 경우가 있을 수 있습니다. 예를 들어 오라클의 계층형 쿼리와 같이 DB에 의존적인 쿼리를 작성할 때가 그러한 경우입니다. JPQL과 구분하기 위해서 개발자가 직접 SQL을 작성해서 엔티티매니저에게 주고 사용하는 방식을 Native SQL을 사용한다고 표현합니다.
Native SQL 사용 예
createQuery 메소드 대신 createNativeQuery 메소드를 사용하고 있기 때문에 쿼리 구문에서 사용한 "emp"는 데이터베이스에 정의된 테이블명이고 "ename"은 칼럼명이어야 합니다.
엔티티 클래스를 파라미터로 전달하지 않고 처리하는 예제입니다.
엔티티 클래스를 파라미터로 전달하지 않았기 때문에 결과를 List<Emp>로 받을 수 없습니다. 이 경우 List<Object[]>로 결과를 받아서 처리해야 합니다.
인덱스기반 위치 지정자 '?'에 파라미터를 대입하여 쿼리를 완성하는 예제입니다.
네임드 위치 지정자 기호 ':'를 사용하여 파라미터를 대입하여 쿼리를 완성하는 예제입니다.
결과 매핑이 복잡한 경우 @SqlResultSetMaping 어노테이션을 이용하여 엔티티에 결과 매핑을 정의해 놓고 사용하는 것이 편리합니다.
다음은 사용 예제입니다.
2.2.4. Querydsl
Querydsl은 JPA 표준은 아니지만 JPQL을 작성하기 위해서 많이 사용되는 오픈 소스 프레임워크입니다. Criteria처럼 JPQL을 메소드 기반으로 작성할 수 있도록 도와주는 빌더 클래스 모음을 제공합니다.
Spring Data JPA 프로젝트도 QueryDSL을 지원하고 있습니다. JPAQuery, JPAQueryFactory를 이용하여 객체지향 쿼리를 작성할 수 있고 JPASQLQuery, SQLQuery, SQLQueryFactory 클래스를 이용하여 엔티티 없이 SQL쿼리를 직접 작성하는 것도 가능합니다. 쿼리 타입(Query Type) 클래스를 먼저 만들고 질의를 위한 메타정보로 사용하여 개발자가 질의 메소드를 작성할 때 보다 간단하게 작성할 수 있도록 도와주는 기술입니다. JPA, JDO, MongoDB, Java Collection, Hibernate Search등에서 지원하며 Spring Data JPA 프로젝트에서 지원하는 오픈소스 프로젝트 입니다.
Querydsl은 JDK 6.0 이상에서 동작합니다. Querydsl에 대한 자세한 내용은 3장부터 살펴봅니다.
2.2.5. 기타 데이터베이스 처리기술
JPA와 함께 Spring JDBC, MyBatis와 같은 기타 데이터베이스 처리기술을 병행해서 사용하는 것도 가능합니다. 일반적으로 신규프로젝트는 여러 기술을 병행 적용해서 개발하지 않지만 기존 프로젝트를 조금씩 업그레이드해 나갈 때는 병행해서 사용해야 하는 필요성이 생길 수 있습니다.
댓글 없음:
댓글 쓰기