select m.username // 상태필드
from Member m
join m.team t // 단일 값 연관 필드
join m.orders o // 컬렉션 값 연관 필드
where t.name = '팀A'
@ManyToOne
, @OneToOne
, 대상이 엔티티(ex: m.team)@OneToMany
, @ManyToMany
, 대상이 컬렉션(ex: morders)상태 필드(state field) : 경로 탐색의 끝, 탐색X
"select m.username.userfamilyName() from Member m"
은 불가능JPQL
의 코드가 그대로 SQL
로 넘어간다.단일 값 연관 경로: 묵시적 내부 조인(inner join) 발생, 탐색O
String query = select m.team from Member m
를 통해 IDE를 실행한다면 JPA는 어떻게 처리할까?SELECT t.id, t.name FROM Member INNER JOIN Team t ON member.TEAM_ID=t.id
MEMBER
와 TEAM
을 조인한다. 이것을 묵시적 내부 조인이라고 한다.SELECT o.member FROM Order o // JPQL
SELECT m.* FROM Orders. o INNER JOIN Member m on o.member_id = m.id // SQL
컬렉션 값 연관 경로: 묵시적 내부 조인 발생, 탐색X
select m from Member m join m.team t
select m.team from Member m
SELECT
, WHERE
절에서 사용하지만 묵시적 조인으로 인해 SQL의 FROM (JOIN) 절에 영향을 줌실무에서 매우 중요하다.
나랑 연관된 것들을 모조리 끌고 오는 것.
SQL 조인 종류X
JPQL에서 성능 최적화를 위해 제공하는 기능
연관된 엔티티나 컬렉션을 SQL 한 번에 함께 조회하는 기능
join fetch 명령어 사용
페치 조인 ::=[LEFT [OUTER]|INNER] JOIN FETCH 조인 경로
select m from Member m join fetch m.team
SELECT M.*, T.* FROM MEMBER M INNER JOIN TEAM T ON M.TEAM_ID=T.ID
일대다 관계, 컬렉션 페치 조인
[JPQL]
select t
from Team t join fetch t.members
where t.name = ‘팀A'
SELECT T.*, M.*
FROM TEAM T
INNER JOIN MEMBER M ON T.ID=M.TEAM_ID
WHERE T.NAME = '팀A'
DISTINCT
SQL의 DISTINCT
는 중복된 결과를 제거하는 명령
JPQL의 DISTINCT
두 가지 기능 제공
DISTINCT
를 추가select distinct t from Team t join fetch t.members where t.name = '팀A'
SQL에 DISTINCT를 추가하지만 데이터가 다르므로 SQL 결과에서 중복 제거 실패
일반 조인 실행시 연관된 엔티티를 함께 조회하지 않음
SELECT t FROM Team t JOIN t.members m WHERE t.name = '팀A'
SELECT T.* FROM TEAM T INNER JOIN MEMBER M ON T.ID = M.TEAM_ID WHERE T.NAME = '팀A'
JPQL은 결과를 반환할 때 연관관계 고려X
단지 SELECT 절에 지정한 엔티티만 조회할 뿐
여기서는 팀 엔티티만 조회하고, 회원 엔티티는 조회X
페치 조인을 사용할 때만 연관된 엔티티도 함께 조회(즉시 로딩)
페치 조인은 객체 그래프를 SQL 한번에 조회하는 개념
@OneToMany(fetch = FetchType.LAZY)
//글로벌 로딩 전략Item
중에 Book
, Movie
를 조회해라select i from Item i
where type(i) IN (Book, Movie)
-[SQL]select i from i
where i.DTYPE in ('B', 'M')
FROM
, WHERE
, SELECT(hibernate 지원)
사용Item
과 자식 Book
이 있다.select i from Item i
where type(i as Book).author = 'kim'
-[SQL]select i.* from Item i
where i.DTYPE = 'B' and i.author = 'kim'
select count(m.id) from Member m //엔티티의 아이디를 사용
select count(m) from Member m //엔티티를 직접
-[SQL: JPQL 둘다 같은 다음 SQL 실행]select copunt(m.id) as cnt from Member
//엔티티를 파라미터로 전달
String jqpl = "select m from Member m where m = :member";
List resultList = em.createQuery(jpql)
.setParameter("member", member)
.getResultList();
//식별자를 직접 전달
String jqpl = "select m from Member m where m.id = :memberId";
List resultList = em.createQuery(jpql)
.setParameter("memberId", memberId)
.getResultList();
실행된 SQL : select m.* from Member m where m.id=?
Team team = em.find(Team.class, 1L);
String qlString = "select m from Member m where m.team = :team";
List resultList = em.createQuery(qlString)
.setParameter("team", team)
.getResultList();
String qlString = "select m from Member m where m.team.id = :teamId";
List resultList = em.createQuery(qlString)
.setParameter("teamId", teamId)
.getResultList();
실행된 SQL : select m.* from Member m where m.team_id=?
Named 쿼리는 쿼리에 이름을 부여
@Entity
@NamedQuery(
name = "Member.findByUsername",
query = "select m from Member m where m.username = :username")
pulbic class Member {
...
}
List<Member> resultList =
em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", "회원1")
.getResultList();
벌크연산은 단건 조회나 삭제를 제외한 일반적으로 우리가 알고 있는 UPDATE
문과 DELETE
문이다.
UPDATE SQL
실행executeUpdate()
의 결과는 영향받은 엔티티 수 반환UPDATE
, DELETE
지원INSERT(insert into .. select, 하이버네이트 지원)
String qlString = "update Product p " +
"set p.price = p.price * 1.1 " +
"where p.stockAmount < :stockAmount";
int resultCount = em.createQuery(qlString)
.setParameter("stockAmount", 10)
.executeUpdate();