Entity Graph 란.
- Fetch 전략을 유연하게 설정할 수 있게 해준다.
현재 Entity 의 주인은 Comment , N 으로서 @ManyToOne , 기본 FETCH 전략은 EAGER
@Entity
public class Comment {
@Id @GeneratedValue
private Long id;
private String title;
private String comment;
@ManyToOne
private Post post;
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
@Override
public String toString() {
return "Comment{" +
"id=" + id +
", title='" + title + '\'' +
", comment='" + comment + '\'' +
'}';
}
}
@RunWith(SpringRunner.class)
@DataJpaTest
public class EntityGraphTest {
@Autowired
private PostRepository postRepository;
@Autowired
private CommentRepository commentRepository;
@Test
public void testEntityGraph(){
Optional<Comment> selectedComment_opt = commentRepository.findById(1L);
}
}
///
Hibernate:
select
comment0_.id as id1_0_0_,
comment0_.comment as comment2_0_0_,
comment0_.post_id as post_id4_0_0_,
comment0_.title as title3_0_0_,
post1_.id as id1_1_1_,
post1_.created as created2_1_1_,
post1_.title as title3_1_1_
from
comment comment0_
left outer join
post post1_
on comment0_.post_id=post1_.id
where
comment0_.id=?
2020-11-18 23:17:20.078 TRACE 564 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
@ManyToOne 의 FETCT 전략을 LAZY 로 변경하면 필요할 때에만 Join 으로 데이터를 가져오게 된다.
@Entity
public class Comment {
@Id @GeneratedValue
private Long id;
private String title;
private String comment;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
@Override
public String toString() {
return "Comment{" +
"id=" + id +
", title='" + title + '\'' +
", comment='" + comment + '\'' +
", post=" + post +
'}';
}
}
테스트 코드를 짜던 중에 Post Entity 가 Transient 상태이기 때문에 flush() 시키기 전에 저장해 달라는 친절한 에러메세지가 나와서 행복해했다. 1차 캐싱으로 인해 select 문은 날리지 않았다.
@RunWith(SpringRunner.class)
@DataJpaTest
public class EntityGraphTest {
@Autowired
private PostRepository postRepository;
@Autowired
private CommentRepository commentRepository;
@Test
public void testEntityGraph(){
Post post = new Post();
post.setTitle("Spring");
Comment comment = new Comment();
comment.setTitle("JPA");
Post savedPost = postRepository.save(post);
comment.setPost(savedPost);
commentRepository.save(comment); // 주인 Entity 에서 영속화 시켜야만 한다. 주의.
commentRepository.flush();
Optional<Comment> selectedComment_opt = commentRepository.findById(2L);
String commentToString = selectedComment_opt.get().toString();// toString 으로 인해 post 멤버변수도 찍혀야하는 상황
System.out.println(commentToString);
}
}
///
Hibernate:
insert
into
post
(created, title, id)
values
(?, ?, ?)
2020-11-18 23:31:50.442 TRACE 10292 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [TIMESTAMP] - [null]
2020-11-18 23:31:50.443 TRACE 10292 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [Spring]
2020-11-18 23:31:50.446 TRACE 10292 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [BIGINT] - [1]
Hibernate:
insert
into
comment
(comment, post_id, title, id)
values
(?, ?, ?, ?)
2020-11-18 23:31:50.450 TRACE 10292 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [null]
2020-11-18 23:31:50.451 TRACE 10292 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [1]
2020-11-18 23:31:50.452 TRACE 10292 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [VARCHAR] - [JPA]
2020-11-18 23:31:50.453 TRACE 10292 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [BIGINT] - [2]
Comment{id=2, title='JPA', comment='null', post=Post{id=1, title='Spring', created=null}}
EntityGraph 존재 이유
- 설정 상태에서 필요한 경우 FETCH 전략을 바꾸고자한다.
- @NamedEntityGraph 어노테이션 사용
- name 속성에 EntityGraph 의 이름 지정
- attributeNodes 속성에 @NamedAttributeNode로 연관관계 이름을 주면 된다.
@NamedEntityGraph(name = "Comment.post",attributeNodes = @NamedAttributeNode("post"))
@Entity
public class Comment {
@Id @GeneratedValue
private Long id;
private String title;
private String comment;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
@Override
public String toString() {
return "Comment{" +
"id=" + id +
", title='" + title + '\'' +
", comment='" + comment + '\'' +
", post=" + post +
'}';
}
}
설정이 완료되면 사용은 Repository 인터페이스에서 한다.
- 메서드에 @EntityGraph 어노테이션 사용
- value 속성에 EntityGraph 이름 기술
- type 속성에 EntityGraphType 기술
- EntityGraphType 의 기본 값은 FETCH , 설정한 어트리뷰트는 EAGER , 나머지 어트리뷰트는 LAZY 로 가져오는 전략이다. 원시타입 어트리뷰트는 EAGER 로 가져온다.
- EntityGraphType.LOAD 는 설정한 어트리뷰트는 EAGER 로 가져오며 나머지 어트리뷰트는 기본 전략을 따른다.
public interface CommentRepository extends JpaRepository<Comment,Long> {
@EntityGraph(value = "Comment.post",type = EntityGraph.EntityGraphType.LOAD)
Optional<Comment> getById(Long id);
}
해당 메서드 호출시 LAZY 전략임에도 불구하고 @EntityGraph 에 의해 EAGER 전략으로 가져온다.
@Test
public void loadCommentById(){
Post post = new Post();
post.setTitle("Spring");
Comment comment = new Comment();
comment.setTitle("JPA");
Post savedPost = postRepository.save(post);
comment.setPost(savedPost);
commentRepository.save(comment);
commentRepository.flush();
Optional<Comment> selectedComment_opt = commentRepository.getById(2L);
}
///
Hibernate:
select
comment0_.id as id1_0_0_,
post1_.id as id1_1_1_,
comment0_.comment as comment2_0_0_,
comment0_.post_id as post_id4_0_0_,
comment0_.title as title3_0_0_,
post1_.created as created2_1_1_,
post1_.title as title3_1_1_
from
comment comment0_
left outer join
post post1_
on comment0_.post_id=post1_.id
where
comment0_.id=?
2020-11-18 23:50:17.293 TRACE 13864 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [2]
2020-11-18 23:50:17.307 TRACE 13864 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([id1_0_0_] : [BIGINT]) - [2]
2020-11-18 23:50:17.307 TRACE 13864 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([id1_1_1_] : [BIGINT]) - [1]
Entity 클래스에 @NameEntityGraph 없이 Repository 메서드에 @EntityGraph 어노테이션의 attributePaths 에 배열로 원하는 프로퍼티를 제시, FETCH 전략을 제공할 수 있다.
public interface CommentRepository extends JpaRepository<Comment,Long> {
@EntityGraph(attributePaths = "post",type = EntityGraph.EntityGraphType.LOAD)
Optional<Comment> getById(Long id);
}
'springframework > Spring Data JPA' 카테고리의 다른 글
Spring Data JPA : Specifications (0) | 2020.11.19 |
---|---|
Spring Data JPA : Projection (0) | 2020.11.19 |
Spring Data JPA : Update 쿼리 (0) | 2020.11.18 |
Spring Data JPA : Named Parameter , SpEL (0) | 2020.11.18 |
Spring Data JPA : Sort 시 내부함수 호출 (0) | 2020.11.18 |