[자바 ORM 표쥰 JPA 프로그래밍] 19일차 - JPQL
JPA는 엔티티를 사용하여 데이터들을 관리한다.
하지만 필드값으로 조건을 부여하는 이러한 방법 이외에도 다양한 조건을 통해 데이터를 조회해야하는데
이러면 한계가 있기 때문에 JPA는 JPQL, Native 등 다양한 조회방법을 제공해준다.
final Member member = em.find(Member.class, "ID");
1. JPQL
JPQL은 기존의 SQL를 작성하여 SELECT 하는 방식과 동일한데 다른점이라고 한다면 엔티티를 기준으로 SQL를 작성한다.
또한 데이터베이스마다 쿼리의 방언이 다 다르며 사용하는 함수가 다르기 때문에 이를 보완 해준다.
@Data
@Entity
public class Member {
@Id
@GeneratedValue
private long id;
@Column(name = "MEMBER_NM")
private String name;
}
이번 예시로 사용될 엔티티
public static void login(EntityManager em){
// JPQL 쿼리를 작성
String jpql = "select m from Member as m where m.name = 'test0'";
// 쿼리를 가지고 조회
final List<Member> resultList = em.createQuery(jpql, Member.class).getResultList();
System.out.println("resultList = " + resultList.get(0).getName());
}
JPQL를 가지고 DB로부터 데이터를 SELECT 한 코드이다.
쿼리 작성 방식은 ANSI 쿼리와 같이 작성하며 조회하는 주체는 테이블명이 아닌 엔티티 명과 필드로 조회해야한다.
쿼리를 많이 작성해봤다면 위 쿼리문만 봐도 어떤 값이 조회될지 예상이 된다.
2. Criteria
Criteria API는 기존의 JPQL를 좀 더 사용하기 편하게 제공해주는 빌더 클래스이다.
public static void login(EntityManager em){
// Criteria 빌더 준비
CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<Member> query = cb.createQuery(Member.class);
// ROOT 클래스 (조회를 시작할 클래스)
final Root<Member> m = query.from(Member.class);
// 쿼리를 생성한다.
final CriteriaQuery<Member> cq = query.select(m).where(cb.equal(m.get("name"), "test0"));
// 생성한 쿼리로 조회
final List<Member> resultList = em.createQuery(cq).getResultList();
System.out.println("resultList = " + resultList.get(0).getName());
}
JPQL은 직업 쿼리를 작성하여 조회하는 방식이라면 Criteria 는 빌더 패턴을 사용하여 쿼리를 작성한다.
이렇게 작성하면 런타임 환경에서 쿼리의 문제를 확인해야만 하는
JPQL과는 다르게 컴파일 환경에서 쿼리의 문제를 확인할 수 있다.
또한 동적 쿼리 작성하기에 편하기 때문에 비교적 안전하다고 할수 있다.
하지만 딱봐도 저게 어떤 쿼리인지 확인하기가 어렵기 때문에 가독성에 문제가 있을수 있다.
3. QueryDSL
QueryDSL 또한 JPQL을 보다 편하게 사용하기 위한 API이다.
하지만 QueryDSL은 JPA의 표준이 아닌 오픈소스 프로젝트이다보니 별도의 의존성 라이브러리를 추가해주어야한다.
스프링 데이터에서 지원해주고 다양한 문법을 지원해서 Criteria 보다 선호한다고 한다..
public static void login(EntityManager em){
// 준비
JPAQuery query = new JPAQuery(em);
QMember member = QMember.member;
// 쿼리, 결과조회
List<Member> members =
query.from(member)
.where(member.name.eq("test0"))
.list(member);
}
지금은 저 QMember 가 메타 모델 API를 통해서 자바가 제공하는 어노테이션 프로세서의 기능을 사용하여 Member 엔티티 클래스로부터 만들어져야하는데 아직은 작성이 안되어 안될 것이다..
Criteria API와 비해 확실히 가독성이 좋아진게 보인다.
쿼리 조회시 어떤 조건으로 조회되는지 잘보인다.
4. Native SQL
Native SQL은 정말 순수하게 SQL를 작성하여 데이터를 조회하는 방식이다.
아무리 JPA가 다양한 데이터베이스의 방언들을 지원 해준다고 해도 ORACLE의 CONNECT BY, 특정 데이터베이스의 힌트등 공용하지않는 특수 기능을 사용하기 위해서는 순수 SQL를 작성해야하기 때문이다.
public static void login(EntityManager em){
// SQL 작성
String sql = "SELECT ID, MEMBER_NM FROM MEMBER WHERE MEMBER_NM = 'test0'";
// 조회
final List resultList = em.createNativeQuery(sql, Member.class).getResultList();
System.out.println("resultList = " + resultList.get(0));
}
em.createNativeQuery 로 조회하면 된다.
심플한 방식이지만 이렇게 사용할 경우 해당 데이터베이스에 종속된 다는 것이다.
이것은 JPA의 장점을 훼손하기 때문에 각별한 주의가 필요하다.
5. JDBC, Mybatis 등
JPA를 사용하지 않고 JDBC 커넥션에 다이렉트로 접근하기 위한 방법도 제공해준다.
public static void login(EntityManager em){
final Session session = em.unwrap(Session.class);
session.doWork(new Work() {
@Override
public void execute(Connection conn) throws SQLException {
final PreparedStatement pst = conn.prepareStatement("SELECT * FROM MEMBER WHERE MEMBER_NM = 'test0'");
final ResultSet resultSet = pst.executeQuery();
// 조회 코드 ...
}
});
}
하지만 이렇게하여 데이터를 조회할 경우 JPA를 우회하여 DB에 접근하는 방식이기 때문에 영속성 컨텍스트에 쌓이지않는다.
즉 JPA의 영속성 컨텍스트와 DB와의 무결성 이슈가 생길 가능성이 있다.
물론 JPA를 우회해서 SQL을 실행하기 직전에 영속성 컨텍스트를 수동으로 플러시에 해서 동기화시키면 해결할 수 있다.