select m.username -> 상태 필드
from Member m
join m.team t -> 단일 값 연관 필드
join m.orders o -> 컬렉션 값 연관 필드
where t.name = '팀A'
// JPQL
select m.username, m.age from Member m
// SQL
select m.username, m.age from Member m
//JPQL
select o.member from Order o
// SQL
select m.*
from Orders o
inner join Member m on o.member_id = m.id
select m from Member m join m.team t
select m.team from Member m
예)
select o.member.teamfrom Order o -> 성공
select t.members from Team -> 성공
select t.members.username from Team t -> 실패
select m.username from Team t join t.members m -> 성공
실무에서는?
- 가급적 묵시적 조인 대신에 명시적 조인 사용
- 조인은 SQL 튜닝에 중요 포인트
- 묵시적 조인은 조인이 일어나는 상황을 한눈에 파악하기 어려움
// 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
// 페치 조인 사용 코드
String jpql = "select m from Member m join fetch m.team";
List<Member> members = em.createQuery(jpql, Member.class)
.getResultList();
for (Member member : members) {
//페치 조인으로 회원과 팀을 함께 조회해서 지연 로딩X
System.out.println("username = " + member.getUsername() + ", " +
"teamName = " + member.getTeam().name());
}
// JPQL
select t
from Team t join fetch t.members
where t.name = ‘팀A'
// SQL
SELECT T.*, M.*
FROM TEAM T
INNER JOIN MEMBER M ON T.ID=M.TEAM_ID
WHERE T.NAME = '팀A'
// 컬렉션 페치 조인 사용 코드
String jpql = "select t from Team t join fetch t.members where t.name = '팀A'"
List<Team> teams = em.createQuery(jpql, Team.class).getResultList();
for(Team team : teams) {
System.out.println("teamname = " + team.getName() + ", team = " + team);
for (Member member : team.getMembers()) {
//페치 조인으로 팀과 회원을 함께 조회해서 지연 로딩 발생 안함
System.out.println(“-> username = " + member.getUsername()+ ", member = " + member);
}
}
-> 하이버네이트6 부터는 DISTINCT 명령어를 사용하지 않아도 애플리케이션에서 중복 제거가 자동으로 적용됩니다
일반 조인
- JPQL은 결과를 반환할 때 연관관계 고려X
- 단지 SELECT 절에 지정한 엔티티만 조회할 뿐
- 여기서는 팀 엔티티만 조회하고, 회원 엔티티는 조회X
페치조인
- 페치 조인을 사용할 때만 연관된 엔티티도 함께 조회(즉시 로딩)
- 페치 조인은 객체 그래프를 SQL 한번에 조회하는 개념

// 예) Item 중에 Book, Movie를 조회해라
// JPQL
select i from Item i where type(i) IN (Book, Movie)
// SQL
select i from i where i.DTYPE in (‘B’, ‘M’)
// 예) 부모인 Item과 자식 Book이 있다
// JPQL
select i from Item i where treat(i as Book).author = ‘kim’
// SQL
select i.* from Item i where i.DTYPE = ‘B’ and i.author = ‘kim’
// JPQL
select count(m.id) from Member m //엔티티의 아이디를 사용
select count(m) from Member m //엔티티를 직접 사용
// SQL (JPQL 둘다 같은 다음 SQL 실행)
select count(m.id) as cnt from Member m
// 엔티티를 파라미터로 전달
String jpql = “select m from Member m where m = :member”;
List resultList = em.createQuery(jpql)
.setParameter("member", member)
.getResultList();
// 식별자를 직접 전달
String jpql = “select m from Member m where m.id = :memberId”;
List resultList = em.createQuery(jpql)
.setParameter("memberId", memberId)
.getResultList();
@Entity
@NamedQuery(
name = "Member.findByUsername",
query="select m from Member m where m.username = :username")
public class Member {
...
}
List<Member> resultList =
em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", "회원1")
.getResultList();
-> spring data jpa의 @query를 쓰자
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();
벌크 연산 주의
• 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접
쿼리
• 벌크 연산을 먼저 실행
• 벌크 연산 수행 후 영속성 컨텍스트 초기화
[인프런] 자바 ORM 표준 JPA 프로그래밍 - 김영한