경로 표현식#
.으로 객체 그래프를 탐색
SELECT m.username -> 상태필드
FROM Member m
join m.team t -> 단일 값 연관 필드
join m.orders o -> 컬렉션 값 연관 필드
where t.name = 'TEAM_A'
- 상태 필드 : 단순히 값을 저장하기 위한 필드 -> 경로 탐색의 끝이므로 탐색 ❌
- 연관 필드 : 연관관계를 위한 필드 -> 묵시적 내부 조인 발생
- 단일값 :
@ManyToOne,@OneToOne, 엔티티(m.team) -> 탐색 ⭕️ - 컬렉션 :
@OneToMany, 대상이 컬렉션 (m.orders) -> 탐색 ❌
실무에선 묵시적 내부조인이 최대한 발생하지 않는 쪽으로 하자 => 무조건 명시적 조인 사용
fetch join ❗️❗️❗️#
JPQL에서 성능 최적화를 위해 제공하는 기능
join fetch
- 연관된 엔티티나 컬렉션을 SQL 한 번에 같이 조회하는 기능 (즉시 로딩)
FetchType.LAZY보다 우선됨
Distinct#
DB에서는 join에 여러 결과가 있으면, 다중 row를 반환한다.
String query = "select t From Team t join fetch t.members ";
List<Team> resultList = em.createQuery(query, Team.class)
.getResultList();따라서, resultList에는 중복된 team이 들어가게 된다.
Distinct를 사용해서, 엔티티의 중복을 제거할 수 있다.
String query = "select distinct t From Team t join fetch t.members ";
한계#
- 폐치 조인 대상에는 별칭 사용 ❌(권장)
- 둘 이상의 컬렉션엔 사용❌
- 컬렉션 패치 조인시, 페이징API 사용 ❌
지금은 쓸 일이 없겠지만, 나중에 이 이걸 읽을때, hibernate.default_batch_fetch_size에 대해 알아보자
엔티티 직접 사용시#
String query = "select m From Member m where m.team = :team ";이런식으로 엔티티를 직접 사용시엔 엔티티 ID값을 사용함
String query = "select m From Member m where m.team.id = :teamid ";여기에 teamid를 직접넘기는것도 같은 SQL 생성
Named쿼리#
@NamedQuery를 사용해서 미리 이름을 부여해두고 사용하는 JPQL ( ** 정적쿼리만 가능 **)
- 애플리케이션 로딩 시점에 초기화(검증) 후 재사용)
@Entity
@NamedQuery(
name = "Member.findByUsername",
query = "select m from Member m where m.username = :username"
)
public class Member {
///////////////////////
//main
List<Member> resultList = em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", "user1")
.getResultList();스프링 데이터 JPA서는 repository에 코딩 가능
벌크 연산#
한방에 여러개 업데이트
int resultCount = em.createQuery("update Member m set m.age = 20 where 1=1 ")
.executeUpdate(); // 업데이트 한 수 반환영속성 컨텍스트를 무시하고 DB에 쿼리가 들어가기 때문에, 벌크연산을 먼저 실행하고 -> 영속성 컨텍스트 초기화
em.claer()