JPA - JPQL1

송현진·2023년 7월 30일
0

Jpa

목록 보기
9/9
post-thumbnail
post-custom-banner

JPQL

  • 가장 단순한 조회 방법

    • EntityManager.find()
    • 객체 그래프 탐색(a.getB().getC())
  • JPA를 사용하면 엔티티 객체를 중심으로 개발

  • 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색

  • 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능

  • 필요한 데이터만 DB에서 불러오려면 검색 조건이 포함된 SQL이 필요

SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공
SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원

JPQL은 엔티티 객체를 대상으로 쿼리
SQL은 데이터베이스 테이블 대상으로 쿼리

Criteria

문자가 아닌 자바코드로 JPQL 작성가능
JPQL 빌더 역할, JPA 공식 기능
너무 복잡하고 실용성이 없는 단점 존재
QueryDSL 권장!

⭐ QueryDSL

문자가 아닌 자바코드로 JPQL 작성가능
JPQL 빌더 역할
컴파일 시점에 문법 오류 찾을 수 있음
단순하고 쉬워서 동적쿼리 작성 편하다(실무 사용 권장)

네이티브 쿼리

JPA가 제공하는 SQL을 직접 사용하는 기능
JPQL로 해결할 수 없는 특정 데이터베이스에 의존적인 기능
ex) 오라클 CONNECT BY, 특정 DB만 사용하는 SQL 힌트

JPQL 문법

Member member = new Member();
member.setUsername("member1");
member.setAge(10);
em.persist(member);

TypedQuery<Member> query1 = em.createQuery("select m from Member m", Member.class);
List<Member> members = query1.getResultList();
for (Member member1 : members) {
    System.out.println("members usernames = "+ member1.getUsername());
}
----------------------------------------------------------------------------
TypedQuery<Integer> query2 = em.createQuery("select m.age from Member m", Integer.class);
int age = query2.getSingleResult();
System.out.println("age = "+ age);
---------------------------------------------------------------------------
// select m from Member m where m.username = :username 도 가능
Member result = em.createQuery("select m from Member m where m.username = ?1", Member.class)
    .setParameter(1, "member1")
    .getSingleResult();
System.out.println("result = "+result.getUsername());

프로젝션

SELECT 절에 조회할 대상을 지정하는 것
대상 : 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자 등 기본 데이터 타입)
Distinct로 중복 제거

Select m From Member m -> 엔티티 프로젝션
Select m.team From Member m -> 엔티티 프로젝션
Select m.address From Member m -> 임베디드 타입 프로젝션
Select m.username, m.age From Member m -> 스칼라 타입 프로젝션

// 엔티티 프로젝션은 전부 다 영속성 컨텍스트에 관리됨
List<Member> result = em.createQuery("select m from Member m", Member.class)
    .getResultList();
Member findMember = result.get(0);
findMember.setAge(20);
----------------------------------------------------------------------------
// 둘 다 inner join으로 쿼리가 나가는데 이런 방식은 별로 좋지 않음(묵시적 조인)
List<Team> result = em.createQuery("select m.team from Member m ", Team.class)
    .getResultList();
// JPQL도 쿼리 나가는 것과 비슷하게 써줘야 됨. 성능에 영향을 줄 수 있는 요소가 많기 때문(명시적 조인)
List<Team> result = em.createQuery("select t from Member m join m.team t", Team.class)
    .getResultList();
----------------------------------------------------------------------------
// 한계 - select address from Address o 는 안됨(소속되어 있기 때문)
em.createQuery("select o.address from Order o", Address.class)
    .getResultList();
----------------------------------------------------------------------------
// 일반 SQL과 가장 흡사
em.createQuery("select distinct m.username, m.age from Member m")
    .getResultList();

여러값 조회

  1. Query 타입으로 조회
    위 코드들!
  2. Object[] 타입으로 조회
List resultList = em.createQuery("select distinct m.username, m.age from Member m")
    .getResultList();
Object o = resultList.get(0);
Object[] result = (Object[]) o;
----------------------------------------------------------------------------
List<Object[]> resultList = em.createQuery("select distinct m.username, m.age from Member m")
    .getResultList();
Object[] result = resultList.get(0);
System.out.println("username = "+ result[0]);
System.out.println("age = "+ result[1]);
  1. new 명령어로 조회
  • 단순 값을 DTO로 바로 조회
  • 패키지 명을 포함한 전체 클래스명 입력
  • 순서와 타입이 일치하는 생성자 필요
List<MemberDTO> result = em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member m", MemberDTO.class)
    .getResultList();
MemberDTO memberDTO = result.get(0);
System.out.println("memberDTO username = "+ memberDTO.getUsername());
System.out.println("memberDTO age = "+ memberDTO.getAge());

페이징

JPA는 페이징을 두 API로 추상화

  • setFirstResult(int startPosition) : 조회할 시작 위치(0부터 시작)
  • setMaxResults(int maxResult) : 조회할 데이터 수
List<Member> result = em.createQuery("select m from Member m order by m.age desc", Member.class)
    .setFirstResult(1)
    .setMaxResults(10)
    .getResultList();

페이징 쿼리

조인

내부조인

Select m from Member m [INNER] Join m.team t;

외부조인

Select m from Member m LEFT[OUTER] Join m.team t;

세타조인

Select count(m) from Member m, Team t where m.team = t.id";

ON 절

  1. 조인 대상 필터링
// 회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인
// JPQL
select m, t from member m Left Join m.team t on t.name = 'A';

// SQL
select m.*, t.* from member m Left Join Team t ON m.team_id = t.id and t.name = 'A';
  1. 연관관계 없는 엔티티 외부 조인(중요)
// 회원의 이름과 팀의 이름이 같은 대상 외부 조인
// JPQL
select m, t from member m Left Join Team t ON m.team = t.id";

//SQL
select m.*, t.* from member m Left Join Team t ON m.team = t.id";

서브쿼리

where, having 절에서만 서브쿼리 사용 가능
select 절에서도 능

한계
from 절의 서브쿼리는 현재 JPQL에서 불가능

String query = "select mm.age, mm.username 
		from (select m.age, m.username from Member m) as mm";

대안
조인으로 풀 수 있으면 풀어서 해결

지원 함수

  • [NOT] EXISTS (subquery) : 서브쿼리에 결과가 존재하면 참
  • {ALL | ANY | SOME} (subquery)
    • ALL 모두 만족하면 참
    • ANY, SOME - 같은 의미, 조건을 하나라도 만족하면 참
  • [NOT] IN (subquery) : 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참

JPQL 타입 표현

문자 - 'HELLO', 'She''s'
숫자 - 10L(Long), 10D(Double), 10F(Float)
Boolean - True, False
ENUM - jpabook.MemberType.Admin(패키지명 포함)
엔티티 타입 - TYPE(m) = Member(상속관계에서 사용)
표준 SQL 다 지원한다고 보면 됨

조건식 - CASE

기본

select
	case when m.age <= 10 then '학생요금'
    	 when m.age >= 60 then '경로요금'
         else '일반요금'
    end
from Member m

COALESCE - 하나씩 조회해서 null이 아니면 반환
NULLIF - 두 값이 같으면 null 반환, 다르면 첫번째 값 반환

select coalesce(m.username, '이름 없는 회원') from Member m

select nullif(m.username, '관리자') from Member m

기본 함수

표준함수(데이터베이스와 관계없이 쓰면 됨)

  • CONCAT (문자열 두 개 더하기)
  • SUBSTRING (문자열 자르기)
  • TRIM (공백 제거)
  • LOWER, UPPER (대소문자로 변경)
  • LENGTH (문자의 길이)
  • LOCATE ( locate('de', 'abcdef') 하면 4(숫자)를 리턴, d 시작점이 4번째이기 때문)
  • ABS, SQRT, MOD (절대값, 제곱근, 첫 번째 매개변수를 두 번째 매개변수로 나눈 후 나머지)
  • SIZE, INDEX(JPA 용도) (안쓰는 게 좋음)

사용자 정의 함수
데이터 베이스 방언에 추가해야 함

  • 사용하는 DB 방언을 상속받고, 사용자 정의 함수를 등록
select function('group_concat', i.name) from Item i;
profile
개발자가 되고 싶은 취준생
post-custom-banner

0개의 댓글