[JPA-Basic][10-1] 객체지향 쿼리 언어1 - 기본 문법

kiteB·2021년 10월 22일
0

JPA

목록 보기
11/28
post-thumbnail

[ 객체지향 쿼리 언어 소개 ]

JPA는 복잡한 검색 조건을 사용해서 엔티티 객체를 조회하는 다양한 쿼리 기술을 지원한다.

💡 JPA가 지원하는 다양한 쿼리 방법

  • JPQL: 표준 문법
  • JPA Criteria, QueryDSL: 문자가 아닌 프로그래밍 코드로 JPQL 작성
  • 네이티브 SQL: 표준 SQL 문법을 벗어나는 쿼리를 작성할 수 있는 기능 제공
  • JDBC API 직접 사용, MyBatis와 같은 SQL 매퍼 프레임워크 사용

1. JPQL

엔티티 객체를 조회하는 객체지향 쿼리

소개

  • 가장 단순한 조회는 다음 두 가지를 활용하면 된다.
    • EntityManager.find()
    • 객체 그래프 탐색 (a.getB().getC())
  • 하지만 검색을 할 때 문제가 발생한다.

필요성

  • JPA를 사용하면 테이블이 아닌 엔티티 객체를 중심으로 개발한다.
    • 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능하다!
  • 결국 애플리케이션이 필요한 데이터만 DB에서 불러오려면 검색 조건이 포함된 SQL이 필요하다.

특징

  • JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공한다.
    • 이 JPQL은 SQL과 문법이 유사하며, 엔티티 객체를 대상으로 쿼리한다.
  • SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.

예제

List<Member> result = em.createQuery(
    "select m From Member m where m.name like '%hello%'", 
     Member.class
).getResultList();
  • Member는 테이블이 아니라 엔티티이다.
실행된 SQL 
    select 
        m.id as id, 
        m.age as age, 
        m.USERNAME as USERNAME, 
        m.TEAM_ID as TEAM_ID 
    from 
        Member m 
    where 
        m.age>18

2-1. Criteria (비추🙅🏻‍♀️)

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

  • JPQL 빌더 역할
  • JPA 공식 기능
  • 단점: 너무 복잡하고 실용성이 없다.
//Criteria 사용 준비
CriteriaBuilder cb = em.getCriteriaBuilder();
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()

2-2. QueryDSL ⭐

문자가 아닌 자바코드로 JPQL을 작성할 수 있으며,
실용성이 없는 Criteria 대신 오픈 소스 라이브러리인 QueryDSL을 사용한다!

  • JPQL 빌더 역할
  • 쿼리를 String 문자가 아닌 자바 코드로 작성하기 때문에 컴파일 시점에 문법 오류를 찾을 수 있다.
  • 동적쿼리 작성이 편리하고 코드가 단순하고 쉽다.
  • 실무에서 많이 사용되는 방식!
//JPQL
//select m from Member m where m.age > 18
JPAFactoryQuery queryFactory= new JPAQueryFactory(em); 
QMember m = QMember.member;
List<Member> list = queryFactory.selectFrom(m)
                        .where(m.age.gt(18))
                        .orderBy(m.name.desc())
                        .fetch()
  • QMemberMember 엔티티 클래스를 기반으로 생성한 QueryDSL 쿼리 전용 클래스

Criteria 코드와 비교하면 훨씬 직관적!


3. 네이티브 SQL

JPA가 제공하는 SQL을 직접 사용하는 기능

  • JPQL로 해결할 수 없는 특정 데이터베이스에 의존적인 기능을 사용할 때 쓰인다.
  • Ex) 오라클 CONNECT BY, 특정 DB만 사용하는 SQL 힌트
String sql = "SELECT ID, AGE, TEAM_ID, NAME FROM MEMBER WHERE NAME = 'kim'"; 
List<Member> resultList = em.createNativeQuery(sql, Member.class).getResultList();

4. JDBC 직접 사용, SpringJdbcTemplate 등

JPA를 사용하면서 JDBC 커넥션을 직접 사용하거나,
SpringJdbcTemplate, MyBatis 등을 함께 사용할 수 있다.

  • 단, 영속성 컨텍스트를 적절한 시점에 강제로 플러시가 필요하다.
  • Ex) JPA를 우회해서 SQL을 실행하기 직전에 영속성 컨텍스트 수동 플러시
String sql = "SELECT ID, AGE, TEAM_ID, NAME FROM MEMBER WHERE NAME = 'kim'"; 
List<Member> resultList = em.createNativeQuery(sql, Member.class).getResultList(); 

[ 기본 문법과 쿼리 API ]

1. JPQL 소개

  • JPQL은 객체지향 쿼리 언어이다. → 테이블이 아닌, 엔티티 객체를 대상으로 쿼리한다.
  • JPQL은 SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.
  • JPQL은 결국 SQL로 변환된다.


2. JPQL 문법

  • select m from Membebr as m where m.age > 18
    • Member는 테이블이 아닌 엔티티
  • 엔티티와 속성은 대소문자 구분 ⭕ (Member, age)
  • JPQL 키워드는 대소문자 구분 ❌ (SELECT, FROM, where)
  • 테이블 이름이 아닌 엔티티 이름을 사용해야 한다.
  • 별칭은 필수(m) (as는 생략 가능)

집합과 정렬


3. TypeQuery, Query

//TypeQuery
TypedQuery<Member> query = em.createQuery("select m from Member m", Member.class);
TypedQuery<String> query1 = em.createQuery("select m.username from Member m", String.class);

//Query
Query query2 = em.createQuery("select m.username, m.age from Member m");
  • TypeQuery: 반환 타입이 명확할 때 사용
  • Query: 반환 타입이 명확하지 않을 때 사용

4. 결과 조회 API

다음 메서드들을 호출하면 실제 쿼리를 실행해서 데이터베이스를 조회한다.

  • query.getResultList(): 결과가 하나 이상일 때, 리스트 반환
    • 결과가 없으면 빈 리스트 반환
  • query.getSingleResult(): 결과가 정확히 하나일 때, 단일 객체 반환
    • 결과가 없으면: javax.persistence.NoResultException
    • 둘 이상이면: javax.persistence.NonUniqueResultException

5. 파라미터 바인딩 - 이름 기준, 위치 기준

이름 기준 파라미터

  • 앞에 :를 사용한다.
SELECT m FROM Member m where m.username = :username 
query.setParameter("username", usernameParam);

위치 기준 파라미터

  • ? 다음에 위치 값을 주면 된다. (위치 값은 1부터 시작)
  • 웬만하면 사용하지 말기! (권장하지 않는다.)
SELECT m FROM Member m where m.username = ?1 
query.setParameter(1, usernameParam)

[ Reference ]

profile
🚧 https://coji.tistory.com/ 🏠

0개의 댓글