(1) 객체지향 쿼리 소개
모든 엔티티를 메모리에 올려두고 애플리케이션에서 특정 조건으로 검색하는 것은 현실성이 없다. 결국 데이터는 데이터베이스에 있으므로 SQL로 필요한 내용을 최대한 걸러서 조회해야 한다.
하지만 ORM을 사용하면 데이터베이스 테이블이 아닌 엔티티 객체를 대상으로 개발하므로, 검색도 테이블이 아닌 엔티티 객체를 대상으로 하는 방법이 필요하다.
종류 | 설명 |
---|---|
SQL | 데이터베이스 테이블을 대상으로 하는 데이터 중심의 쿼리 |
JPQL | - 엔티티 객체를 대상으로 하는 객체지향 쿼리. - SQL을 추상화해서 특정 DB 의 SQL에 의존하지 않는다. |
JPQL을 사용하면 JPQ는 이 JPQL 을 분석한 다음 적절한 SQL을 만들어 데이터베이스를 조회한다. 그리고 조회한 결과로 엔티티 객체를 생성해서 반환한다.
JPA가 공식 지원하는 기능
객체지향 쿼리 | 설명 |
---|---|
JPQL | Java Persistence Query Language |
Criteria 쿼리 | JPQL을 편하게 작성하도록 도와주는 API, 빌더 클래스 모음 |
네이티브 SQL | JPA 에서 JPQL 대신 직접 SQL을 사용 |
다른 객체지향 쿼리
객체지향 쿼리 | 설명 |
---|---|
QueryDSL | JPQL을 편하게 작성하도록 도와주는 빌더 클래스 모음. 비표준 오픈소스 프레임워크 |
JDBC 직접 사용, MyBatis 등 | 필요 시 JDBC 직접 사용 가능 |
가장 중요한 건 JPQL 이다. Criteria
나 QueryDSL
은 JPQL을 편하게 작성하도록 도와주는 빌더 클래스일 뿐이다.
10.1.1 JPQL 소개
JPQL(Java Persistence Query Language)
은 엔티티 객체를 조회하는 객체지향 쿼리다.
1. JPQL은 SQL을 추상화해서 특정 데이트베이스에 의존하지 않는다.
- 데이터베이스 방언만 변경하면 JPQL을 수정하지 않아도 자연스럽게 데이터베이스를 변경할 수 있다.
2.JPQL은 SQL보다 간결하다.
- 엔티티 직접 조회, 묵시적 조인, 다형성 지원으로 SQL보다 코드가 간결하다.
회원 엔티티
@Entity(name = "Member")public class Member {@Column(name = "name")private String username;// ...}
JPQL 사용 예
// 쿼리 생성String jpql = "select m from Member as m where m.username = 'kim'";List<Member> resultList =em.createQuery(jpql, Member.class).getResultList();
getResultList()
메소드를 실행하면 JPA는 JPQL을 SQL로 변환해서 데이터베이스를 조회한다.
그리고 조회한 결과로 Member
엔티티를 생성해서 반환한다.
실행된 SQL
selectmember.id as id,member.age as age,member.team_id as team,member.name as namefromMember mbmerwheremember.name = 'kim'
10.1.2 Criteria 쿼리 소개
Criteria는 JPQL을 생성하는 빌더 클래스다.
JPA 2.0 부터 Criteria를 지원한다.
장점
프로그래밍 코드로 JPQL을 작성할 수 있다.
- ex) query.select(m).where(...)
컴파일 시점에 오류를 발견할 수 있다.
- 문자기반 쿼리는 런타임 시점에 발견 가능 -> 문제
- IDE를 사용하면 코드 자동완성을 지원한다.
- 동적 쿼리를 작성하기 편하다.
Criteria 쿼리 예
// Criteria 사용 준비CriteriaBuilder cb = em.getCriteriaBuilder();CriteriaQuery<Member> query = cb.creatQuery(Member.class);//루크 클래스(조회를 시작할 클래스)Root<Member> m = query.from(Member.class);//쿼리 생성CriteriaQuery<Member> cq =query.select(m).where(cb.equal(m.get("username"), "kim));List<Member> resultList = em.createQuery(cq).getResultList();
메타모델 API
자바가 제공하는 어노테이션 프로세서 기능을 사용하면 어노테이션을 분석해서 클래스를 생성할 수 있다.
JPA는 이 기능을 사용해서 엔티티 클래스로부터 Criteria 전용 클래스를 생성하는데, 이를 메타 모델
이라 한다.
ex) Member 엔티티 클래스로부터 Member_ 라는 Criteria 전용 클래스 생성
메타모델 사용 전 | 사용 후 |
---|---|
m.get("username") | m.get(Member_.username) |
단점
- 모든 장점을 상쇄할 정도로 복잡하고 장황하다.
- 사용하기 불편하다.
- 코드를 읽기 어렵다.
10.1.3 QueryDSL 소개
QueryDSL 도 Criteria 처럼 JPQL 빌더 역할을 한다. JPA 표준은 아니다.
장점
- 코드 기반이면서 단순하고 사용하기 쉽다.
- 작성한 코드도 JPQL과 비슷해서 한눈에 들어온다.
- 스프링 데이터 프로젝트에서 지원할 정도로 기대되는 프로젝트다.
영한님께서는 QueryDSL을 매우 선호하신다고 하셨다.
//준비JPAQuery query = new JPAQuery(em);QMember member = QMember.member;//쿼리, 결과조회List<Member> members =query.from(member).where(member.username.eq("kim")).list(member);
QueryDSL
도 어노테이션 프로세서
를 사용해서 쿼리 전용 클래스를 만들어야 한다. QMember
는 Member 엔티티 클래스를 기반으로 생성한 QueryDSL 쿼리 전용 클래스.
10.1.4 네이티브 SQL 소개
JPQ는 SQL을 직접 사용할 수 있는 기능을 지원하는데 이것을 네이티브 SQL 이라 한다.
SQL은 지원하지만 JPQL이 지원하지 않는 기능을 사용할 때는 네이티브 SQL
을 사용하면 된다.
네이티브 SQL의 단점
특정 데이터베이스에 의존하는 SQL 작성
=> 데이터베이스 변경 시 네이티브 SQL도 수정해줘야 한다.
네이티브 SQL 코드
String sql = "SELECT ID, AGE, ITEM_ID, NAME FROM MEMBER WHERE NAME = 'kim'";List<Member> resultList =em.createNativeQuery(sql, Member.class).getResultList();
em.createNativeQuery()
를 사용하여 직접 작성한 SQL을 데이터베이스에 전달한다.
10.1.5 JDBC직접 사용, MyBatis 같은 SQL 매퍼 프레임워크 사용
JPA는 JDBC 커넥션을 얻는 API를 제공하지 않기 때문에 JPA 구현체가 제공하는 방법을 사용해야 한다.
하이버네이트 JDBC 획득
Session session = entityManager.unwrap(Session.class);session.doWork(new Work() {@Overridepublic void execute(Connection connection) throws SQLExcetion {// work ...}});
JPA를 우회해서 데이터베이스에 접근하기 때문에 JPA가 전혀 인식하지 못한다.
JDBC
나 마이바티스
를 사용하는 것은 모두 JPA를 우회해서 데이터베이스에 접근한다.
그럼 영속성 컨텍스트와 데이터베이스를 불일치 상태로 만들어 데이터 무결성을 훼손하는 경우가 발생할 수 있다.
따라서 JPA와 함께 사용하려면 영속성 컨텍스트를 적절한 시점에 강제로 플러시를 해줘야 한다.
이런 이슈를 해결하기 위해 SQL을 실행하기 직전에 영속성 컨텍스트를 수동으로 플러시해서 데이터베이스와 영속성 컨텍스트를 동기화해준다.
스프링 프레임워크의 AOP를 적절히 활용해서, JPA를 우회하여 데이터베이스에 접근하는 메소드를 호출할 때마다 영속성 컨텍스트를 플러시하면 깔끔하게 해결 가능하다!