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: m.orders)
“select m.team from Member m”
실행 시 sql에선 조인이 실행됨 (묵시적 내부 조인) 묵시적 내부 조인이 발생하게 짜면 안된다. (운영이 힘들어짐)
“select t.members from Team t”
t.members에선 .(점)으로 또 탐색할 수는 없다.
FROM 절에서 명시적 조인을 통해 별칭을 얻으면 별칭으로 탐색 가능
“select m from Team t join t.members m”
select m from Member m join m.team t
select m.team from Member m
페치 조인 ::= [ LEFT [OUTER] | INNER ] JOIN FETCH 조인경로 |
select m from Member m **join fetch** m.team
[SQL] select m만 해줬는데 실행 sql을 보면 Team도 같이 가져온다. (즉시 로딩)
SELECT M.*, T.* FROM MEMBER M INNER JOIN TEAM T ON M.TEAM_ID=T.ID
String query = "select m from Member m join fetch m.team";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
for (Member member : result) {
System.out.println("userName = " + member.getUserName());
System.out.println("teamName = " + member.getTeam().getName());
}
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'
String query = "select t from Team t join fetch t.members where t.name = '팀A'";
List<Team> teams = em.createQuery(query, 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);
}
}
결과
teamname = 팀A, team = Team@0x100
-> username = 회원1, member = Member@0x200
-> username = 회원2, member = Member@0x300
teamname = 팀A, team = Team@0x100
-> username = 회원1, member = Member@0x200
-> username = 회원2, member = Member@0x300
이렇게 일대다 관계에서 조인을 날리면 데이터가 뻥튀기 된다. (팀A가 두 번 나옴) 이유는 회원 두 명이 팀A에 매핑된 것을 표현하기 위해 테이블 상에선 위 그림처럼 표현하기 때문이다. 저 테이블에서 팀 컬렉션을 가져오면 팀A는 두 개가 된다. (팀의 회원 수가 엄청 많다면 엄청난 문제..)
select **distinct** t from Team t join fetch t.members where t.name = ‘팀A’
[DISTINCT 추가시 결과]
teamname = 팀A, team = Team@0x100
-> username = 회원1, member = Member@0x200
-> username = 회원2, member = Member@0x300
페치 조인의 특징과 한계
select t from Team t join fetch t.members **m** (X)
where m…
으로 3명 정도만 가져온다고 쳤을 때 누락된 2명이 생김. = 팀 엔티티의 members에 원래 5명인데 3명만 있는 것페치 조인의 컬렉션은 하나만 지정할 수 있다.
(아니면 일대다 조인 시 발생하는 데이터 뻥튀기가 심해질 수도)
@OneToMany(fetch = FetchType.LAZY)
//글로벌 로딩 전략String query = "select t from Team t";
List<Team> teams = em.createQuery(query, Team.class)
.setFirstResult(0)
.setMaxResults(2)
.getResultList();
@BatchSize
어노테이션을 쓴다.@BatchSize(size = 100)
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
BatchSize
없이는 총 10번의 members 조회 쿼리가 나가야 한다고 가정하자.@BatchSize(size = 2)
로 둔다면 한 번에 2팀씩 가져와서 총 5번 쿼리가 나갈 것이다.@BatchSize(size = 6)
으로 둔다면 팀6까지 한 번에 가져오고 나머지 팀 4개를 다음 지연 로딩으로 가져와 총 2번 쿼리가 나간다.페치 조인 - 정리
select i from Item i where type(i) IN (Book, Movie)
select i from i where i.DTYPE in (‘B’, ‘M’)
예) 부모인 Item과 자식 Book이 있다.
select i from Item i where treat(i as Book).auther = ‘kim’ // 다운 캐스팅과 유사
select i.* from Item i where i.DTYPE = ‘B’ and i.auther = ‘kim’
엔티티를 파라미터로 전달
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();
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();
@Entity
@NamedQuery(
name = "Member.findByUserName",
query = "select m from Member m where m.userName = :username"
)
public class Member {
List<Member> result = em.createNamedQuery("Member.findByUserName", Member.class)
.setParameter("username", "회원1")
.getResultList();
executeUpdate()
의 결과는 영향받은 엔티티 수 반환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();
벌크 연산 주의