우아한테크코스 팀 프로젝트에서 JPA를 사용하기로 했다.
JPA의 Lazy Loading에서 select문이 나가는 시점이 조금 헷갈려서, select 문이 실행되는 시점을 직접 확인해보았습니다.
@Entity
@Getter
public class Post {
@Id @GeneratedValue(strategy = IDENTITY)
private Long id;
private String title;
private String content;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "member_id")
private Member member;
protected Post() {
}
public Post(String title, String content, Member member) {
this.title = title;
this.content = content;
this.member = member;
}
}
@Entity
@Getter
public class Member {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "member_id")
private Long id;
private String name;
@OneToMany(mappedBy = "member")
private List<Post> posts = new ArrayList<>();
protected Member() {
}
public Member(String name) {
this.name = name;
}
}
먼저 테스트해본 Entity에 대해 간단히 짚고 넘어가보겠습니다.
Post와 Member는 N:1 양방향 연관관계를 맺습니다.
두 엔티티는 서로에 대해 Lazy Loading을 하도록 설정해두었습니다.
insert into member (name) values ('chris')
insert into post (content, title, member_id) values ('첫번째 글 제목', '첫번째 글 본문', 1)
insert into post (content, title, member_id) values ('두번째 글 제목', '두번째 글 본문', 1)
테스트를 위해서, 데이터를 먼저 insert 해주었고, 아래의 테스트에서 @sql을 통해서 Test 전에 실행되도록 했습니다.
@DataJpaTest
@Sql("classpath:test.sql")
class MyTest {
@Autowired
private MemberRepository memberRepository;
@Test
void myTest() {
Member found = memberRepository.findById(1L)
.orElseThrow(IllegalArgumentException::new);
System.out.println("-----------------Before getPosts()-----------------");
List<Post> posts = found.getPosts();
System.out.println("-----------------Before posts.get(0)-----------------");
Post post = posts.get(0);
System.out.println("-----------------Before posts.get(1)-----------------");
Post post2 = posts.get(1);
System.out.println(post.getContent());
System.out.println(post2.getContent());
}
}
실행결과
Hibernate: select member0_.member_id as member_i1_0_0_, member0_.name as name2_0_0_ from member member0_ where member0_.member_id=?
-----------------Before getPosts()-----------------
-----------------Before posts.get(0)-----------------
Hibernate: select posts0_.member_id as member_i4_1_0_, posts0_.id as id1_1_0_, posts0_.id as id1_1_1_, posts0_.content as content2_1_1_, posts0_.member_id as member_i4_1_1_, posts0_.title as title3_1_1_ from post posts0_ where posts0_.member_id=?
-----------------Before posts.get(1)-----------------
첫번째 글 제목
두번째 글 제목
위의 실행결과를 보면 found.getPosts()로 List를 조회해도 select문이 실행되지 않습니다. 이때까지는 List에 해당하는 프록시 객체만이 존재합니다.
그 후, posts.get(0)로 List<Post>의 요소를 조회할 때, select문이 실행되고 실제 객체들을 로딩
합니다.
또한, 위의 테스트에서 알 수 있는 것은 select 문이 실행될 때, member_id를 가진 모든 Post들을 다 조회해옵니다. 두번째 sql문을 보아도 알 수 있고, posts.get(1)를 해도 추가적인 select문이 실행되지 않습니다.
ONE의 pk가 아닌 컬럼에 접근
@DataJpaTest
@Sql("classpath:test.sql")
class MyTest {
@Autowired
private MemberRepository memberRepository;
@Test
void myTest() {
Post post = postRepository.findById(1L)
.orElseThrow(IllegalArgumentException::new);
System.out.println("-----------------Before getMember()-----------------");
Member member = post.getMember();
System.out.println("-----------------Before member.getName()-----------------");
System.out.println(member.getName());
}
}
실행결과
Hibernate: select post0_.id as id1_1_0_, post0_.content as content2_1_0_, post0_.member_id as member_i4_1_0_, post0_.title as title3_1_0_ from post post0_ where post0_.id=?
-----------------Before getMember()-----------------
-----------------Before member.getName()-----------------
Hibernate: select member0_.member_id as member_i1_0_0_, member0_.name as name2_0_0_ from member member0_ where member0_.member_id=?
chris
getMember로 Member 객체를 조회했을 때는 Proxy 객체 만이 존재합니다.
이후에 member의 content 필드를 조회할 때, select문이 실행
되는 것을 알 수 있습니다.
@DataJpaTest
@Sql("classpath:test.sql")
class MyTest {
@Autowired
private MemberRepository memberRepository;
@Test
void myTest() {
Post post = postRepository.findById(1L)
.orElseThrow(IllegalArgumentException::new);
System.out.println("-----------------Before getMember()-----------------");
Member member = post.getMember();
System.out.println("-----------------Before member.getId()-----------------");
System.out.println(member.getId());
}
}
실행결과
Hibernate: select post0_.id as id1_1_0_, post0_.content as content2_1_0_, post0_.member_id as member_i4_1_0_, post0_.title as title3_1_0_ from post post0_ where post0_.id=?
-----------------Before getMember()-----------------
-----------------Before member.getId()-----------------
1
N:1에서 N은 1의 PK를 컬럼으로 가지고 있기 때문에, 1의 getId()를 호출해도 select문이 실행되지 않습니다.
끗.