JAN's History
JPA기초 다지기_JPA 객체 지향 쿼리 본문
JPQL소개 (JPA + SQL)
- 가장 단순한 조회 방법
- EntityManager.find()
- 객체 그래프 탐색을 할 수 있어 엔티티 객체를 중심으로 개발 가능
- 문제는 검색 쿼리인데, 테이블이 아닌 엔티티 객체를 대상으로 검색
- 그러나 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능
- 애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검색 조건이 포함된 SQL이 필요
- JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공
- SQL과 문법 유사
- SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원
- JPQL은 엔티티 객체를 대상으로 쿼리 vs SQL은 데이터베이스 테이블을 대상으로 쿼리
- SQL을 추상화해서 특정 데이터베이스 SQL에 의존 X
- JPQL을 한마디로 정의하면 객체 지향 SQL
import static org.assertj.core.api.Assertions.*;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
class JpqlTest {
@DisplayName("JQPL 테스트")
@Test
void jpqlTest() {
final EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
final EntityManager em = emf.createEntityManager();
final EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
final Member memberA = new Member(1L, null, "Member A");
em.persist(memberA); // 영속 상태
final Member memberB = new Member(2L, null, "Member B");
em.persist(memberB);
final Member memberC = new Member(3L, null, "C");
em.persist(memberC);
em.flush();
em.clear();
final String jpql = "SELECT m FROM Member m WHERE m.name LIKE '%Member%'";
final List<Member> members = em.createQuery(jpql, Member.class)
.getResultList();
assertThat(members).hasSize(2);
transaction.commit();
} catch (final Exception e) {
System.out.println(e.getMessage());
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
}
- 엔티티와 속성은 대소문자 구분(Member, username)
- JPQL 키워드는 대소문자 구분 안함
- 테이블 이름이 아닌 엔티티 이름을 사용해야 함.
- 별칭은 필수
결과조회 API
- query.getResultList(): 결과가 하나 이상, 리스트 반환
- query.getSingleResult(): 결과가 정확히 하나, 단일 객체 반환(정확히 하나가 아니면 예외 발생)
파라미터 바인딩 - 이름 기준, 위치 기준
SELECT m
FROM Member m
WHERE m.username = :username query.setParameter("username", usernameParam);
SELECT m
FROM Member m
WHERE m.username = ?1 query.setParameter(1, usernameParam);
페이징 API
- JPA는 페이징을 다음 두 API로 추상화
- setFirstResult(int startPosition) : 조회 시작 위치 (0부터 시작)
- setMaxResult(int maxResult): 조회할 데이터 수
import static org.assertj.core.api.Assertions.*;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.transaction.Transactional;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@Transactional
class JpqlTest {
@DisplayName("페이징 테스트")
@Test
void pagingTest() {
final EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
final EntityManager em = emf.createEntityManager();
final EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
for (long i = 0; i < 40; i++) {
em.persist(new Member(i, null, "Member " + i));
}
em.flush();
em.clear();
final String jpql = "SELECT m FROM Member m ORDER BY m.name DESC";
final List<Member> members = em.createQuery(jpql, Member.class)
.setFirstResult(0)
.setMaxResults(20)
.getResultList();
assertThat(members).hasSize(20);
transaction.commit();
} catch (final Exception e) {
System.out.println(e.getMessage());
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
}
select member0_.id as id1_0_,
member0_.name as name2_0_,
member0_.team_id as team_id3_0_
from Member member0_
order by member0_.name DESC limit ?
페이징 API - SQL / Oracle 방언
집합과 정렬
SELECT COUNT(m), // 수
SUM (m.age), // 합
AVG (m.age), // 평균
MAX (m.age), // 최대
MIN (m.age) // 최소
from
Member m;
- GROUP BY, HAVING
- ORDER BY
조인
// 내부 조인
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.username = t.name;
페치 조인 (현업에서 자주 사용)
- 엔티티 객체 그래프를 한번에 조회하는 방법
- N+1의 문제를 해결할 수 있어서 유용함!
// JPQL
SELECT m
FROM Member m
join fetch m.team;
// SQL
SELECT M.*, T.*
FROM MEMBER M
INNER JOIN TEAM T ON M.TEAM_ID = T.ID;
import static org.assertj.core.api.Assertions.*;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.transaction.Transactional;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@Transactional
class JpqlTest {
@DisplayName("페치조인 테스트")
@Test
void fetchJoinTest() {
final EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
final EntityManager em = emf.createEntityManager();
final EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
final Team teamA = new Team(1L, "TEAM A");
final Team teamB = new Team(2L, "TEAM B");
em.persist(teamA);
em.persist(teamB);
final Member memberA = new Member(1L, teamA, "MEMBER A");
final Member memberB = new Member(2L, teamB, "MEMBER B");
em.persist(memberA);
em.persist(memberB);
em.flush();
em.clear();
final String jpql = "SELECT m FROM Member m JOIN FETCH m.team";
final List<Member> members = em.createQuery(jpql, Member.class)
.getResultList();
assertThat(members).hasSize(2);
transaction.commit();
} catch (final Exception e) {
System.out.println(e.getMessage());
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
}
'JPA' 카테고리의 다른 글
JPA 기본기 다지기 Spring Data JPA와 QueryDSL (0) | 2024.03.27 |
---|---|
JPA기본기 다지기_JPA 내부구조 (0) | 2024.03.25 |
JPA기본기 다지기_양방향 매핑(복습...@_@) (0) | 2024.03.24 |
JPA기본기 다지기_연관관계 매핑(복습..복습..복습....) (0) | 2024.03.23 |
JPA기본기 다지기_필드와 컬럼 매핑 (1) | 2024.03.22 |