10. 객체지향 쿼리 언어(1) - JPA에서 지원하는 엔티티 조회 방법


1. 객체지향 쿼리

  • JPA에서 엔티티를 조회하는 방법?
    • EntityManager.find() 메소드
    • 조건부 탐색을 하고싶을 땐? (e.g. 나이가 18세 이상인 회원을 조회)
      • 모든 엔티티를 조회 후에 조건을 적용해야 함
      • 데이터베이스 조회 시 필요한 내용을 최대한 거를 필요가 있음
  • JPQL(Java Persistence Query Language)
    • 테이블이 아닌 객체를 대상으로 검색하는 객체지향 쿼리
    • SQL을 추상화 해서 특정 데이터베이스 SQL에 의존하지 않음
  • Criteria 쿼리
    • JPQL을 편하게 작성하도록 도와주는 API, 빌더 클래스 모음
  • 네이티브 SQL
    • JPA에서 JPQL대신 직접 SQL을 사용할 수 있도록 해줌
  • QueryDSL
    • Criteria 쿼리처럼 JPQL을 편하게 작성하도록 도와주는 빌더 클래스 모음
    • 비표준 오픈소스 프레임워크
  • JDBC 직접 사용
  • MyBatis와 같은 SQL 매퍼 프레임워크를 사용

1. JPQL

  • 엔티티 객체를 조회하는 객체지향 쿼리
  • SQL과 비슷한 문법, ANSI 표준 SQL이 제공하는 기능을 유사하게 지원
  • SQL보다 간결한 문법
    • 엔티티 직접 조회
    • 묵시적 조인
    • 다형성 지원
  • SQL을 추상화 했기 때문에 특정 데이터베이스에 의존하지 않음
    • 데이터베이스 방언(Dialect)만 변경하면 JPQL을 수정하지 않아도 데이터베이스 변경 가능
  •   String jpql = "select m from Member as m where m.username = 'kim'";
      List<Member> result = em.createQuery(jpql, Member.class).getResultList();
      /*
        SELECT 
            member.id as id,
            member.age as age,
            member.team_id as team,
            member.name as name,
        FROM
            MEMBER member
        WHERE
            member.name = "kim"
      */
    
    • m.username과 같이 데이터베이스 컬럼명이 아닌 엔티티의 필드명으로 조회

2. Criteria 쿼리

  • JPQL을 생성하는 빌더 클래스
  • 문자가 아닌 프로그래밍 코드(함수 호출)로 JPQL 작성이 가능함
    • e.g. query.select(m).where(…)
    • 이게 왜 장점이냐?
      • JPQL을 직접 작성할 경우 쿼리문에 오타가 있어도 런타임 시점에 오류가 발생함(문자기반 쿼리의 단점)
  • 장점
    • 컴파일 시점에 오류 발견이 가능
    • IDE를 사용하면 코드 자동완성을 지원
    • 동적 쿼리를 작성하기 편함
  • 단점
    • 너무 복잡하고 실용성이 없음
  •   //String jpql = "select m from Member as m where m.username = 'kim'";
    
      //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();
    
  • Critera 대신 QueryDSL 사용을 권장

3. QueryDSL

  • Criteria와 마찬가지로 JPQL 쿼리 빌더 역할
  • 코드 기반이면서 단순하고 사용이 쉬움
  • 실무에서 사용 권장
  •   //준비
      JPAQuery query = new JPAQuery(em);
      QMember member = QMember.member;
    
      //쿼리, 결과 조회
      List<Member> members = 
          query.from(member).where(member.username.eq("kim")).list(member);
    
  • 어노테이션 프로세서를 사용해서 쿼리 전용 클래스(QMember)를 만들어야 함

4. 네이티브 SQL

  • SQL을 직접 사용
  • JPA가 해결할 수 없는 특정 데이터베이스에 의존적인 문법을 사용해야 할 때
    • e.g. 오라클(CONNECTED BY), 특정 데이터베이스에서만 동작하는 SQL 힌트
  • 데이터베이스를 변경할 경우 쿼리도 수정해야 함
  •   String sql = "SELECT ID, AGE, TEAM_ID, NAME FROM MEMBER WHERE NAME = 'kim'";
      List<Member> resultList = 
              em.createNativeQuery(sql, Member.class).getResultList();
    

5. JDBC 직접 사용, Mybatis와 같은 SQL 매퍼 프레임워크 사용

  • JDBC를 직접 사용하고 싶을 때?
    • JPA는 JDBC 커넥션을 획득하는 API를 제공하지 않음
    • JPA 구현체에서 제공하는 방법을 사용해야 함
    •   //하이버네이트에서 JDBC 커넥션 획득
        Session session = em.unwrap(Session.class);
        session.doWork(new Work(){
          @Override
          public void execute(Connection connection) throws SQLException {
            // work..
          }
        });
      
  • JDBC를 직접 사용하거나 Mybatis를 사용하는거 모두 JPA를 우회해서 데이터베이스를 접근하는 것
    • JPA의 영속성 컨텍스트와 데이터베이스가 불일치 상태가 될 수 있음
    • 따라서 SQL 실행 전 반드시 flush()를 통해 영속성 컨텍스트를 데이터베이스와 동기화 해야 함
    • 스프링 프레임워크에서는 JPA와 Mybatis를 쉽게 통합이 가능함
    • 스프링 프레임워크의 AOP를 활용해서 JPA를 우회하여 데이터베이스에 접근하는 메소드를 호출할 때마다 영속성 컨텍스트를 플러시 해주면 됨





© 2020.02. by blupine