Spring JPA 3
๐ฟ Fetch Join & EAGER & LAZY
์ฆ์ ๋ก๋ฉ(EARGR๋ก ์ค์ )
- ๋ฉค๋ฒ ์ ์ฒด๋ฅผ ์กฐํํ๊ธฐ ์ํด JPQL ์คํ select m from member m
- JPQL์ EAGER์ ๋ฌด๊ดํ๊ฒ SQL๋ก ๊ทธ๋๋ก ๋ฒ์ญ -> select m.* from member
- JPQL ๊ฒฐ๊ณผ๊ฐ member๋ง ์กฐํํ๊ณ , team์ ์กฐํํ์ง ์์
- member์ team์ด ์ฆ์ ๋ก๋ฉ์ผ๋ก ์ค์ ๋์ด ์๊ธฐ ๋๋ฌธ์ ์ฐ๊ด๋ ํ์ ๊ฐ๊ฐ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ ค์ ์ถ๊ฐ ์กฐํ (N+1)
์ง์ฐ ๋ก๋ฉ(LAZY๋ก ์ค์ )
- ๋ฉค๋ฒ ์ ์ฒด๋ฅผ ์กฐํํ๊ธฐ ์ํด JPQL ์คํ select m from member m
- JPQL์ EAGER์ ๋ฌด๊ดํ๊ฒ SQL๋ก ๊ทธ๋๋ก ๋ฒ์ญ -> select m.* from member
- JPQL ๊ฒฐ๊ณผ๊ฐ member๋ง ์กฐํํ๊ณ , team์ ์กฐํํ์ง ์์
- member์ team์ด ์ง์ฐ ๋ก๋ฉ์ผ๋ก ์ค์ ๋์ด ์๊ธฐ ๋๋ฌธ์ ๊ฐ์ง ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋ฃ์ด๋๊ณ , ์ค์ ํ์์ ํ์ ์กฐํํ์ง ์์
- ์ค์ team์ ์ฌ์ฉํ๋ ์์ ์ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ ค์ ๊ฐ๊ฐ ์กฐํ(N+1)
fetch join ๋๋ ์ํฐํฐ ๊ทธ๋ํ(EAGER, LAZY ์๊ด ์์)
- ๋ฉค๋ฒ์ ํ์ ํ๋ฒ์ ์กฐํํ๊ธฐ ์ํด JPQL+fetch join ์คํ select m from member m join fetch m.team
- JPQL์์ fetch join์ ์ฌ์ฉํ์ผ๋ฏ๋ก SQL์ ๋ฉค๋ฒ์ ํ์ ํ ์ฟผ๋ฆฌ๋ก ์กฐํ -> select m., t. from member join team โฆ
- JPQL ๊ฒฐ๊ณผ๊ฐ member์ team์ ํ๊บผ๋ฒ์ ์กฐํํจ
- member์ team์ด fetch join์ผ๋ก ํ๋ฒ์ ์กฐํ๋์์ผ๋ฏ๋ก N+1 ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์์
๐ฟ Fetch Join ๊ณผ ๋ณ์นญ
JPA ํ์ค ์คํ์๋ fetch join ๋์์ ๋ณ์นญ์ ์ฌ์ฉํ ์ ์๋ค.
- ํ์ง๋ง Hibernate๋ ํ์ฉ
- ์ฃผ์์ฌํญ์ด ์์
์ฃผ์ ์ฌํญ
- fetch join JPQL์ on ์กฐ๊ฑด์ ๋ฌ๋ฉด ๋ฌด์กฐ๊ฑด ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค.(with-clause not allowed on fetch association)
- Select t from Team t join fetch t.member m on m.name=:memberName
- member ์ปฌ๋ ์ ์ด ์ ๋ถ ์กฐํ๋์ง ์๊ธฐ ๋๋ฌธ์ ์คํ ๋ถ๊ฐ
- ๊ทธ๋ฌ๋ฉด Select t from Team t join fetch t.member m on t.name=:teamName
- ์์ ๊ฒ์ Member ์ปฌ๋ ์ ์ ์ํฅ์ ์ฃผ์ง ์๋๋ฐ ์ ์๋ ๊น?
- ์กฐ์ธ ๊ณผ๋ ๋ฌด๊ดํ๊ฒ Team ์์ฒด์ ๋ฐ์ดํฐ๋ฅผ ํํฐ๋ง ํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ on ์ ์ฌ์ฉํ๋ ๊ฒ์ ์๋์ ๋ง์ง ์๋ค. where์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ง๋ค.
- select t from Team t join fetch t.members m where m.username = โm1โ
- where์ ์ฌ์ฉํ๋๋ผ๋ ํจ์น ์กฐ์ธ ๋์์๋ where์ ์ฌ์ฉํ๋ฉด ์๋๋ค.
- ์คํ ๊ฒฐ๊ณผ team1 = teamA member = m1(member๊ฐ ํ๋๋ง ์กด์ฌ)
๋ฐ๋ผ์ fetch join์ ๊ฒฐ๊ณผ๋ ์ฐ๊ด๋ ๋ชจ๋ Entity๊ฐ ์์๊ฑฐ๋ผ๊ณ ๊ฐ์ ํ๊ณ ์ฌ์ฉํด์ผ ํจ ์ด๋ ๊ฒ fetch join์ ๋ณ์นญ์ ์๋ชป ์ฌ์ฉํด์ ๊ฒฐ๊ณผ๋ฅผ ํํฐ๋ง ํ๋ฉด ๊ฐ์ฒด์ ์ํ์ DB์ ์ํ ์ผ๊ด์ฑ์ด ๊นจ์ง
๊ฒฐ๋ก : fetch join์ ๋์์ on, where ๋ฑ์์ ํํฐ๋ง ์กฐ๊ฑด์ผ๋ก ์ฌ์ฉํ๋ฉด ์๋๋ค.
๐ฟ Hibernate ํ์ฉ
์ผ๊ด์ฑ์ด ๊นจ์ง์ง ์๋ ๋ฒ์ ๋ด์์ ํ์ฉ
- ex) Select m from Member m join fetch m.team t where t.name=:teamName
- ์กฐํ๋ ํ์์ db์ ๋์ผํ ์ผ๊ด์ฑ์ ์ ์งํ ํ์ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
- ํ์ง๋ง ์ด ์ฟผ๋ฆฌ๋ฅผ left join fetch๋ก ๋ณ๊ฒฝํ๋ฉด ์ผ๊ด์ฑ์ด ๊นจ์ง ์ ์์ต๋๋ค.
๐ฟ Fetch Join์ ํ๊ณ
- Fetch Join ๋์์๋ ๋ณ์นญ์ ์ค ์ ์๋ค.
- Hibernate ๋ ๊ฐ๋ฅ, ๊ฐ๊ธ์ ์ฌ์ฉX(fetch join์ ์ฌ๋ฌ๋ฒ ํด์ผ ํ ๋ ๊ฐ๋ ์ฌ์ฉ)
- ๋ ์ด์์ ์ปฌ๋ ์ ์ ํ์น ์กฐ์ธ ํ ์ ์๋ค.
- ์ผ๋ ์
์ Fetch Joinํ๋ฉด ํ์ด์ง API(setFirstResult, setMaxResults)๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
- 1:1, N:1 ๊ฐ์ ๋จ์ผ ๊ฐ ์ฐ๊ด ํ๋๋ค์ ํ์น ์กฐ์ธํด๋ Paging ๊ฐ๋ฅ
- Hibernate๋ ๊ฒฝ๊ณ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๊ณ ๋ฉ๋ชจ๋ฆฌ์์ ํ์ด์ง(์ํ)
- ์๋ฅผ ๋ค์ด ํ์ด์ง๊ฐ 1์ด๋ฉด ๋ค๋์ผ์์ ์ปฌ๋ ์ ์ ์กฐํํ๋ฉด ํ๋์ ํ๋๋ง ๋งค์น๋์ด DB์ ์ผ์น๋์ง ์๋ ๋ฌธ์ ๊ฐ ๋ฐ์
- ํ์ด์ง ๋์ ์ปฌ๋ ์ ์ @BatchSize๋ฅผ ์ค์ ํด์ limit์ ์ค์ ํด ์ค๋ค. ํน์ persistence.xml์์ ๊ธ๋ก๋ฒ ํ๊ฒ ์ค์ (ํ๋ฒ์ N+1 ๋ฌธ์ ์์ limit์ ์ ํด์ค ์ ์๋ค.)
- Dto๋ก ๋ฝ์๋ ๋๋ค.
- ๋ค๋์ผ์์ ์ปฌ๋ ์ ์ ๋ค์ด์ค๋ ๊ฒ์ ํ๋์ ๊ฐ์ฒด์ ์ฌ๋ฌ๊ฐ์ Entity๊ฐ ๋ค์ด์ค๋ ๊ฒ์ด ์๋๋ผ ๊ฐ Entity์ ์ฐ๊ด๋ Entity๊ฐ ์ ๋ถ ํ๋์ฉ ๋์๋์ด ๋ค์ด์จ๋ค.
__
๐ฟ Fetch Join์ ์ต์ ํ
Entity ์ง์ ๋ ธ์ถ
- ์ ๋ Entity๋ฅผ ์ง์ ๋ ธ์ถํด์๋ ์๋๋ค.
1
2
3
4
5
@GetMapping("/api/v1/simple-orders")
public List<Order> orderV1() {
List<Order> all = orderRepository.findAllByString(new OrderSearch());
return all;
}
์ฃผ์
- hibernatemodule์ Bean์ ๋ฑ๋กํด์ผํจ(์ง์ฐ ๋ก๋ฉ ๋ฌธ์ ๋ก ๋ฌดํ query์ ๊ฐํ)
- ์ด๋ฌ๋ฉด LAZY ๋ฌด์ํจ
- ํน์ ์๋ฐฉํฅ์ด ๊ฑธ๋ฆฐ ๊ณณ์ @JsonIgnore์ ์ค์ ํด ์ฃผ์ด์ผ ํจ
- ์ฆ์๋ก๋ฉ(EAGER)์ฐ๋ฉด ๋๋๋ฐ ์ฐ์ง ๋ง์
- ๊ทธ๋ฅ ์ฐ์ง ๋ง์
Dto ๋ณํ
1
2
3
4
5
6
7
@GetMapping("/api/v2/simple-orders")
public List<SimpleOrderDto> orderV2() {
List<Order> orders = orderRepository.findAllByString(new OrderSearch());
return orders.stream()
.map(SimpleOrderDto::new)
.collect(Collectors.toList());
}
์ฃผ์
- ์์ง n+1 ๋ฌธ์ ๊ฐ ๋จ์์์(์ฒ์์ ํ๋ ๋ณด๋ด๋ฉด members ์ป์ผ๋ ค๊ณ 2๋ฒ ๊ทธ๋ฆฌ๊ณ delivery ์ป์ผ๋ ค๊ณ 2๋ฒ ํด์ ์ด 5๋ฒ ๋๊ฐ)
- DTO ๋ง๋ค๋ ํญ์ ์์ ์๋ ์ฐ๊ด ๊ด๊ณ๊ฐ ์๋ Entity๋ค๋ ๊ทธ๋๋ก ๋ ธ์ถํ์ง ๋ง๊ณ DTO๋ก ๋ง๋ค์ด์ ๋ ธ์ถ์์ผ๋ผ
fetch join
- ๊ฐ์ ธ์ฌ๋ ์ ๋ถ DB์์ ๋ถํ์ ๊ฐ์ ธ์ด
1
2
3
4
5
6
public List<Order> findAllWithMemberDelivery() {
return em.createQuery("select o from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d", Order.class
).getResultList();
}
์ฃผ์
- ์ผ๋๋ค์ผ ๊ฒฝ์ฐ ๋ฐ์ดํฐ๊ฐ ๋ค ๊ธฐ์ค์ผ๋ก ๋์ด๋จ(DB๋ ๊ทธ๋ฅ left join ์์ผ๋ฒ๋ฆฌ๊ธฐ ๋๋ฌธ์ record๊ฐ ๋์ด๋ ์ ๋ฐ์ ์์)
- distinct๋ฅผ ์ฐ๋ฉด ๋๋ค(JPA๊ฐ reference ๊ฐ์ด ๊ฐ์ ๊ฐ์ฒด๋ List์ ์ถ๊ฐํ์ง ์์)
- SQL์ distinct๋ฅผ ์ถ๊ฐํ๊ณ ๋ํด์ ๊ฐ์ ์ํฐํฐ(ref๊ฐ์ด ๊ฐ์ JPA๋ id๊ฐ ๊ฐ์ผ๋ฉด ref๊ฐ ๊ฐ์)๊ฐ ์กฐํ๋๋ฉด ์ค๋ณต์ ๊ฑธ๋ฌ์ค
- ๊ทผ๋ฐ ์ด๊ฑฐ ์ฐ๋ฉด paging ๋ถ๊ฐ
ํ์ด์ง ๋ถ๊ฐ
- ์ผ๋ ์ ์ ํ์น ์กฐ์ธํ๋ฉด ์ผ๋๋ค ์กฐ์ธ์ด ๋ฐ์ํ๋ฏ๋ก ๋ฐ์ดํฐ๊ฐ ์ฆ๊ฐํ๋ค.
- ์ผ๋๋ค ๊ด๊ณ์์ 1์ ๊ธฐ์ค์ผ๋ก ํ์ด์ง์ ํ๋ ๊ฒ์ด ๋ชฉ์ ์ด๋ค. ๊ทธ๋ฐ๋ฐ ๋ฐ์ดํฐ๋ ๋ค๋ฅผ ๊ธฐ์ค์ผ๋ก row๊ฐ ์์ฑ๋๋ค.
- Order๋ฅผ ๊ธฐ์ค์ผ๋ก ํ์ด์ง์ ํ๊ณ ์ถ์๋ฐ, ๋ค(N)์ธ OrderItem์ ์กฐ์ธํ๋ฉด OrderItem์ด ๊ธฐ์ค์ด ๋์ด๋ฒ๋ฆฐ๋ค.
- setFirstResult, setMaxResult๋ฅผ ์ฐ๋ฉด inmemory์์ ํ์ด์ง์ ์ํ
- warning์ ๋จ๊น ์ฐ์ง ๋ง๊ฒ
DTO๋ก ๊ฐ์ ธ์ค๊ธฐ
1
2
3
4
5
6
public List<SimpleOrderDto> findOrderDtos() {
return em.createQuery("select new com.example.JPALecture2.domain.dto.SimpleOrderDto(o.id, m.name, o.orderDate, o.status, d.address)" +
" from Order o " +
" join o.member m" +
" join o.delivery d", SimpleOrderDto.class
).getResultList();
์ฃผ์
- fetch join๊ณผ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฑ๋ฅ์ ๊ฐ์ผ๋ DTO์ ๋ฑ๋ง๊ฒ Query๊ฐ ์์ฑ๋์ด ์ฌ์ฌ์ฉ ๋ถ๊ฐ
ํ์ด์ง ํ๊ณ ํด์
- ๋จผ์ ToOne(OneToOne, ManyToOne) ๊ด๊ณ๋ ๋ชจ๋ fetch join ํ๋ค.
- ToOne ๊ด๊ณ๋ row๋ฅผ ์ฆ๊ฐ์ํค์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
- ๋จ์ ์ปฌ๋ ์ ์ ์ง์ฐ๋ก๋ฉ์ผ๋ก ์กฐํํ๋ค.
- ์ง์ฐ ๋ก๋ฉ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํด batch_fetch_size ํน์ @BatchSize๋ฅผ ์ ์ฉํ๋ค.
- hibernate.default_batch_fetch_size: ๊ธ๋ก๋ฒ ์ค์
- @BatchSize: ๊ฐ๋ณ ์ต์ ํ
- ์ด ์ต์ ์ ์ฌ์ฉํ๋ฉด ๊ด๋ จ๋ ์ปฌ๋ ์ ์ด๋ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ํ๋ฒ์ ์ค์ ํ size ๋งํผ in ์ฟผ๋ฆฌ๋ก ๋ฏธ๋ฆฌ ๊ฐ์ ธ์ด