가장 단순한 조회 방법
엔티티 객체를 중심으로 개발한다.
문제는 검색 쿼리를 작성할 때 이다. 검색을 할 때도 엔티티 객체를 대상으로 검색하기 때문에 모든 DB 데이터를 객체로 변환하는 것은 불가능하다. 어플리케이션이 필요한 데이터만 DB에서 불러오려면 검색 조건이 포함된 SQL이 필요하다.
JPQL은 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공한다. JPQL은 텐티티 객체를 대상으로 쿼리한다면 SQL은 데이터베이스 테이블을 대상으로 쿼리한다.
객체 지향 SQL이라고 생각하자
JPQL과 실행된 SQL
List<Member> resultList = em.createQuery("select m from Member m where m.username like '%mb%'",Member.class)
.getResultList();
JPA 공식 기능으로 자바코드로 JPQL을 작성할 수 있는 장점이 있다.
하지만 너무 복잡하고 실용성이 없다. 대신 QueryDSL을 사용하자
동적쿼리 작성을 쉽게 만들어주는 라이브러리다. 실무 사용 권장.
따로 정리 예정
JPA가 제공하는 SQL을 직접 사용하는 기능이다.
JPQL로 해결할 수 없는 특정 데이터베이스에 의존적인 기능을 활용할 때 쓴다
예) 오라클 CONNECT BY, 특정 DB에만 사용하는 기능
사용 예시
String sql =
“SELECT ID, AGE, TEAM_ID, NAME FROM MEMBER WHERE NAME = ‘kim’";
List<Member> resultList =
em.createNativeQuery(sql, Member.class).getResultList();
JPA를 사용하면서 JDBC 커넥션을 직접 사용하거나 스프링 JdbcTemplate, 마이바티스등을 함께 사용 가능하다
다만 영속성 컨텍스트를 적절한 시점에 강제로 플러시 해야한다. JPA가 관리해주지 않기 때문이다.
엔티티와 속성은 대소문자를 구분한다
JPQL 키워드는 대소문자를 구분하지 않는다
테이블 이름 말고 엔티티 이름을 사용한다
별칭은 필수로 적는다
SQL과 문법이 비슷하다
TypeQuery, Query
TypeQuery : 반환 타입이 명확할 때 사용한다
TypedQuery<Member> query =
em.createQuery("SELECT m FROM Member m", Member.class);
Query : 반환 타입이 명확하지 않을 때 사용한다
Query query =
em.createQuery("SELECT m.username, m.age from Member m");
query.getResultList() : 결과가 하나 이상일 때 리스트를 반환한다. 결과가 없으면 빈 리스트를 반환한다
query.getSingleResult() : 결과가 정확히 하나. 단일 객체를 반환한다. 결과가 없거나 많으면 에러를 낸다
이름을 기준으로 바인딩하자
Query query = em.createQuery("select m from Member m where m.username = :username");
query.setParameter("username","member1");
SELECT 절에 조회할 대상을 지정하는 것이다.
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 -> 스칼라 타입 프로젝션
// DISTINCT로 중복 제거 가능
Query 타입
Object[] 타입
new 명령어로 조회 - 단순 값을 DTO로 바로 조회한다.
// 패키지 명을 포함한 전체 클래스 명을 입력하자
// 순서와 타입이 일치하는 생성자가 필요하다
SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m
JPA는 페이징을 두 API로 추상화한다
setFirstResult(int startPosition) : 조회 시작 위치
setMaxResults(int maxResult) : 조회할 데이터의 수
예시
String jpql = "select m from Member m order by m.name desc"
List<Member> resultList = em.createQuery(jpql, Member.class)
.setFirstResult(10)
.setMaxResults(20)
.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.username = t.name
ON절 활용 : 조인 대상 필터링 또는 연관관계 없는 엔티티 외부를 조인
조인 대상 필터링
SELECT m,t FROM Member m LEFT JOIN m.team t on t.name = 'A'
연관관계 없는 엔티티 외부 조인
SELECT m,t FROM Member m LEFT JOIN Team t on m.username = t.name
JPQL에서도 서브 쿼리 사용 가능
예)
// 팀A 소속인 회원
select m from Member m
where exists (select t from m.team t where t.name = ‘팀A')
// 전체 상품 각각의 재고보다 주문량이 많은 주문들
select o from Order o
where o.orderAmount > ALL (select p.stockAmount from Product p)
// 어떤 팀이든 팀에 소속된 회원
select m from Member m
where m.team = ANY (select t from Team t)
문자 : 'hello'
숫자 : 10L, 10D, 10F
Boolean : TRUE, FALSE
ENUM : jpabook.MemberType.Admin (패키지명 포함)
엔티티 타입 : TYPE(m) = Member (상속 관계에서 사용한다)
하이버네이트는 사용전 방언에 추가해야 한다
사용하는 DB 방언을 상속받고 사용자 정의 함수를 등록한다
select function('group_concat', i.name) from Item i'
JPQL의 문법은 SQL과 상당히 유사하다. 그러므로 실제 사용시 구글링하자
.(점)을 찍어 객체 그래프를 탐색하는 것이다.
select m.username -> 상태 필드
from Member m
join m.team t -> 단일 값 연관 필드
join m.orders o -> 컬렉션 값 연관 필드
where t.name = '팀A'
상태 필드 : 단순히 값을 저장하기 위한 필드. 경로 탐색의 끝이다
연관 필드 : 연관관계를 위한 필드
- 단일 값 연관 필드 : @ManyToOne, @OneToOne, 대상이 엔티티, 묵시적 내부 조인이 발생한다. 탐색 가능
- 컬렉션 값 연관 필드 : @OneToMany, @ManyToMany, 대상이 컬렉션, 묵시적 내부 조인이 발생한다. 탐색 불가(FROM 절에서 명시적 조인을 통해 별칭을 얻으면 별칭을 통해 탐색 가능)
JPQL : select o.member from Order o
-> SQL : select m.*
from Orders o
inner join Member m on o.member_id = m.id
명시적 조인 : join 키워드 직접 사용
묵시적 조인 : 경로 표현식에 의해 묵시적으로 SQL 조인 발생
경로 표현식을 통해 묵시적 조인을 사용하면 예상치 못한 조인이 발생할 수 있다.
가급적 명시적 조인을 사용하자
출처 : 인프런 자바 ORM 표준 JPA 프로그래밍 - 기본편