JPA

JPQL - JPQL 소개(1)

방구석 대학생 2020. 11. 13. 20:07

"인프런 - 자바 ORM 표준 JPA 프로그래밍 강의를 듣고 작성한 글 입니다."

www.inflearn.com/course/ORM-JPA-Basic#

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다. 초급 웹 개발 프로그

www.inflearn.com

 

DB 에서 특정 조건으로 데이터를 뽑아내려면 결국 SQL 이 실행되어야 한다.

JPA 는 find 와 같이 간단하게 데이터를 검색해서 가져오는것 뿐만 아니라, 다양한 검색조건을 이용할 수 있어야 한다.

DB 에서 다양한 조건들을 설정하여 데이터를 검색하는데 있어 JPQL 을 활용할 수 있다.

 

 

JPA 는 다양한 쿼리 방법을 지원한다.

- JPA Criteria, QueryDSL : 쿼리를 자바 코드로 작성하여 JPQL 을 빌드하게 해주는 Generator 클래스의 모음이라고 생각하면된다.

- 네이티브 SQL : JPA 를 써도 ,가끔 데이터베이스 종속적인 쿼리가 나가야 할 경우 활용 할 수 있다.

- 그외 JDBC API 를 직접 사용하거나 MyBatis, SpringJdbcTemplate 과 함께 사용하는 것도 가능하다.

(거의 대부분은 JPQL 로 해결이 되나, 어쩌다 안될 때 네이티브 SQL 과 같은 것을 같이 활용해도 좋다.)

 

 

JPA 를 사용하면 Entity 객체를 중심으로 개발을 하게 된다.

여기서 검색 쿼리가 문제가 되는데, 검색을 할 때도 테이블이 아닌 Entity 객체를 대상으로 검색을 해야 한다.

-> SQL 을 짜더라도 테이블이 아닌 Entity 객체를 대상으로 쿼리를 작성할 수 있어야 한다.

모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능하므로, 애플리케이션이 필요한 데이터만 DB 에서 불러오면 결국 검색 조건이 포함된 SQL 이 필요해지는 것이다.

-> DB 에서 애플리케이션에 데이터를 올릴 때는 딱 필요한 만큼 최소한의 데이터만 검색해서 가져오는 것이 성능상 좋다.

 

위와 같은 문제를 해결하기 위해 JPA 는 SQL 을 추상화한 JPQL 이라는 객체지향 쿼리언어를 제공한다.

-> SQL 과 문법이 유사하며, ANSI 표준에서 지원하는 기본적인 SQL 문법은 다 지원을 해준다.(SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원)

SQL 은 DB 테이블을 대상으로 쿼리를 수행하는 것과 달리, JPQL 은 Entity 객체를 대상으로 쿼리를 수행한다.

-> JPQL 로 문법을 작성하면 그것이 SQL 로 번역되어서 DB 테이블로 전달된다.

 

 

그래서 JPQL 은 무엇인가?

가장 단순한 데이터 조회 방법으로는 EntityManager.find(), 객체 그래프 탐색(a,getB().getC()) 을 들 수 있겠지만, 여러개의 조건을 달고 검색을 해야 한다면 어떻게 해야할까?

(예를 들어, 나미가 18살 이상인 회원을 모두 검색해야 한다면?)

 

아래와 같은 코드를 작성해보자.

- JpaMain.java

List<Member> result = em.createQuery("select m From Member m where m.username like '%kim%' ", Member.class)
			.getResultList();
// Entity 를 대상으로 하는 쿼리, 여기서 m 은 Member Entity 자체를 뜻한다.

- Hibernate SQL

Hibernate: /* 작성했던 JPQL 이 주석으로 처리되어 나타난다.*/
    /* select
        m 
    From
        Member m 
    where
        m.username like '%kim%' */ select 
            member0_.MEMBER_ID as member_i1_6_,
            member0_.city as city2_6_,
            member0_.street as street3_6_,
            member0_.ZIPCODE as zipcode4_6_,
            member0_.USERNAME as username5_6_,
            member0_.endDate as enddate6_6_,
            member0_.startDate as startdat7_6_ 
        from
            Member member0_ 
        where
            member0_.USERNAME like '%kim%'
/* 작성한 JPQL 이 실제 SQL 로 번역되어 실행된다.
(Entity 의 매핑 정보를 읽어서 적절한 SQL 을 만들어낸다.)
*/

 

* JPQL 은 테이블이 아닌 객체를 대상으로 검색하는 객체지향 쿼리로서 SQL 을 추상화 하기 때문에 특정 DB SQL 에 의존하지 않는다

즉, JPQL 을 한마디로 정의하면 객체지향 SQL 인 것이다.

 

 

그런데 사실 JPQL 은 단순한 String 이다, 그러면 뭘 하기 어려워질까?

동적 쿼리를 만들기 어렵다. -> 실무에서 문자열을 이용하다 보면 딱 버그가 발생하기 좋다.

* 참고

- 정적 쿼리 : 변수가 바뀔 때마다 새로운 SQL 을 생성하여 DB 쿼리를 수행하는것

- 동적 쿼리 : 변수가 바뀔 때마다 파싱을 하지 않고 변수 바인딩 과정을 통해 DB 쿼리를 수행하는것

(변수 바인딩에 대해서는 이후 작성할 글에서 정리하겠다.)

 

여기서 동적 쿼리를 작성하기 힘든 JPQL 에서의 대안이 바로 Criteria 이다.

 

아래와 같이 코드를 작성해보자.

- JpaMain.java

// Criteria 사용 준비
CriteriaBuilder cb = em.getCriteriaBuilder(); // CriteraBuilder - 자바 표준에서 제공하는 클래스
CriteriaQuery<Member> query = cb.createQuery(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();

 - Hibernate SQL

Hibernate: 
    /* select
        generatedAlias0 
    from
        Member as generatedAlias0 
    where
        generatedAlias0.username=:param0 */ select
            member0_.MEMBER_ID as member_i1_6_,
            member0_.city as city2_6_,
            member0_.street as street3_6_,
            member0_.ZIPCODE as zipcode4_6_,
            member0_.USERNAME as username5_6_,
            member0_.endDate as enddate6_6_,
            member0_.startDate as startdat7_6_ 
        from
            Member member0_ 
        where
            member0_.USERNAME=?

 

위의 JPA Criteria 로 작성된 코드를 보면 뭔가 쉬운것 같으면서도 어렵다.

그런데 위와 같이 단순한걸 하면 모르겠으나, 진짜 복잡한 걸 하면 혼란이 오게된다.

SQL 의 경우 문법 자체가 심플하다, 하지만 위의 코드를 보면 query.select(m).where(cb.equal(m.get("username"), "kim")); 과 같이 쉽게 알아보기 힘든 코드가 나오는 경우가 있다.

 

하지만 그래도 이런식으로 코드를 작성하면 얻을 수 있는 장점이 있다.

예를 들어 단순히 SQL 문법 작성을 할 때에 비해서 애플리케이션을 작성하기 전에 단순 오타와 같은 컴파일 오류를 발견하기 쉽다.

또한 동적 쿼리를 작성하기에 훨씬 유리하다.

 

아래의 코드와 같이 동적 쿼리를 작성해 줄 수 있다.(쿼리를 if 조건문 내부에서 실행시키는 경우)

- JpaMain.java

String username = "temporary";
if(username != null){
	cq.where(cb.equal(m.get("username"), "kim"));
}

 

그런데 여기서 Criteria 의 또 하나의 단점으로, 뭔가 SQL 같지가 않다.

강의를 직접 해주셨던 강사님 말씀으로는, 코드를 알아보기가 쉽지 않아 유지보수가 힘들다고 한다.

그냥 직접 실무에서 쓰기 보다는 이런게 있다는 정도만 알아두면 될 것 같다.

 

* Criteria 요약

- 문자열이 아닌 자바 코드로 JPQL 을 작성할 수 있다.

- JPQL 빌더 역할을 하며, JPA 의 공식 기능이다.

- 그러나 단점으로 너무 복잡하고 실용성이 없다.

- Criteria 대신에 QueryDSL(오픈소스 라이브러리) 을 사용하는 것을 권장한다.

 

그런데 실무에서 생각해봤을 때 동적 쿼리를 많이 사용해야 하는 부분도 있고, 또한 이점도 존재한다.

-> 자바 코드로 작성하면 컴파일 때 오류가 나기 때문에 SQL 문법 때문에 오류가 나는 일은 거의 생기지 않는다는 것이다.

 

 

다음글에서는 QueryDSL, 네이티브 SQL, JDBC Template 및 SpringJdbcTemplate 등에 대해 알아보자.