โ๐ป ๊ธ ์์ฑ์ ๊ณ๊ธฐ
์ด๋๋ง ๋ ๋ฒจ3์ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉฐ ํ์๋ค์๊ฒ fetchType ์ LAZY ๋ก ํ๋ ๊ฒ ์ข๋ค๋ ๋ง์ ํ๋ค. ๊ทธ๋ฌ์ ํ์๋ค์ด ์๋๊ณ ๋ฌผ์๊ณ , ํ๋ง๋๋ก ์ ๋ป ์ ๋ฆฌํ์ง ๋ชปํ๋ ๋๋ฅผ ๋ฐ๊ฒฌํ๋ค. ๊ทธ์ 'EAGER ๋ join ์ด ์๋์ผ..' ๋ผ๋ ์ ๋งคํ ๋ง๋ง ๋ํ์ดํ๋ค.
์ดํ ๋ด์ฉ์ ์ ๋ฆฌํด์ ํ์๋ค์๊ฒ ์ด๋ฅผ ๋ฐํ์ผ๋ก ๊ธ ์์ฑ๊น์ง ํ๊ฒ ๋๋ค.
๐ ์ด ๊ธ์ ์ด๋ฐ ๋ถ๋ค๊ป ์ถ์ฒํฉ๋๋ค
- LAZY ๋ก ๋งค๋ฒ ์ค์ ํ๊ธด ํ๋๋ฐ, ์์ธ์ง๋ ์์งํ ์ ๋ชจ๋ฅด๊ฒ ๋ค ํ์๋ ๋ถ๋ค
- ๋งค๋ฒ EAGER ๋ join ์ด ๋๋ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๊ณ ์๋ ๋ถ๋ค
EAGER ๊ฐ ๊ณ ๋ ค๋๋ ์ํฉ
๋ธ๋ก๊ทธ ์๋น์ค๋ฅผ ๊ตฌํํ๊ณ ์๋ค๊ณ ๊ฐ์ ํด๋ณด์. Blog ๋ ๊ฒ์๊ธ, BlogMember ๋ ์์ฑ์๋ฅผ ์๋ฏธํ๋ฉฐ Post ๊ฐ N:1 ์ ๊ด๊ณ๋ก Member ๋ฅผ ๊ฐ๊ณ ์๋ค.
๐ค โBlog๋ BlogMember๋ ํญ์ ๊ฐ์ด ์ฐ์ด๋๋ฐ, ๊ทธ๋ฅ EAGER๋ก ๋ฌถ์ผ๋ฉด ํธํ์ง ์์?โ
์ํฐํฐ๋ฅผ ๊ตฌ์ฑํ๋ฉฐ ์์ ๊ฐ์ ๊ณ ๋ฏผ์ ํ๋ฒ์ฏค์ ํด๋ดค์ ๊ฒ์ด๋ค.
โ๋งค๋ฒ fetch join ํ๊ธฐ๋ ๊ท์ฐฎ๊ณ , ํญ์ ํจ๊ป ์กฐํ๋๋๊น!โ
๊ทธ๋ฌ๋ ์ฌ์ค 'EAGER' ๋ ์ฐ๋ฆฌ๊ฐ ์๊ฐํ๋ ๊ทธ โEAGERโ๋์ ์ข ๋ค๋ฅด๋ค..
findById ๋ฅผ ์ฌ์ฉํ๋ ์ํฉ์์์ EAGER
@Test
void test1() {
// given
BlogMember hero = new BlogMember("ํ๋ก");
BlogMember savedMember = blogMemberRepository.save(hero);
Blog blog = new Blog(savedMember);
Blog savedBlog = blogRepository.save(blog);
// when
Long id = savedBlog.getId();
em.clear();
System.out.println("======");
Blog blog1 = blogRepository.findById(id).get();
System.out.println(blog1.getMember().getName());
}
Hibernate:
select
b1_0.id,
m1_0.id,
m1_0.name
from
blog b1_0
left join
blog_member m1_0
on m1_0.id=b1_0.member_id
where
b1_0.id=?
findById ๋ฅผ ์ฌ์ฉํด์ ์กฐํ๋ฅผ ํ๊ฒ ๋๋ฉด, ์ฐ๋ฆฌ๊ฐ ์์ํ ๋ฐ์ ๊ฐ์ด join ์ด ๋ผ์ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
์ด ์ํฉ์์๋ ์๋ฌด๋ฐ ๋ถ์์ฉ์ด ๋ฐ์ํ์ง ์๋๋ค.
Query Method ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
@Test
void test2() {
// given
BlogMember hero = new BlogMember("ํ๋ก");
BlogMember savedMember = blogMemberRepository.save(hero);
Blog blog = new Blog(savedMember);
Blog savedBlog = blogRepository.save(blog);
// when
Long id = savedBlog.getId();
em.clear();
System.out.println("======");
Blog blog1 = blogRepository.findByMember(savedMember).get();
System.out.println(blog1.getMember().getName());
}
Hibernate:
select
b1_0.id,
b1_0.member_id
from
blog b1_0
where
b1_0.member_id=?
Hibernate:
select
bm1_0.id,
bm1_0.name
from
blog_member bm1_0
where
bm1_0.id=?
๋ง์ฝ findByMember ์ ๊ฐ์ด ์ง์ ์ ์ํ ์ฟผ๋ฆฌ ๋ฉ์๋๋ฅผ ํตํด ์กฐํํ๋ค๋ฉด ์ฟผ๋ฆฌ๊ฐ ์์ ๊ฐ์ด ๋๊ฐ๋ค.
์ฐ๋ฆฌ๊ฐ ์์ํ ๋ฐ๋ก๋, EAGER ์ด๊ธฐ์ Blog ๋ฅผ ์กฐํํด์ฌ ๋ join ์ ํด์ BlogMember ๋ฅผ ์กฐํํด์ฌ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋ค.
๊ทธ๋ฌ๋ ์ฌ์ค์ select ์ฟผ๋ฆฌ๊ฐ ๋๋ฒ ๋๊ฐ๋ค. ์ ์ด๋ ๊ฒ ๋ฌ๋ผ์ง๋ ๊ฒ์ผ๊น?
์ฐ๋ฆฌ๋ ์ด์ ๊น์ง fetchType ์ ์๋ชป ์ดํดํ๊ณ ์์๋ค
fetchType ์ ์ฌ์ค ์ฐ๊ด๋ ์ํฐํฐ๋ฅผ ์ธ์ ๋ก๋ฉํ ์ง ์ ๋ํ ํํธ์ผ ๋ฟ, ์ด๋ป๊ฒ ๋ก๋ฉํ ์ง(์ฟผ๋ฆฌ ๋ฐฉ์) ๋ ์ ์ํ์ง ์๋๋ค.
์ฆ, FetchType.EAGER ์ '๋น์ฅ ์ด ์ฐ๊ด ์ํฐํฐ๋ ๊ฐ์ด ํ์ํ๋๊น ์ง๊ธ ๋ก๋ฉํ์' ๋ ์๋ฏธ์ง๋ง, ์กฐ์ธ์ผ๋ก ๊ฐ์ ธ์ฌ์ง, ์ถ๊ฐ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆด์ง ๋ JPA ๊ตฌํ์ฒด(Hibernate) ๊ฐ ์ ํํ๋ ๊ฒ์ด๋ค.
Hibernate ์์์ fetchType ๋์ ๋ฐฉ์
findById() (JPQL X) | ๋ณดํต LEFT JOIN ์ผ๋ก ์ฐ๊ด ์ํฐํฐ๊น์ง ํ ๋ฒ์ ๊ฐ์ ธ์ด |
findByXXX() (Query Method) | ํ๋ก์๋ฅผ ์ฐ์ ๋ฐํํ๊ณ , ์ค์ ํ๋๋ฅผ ์ฌ์ฉํ ๋ N๋ฒ์ select ๋ก ์ถ๊ฐ ์กฐํ ๋ฐ์ (N+1 ๋ฌธ์ ) |
JPQL ์์ฑ ์ fetch join ๋ช ์ | JOIN FETCH ๋ก ๋ช ์์ ์ผ๋ก ์กฐ์ธํ์ฌ ํ ๋ฒ์ ๊ฐ์ ธ์ฌ ์ ์์ |
Hibernate ๋ ์์ ๊ฐ์ ๋ฐฉ์์ ๋ฐ๋ฅด๊ณ ์๋ค. ์์ ๊ฐ์ ๊ฒฝ์ฐ findByMember ๋ ์ฟผ๋ฆฌ ๋ฉ์๋์ด๊ธฐ์ ํ๋ก์๋ก BlogMember ๊ฐ ์ฐ์ ์ ์ผ๋ก ๋ฐํ๋ ๊ฒ์ด๊ณ , EAGER ์ด๊ธฐ์ ๋ฐ๋ก BlogMember ๋ฅผ ๊ฐ์ ธ์์ผ ํด์ ๋ฐ๋ก ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ ๊ฒ์ด๋ค.
๋ง์ฝ LAZY ์๋ค๋ฉด, ์ค์ ๋ก ์ฌ์ฉ๋๋ ์์ ์ ํด๋น ๊ฐ์ฒด๊ฐ ํ๋ก์๊ฐ ์๋ ๊ฒ์ผ๋ก ๋์ฒด๋๊ธฐ ๋๋ฌธ์ .getBlogMember() ์ ๊ฐ์ด ์ค์ ๋ก ํธ์ถ๋๋ ์์ ์ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ์ ๊ฒ์ด๋ค.
์ด๋ ๊ณต์ ๋ฌธ์์์๋ ์ค์ ๋ก ์ธ๊ธ๋์ด ์๋ค.
https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#fetching
Hibernate ORM User Guide
Starting in 6.0, Hibernate allows to configure the default semantics of List without @OrderColumn via the hibernate.mapping.default_list_semantics setting. To switch to the more natural LIST semantics with an implicit order-column, set the setting to LIST.
docs.jboss.org
์ ํํด์ผ ํ๋ ์ ๋ต

- ๋ชจ๋ ์ฐ๊ด ๊ด๊ณ๋ ๊ธฐ๋ณธ์ ์ผ๋ก LAZY๋ก ์ค์
- ํ์ํ ๊ฒฝ์ฐ์๋ง JOIN FETCH ๋ก ๋ช ์์ ๋ก๋ฉ ์ ์ด
EAGER ๋ก ์ค์ ํ๊ฒ ๋๋ฉด, ๋ฌด์๋ณด๋ค ๊ฐ๋ฐ์๊ฐ ์์ธกํ ์ ์๋ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ ๊ฐ๋ฅ์ฑ์ด ์กด์ฌํ๊ธฐ ๋๋ฌธ์ LAZY ๋ฅผ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ํํ๊ณ FETCH JOIN ์ ํตํด ์ ์ดํ ์ ์๋ ์ ์ ๋๋ ๊ฒ์ด ๊ฐ์ฅ ์ ์ ํ๋ค.
EAGER ๋ ๊ทธ๋ผ ์ ์์๊น?
์์ด๋ฌ๋ํ๊ฒ๋, Hibernate ์ ๊ณต์ ๋ฌธ์์์๋ LAZY ๋ก๋ฉ์ ์ฌ์ฉํ ๊ฒ์ ๊ฐ์กฐํ๋ฉด์๋ Jakarta ์์๋ ๋ชจ๋ ๊ตฌํ์ฒด๋ค์ด EAGER ๋ฅผ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํด์ผ ํ ๊ฒ์ ๊ฐ์ํ๊ณ ์๋ค.

JPA 1.0 ์์๋ ๋ชจ๋ ๊ตฌํ์ฒด๋ค์ด ํ๋ก์๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ์ง ์์๋ค. ๊ทธ๋์ @ManyToOne, @OneToOne ์ ๋ชจ๋ fetchType ์ด ๊ธฐ๋ณธ์ ์ผ๋ก EAGER ์๋ ๊ฒ์ด๋ค.
์ฆ, ์ด๊ธฐ JPA ํ์ค์ด ๋ชจ๋ ๊ตฌํ์ฒด๊ฐ ํ๋ก์๋ฅผ ํตํ ์ ์ฐํ ์ง์ฐ ๋ก๋ฉ์ ์ ๊ณตํ ๊ฒ์ด๋ผ๊ณ ํ์ ํ์ง ๋ชปํ๊ธฐ ๋๋ฌธ์, ๋ชจ๋ ๊ณต๊ธ์๊ฐ ํ์คํ ์ง์ํ ์ ์๋ '์ฆ์ ๋ก๋ฉ'์ ๊ณตํต์ ๊ธฐ๋ณธ ์๋ ๋ฐฉ์์ผ๋ก ์ผ์ ํธํ์ฑ๊ณผ ๊ธฐ๋ณธ์ ์ธ ๋ฐ์ดํฐ ๋ก๋ฉ์ ๋ณด์ฅํ๋ ค ํ๊ธฐ ๋๋ฌธ์ด๋ค.
๋ํ ๊ฐ์ธ์ ์ธ ์๊ฐ์ผ๋ก๋, EAGER ์์ฒด๊ฐ ๊ฐ์ฒด์งํฅ์ ์ธ ๊ด์ ์ ๋ ๊ฐ๊น๋ค๊ณ ๋ณด์ฌ์ง๋ค.
blog.getBlogMember() ๋ฅผ ํ๋๋ฐ ๊ทธ์ ์ผ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๊ณ ํ๋ก์, ์ฌ์ค์ ๊ฐ์ง ๊ฐ์ฒด๊ฐ ํ๋์ ์กด์ฌํ๋ค๋ ๊ฒ ์์ฒด๊ฐ ๊ฐ์ฒด์งํฅ์ ์ด์ง๋ ์์ ๊ด์ ์ด๋ค. ๊ทธ๋ ๊ธฐ์ ์ด๋ป๊ฒ ๋ณด๋ฉด ์ด๊ธฐ์๋ EAGER ์ ๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ์ ๊ณ ๋ คํ๋ ๊ฒ ๋น์ฐํ์ง ์์๊น ์ถ๊ธฐ๋ ํ๋ค.