스프링 데이터 JPA-20.EntityGraph, Projection
스프링 데이터 JPA: EntityGraph
-쿼리 메소드 마다 연관 관계의 Fetch 모드를 설정 할 수 있습니다.
@NamedEntityGraph
- @Entity에서 재사용할 여러 엔티티 그룹을 정의할 때 사용.
@EntityGraph
- @NamedEntityGraph에 정의되어 있는 엔티티 그룹을 사용 함.
- 그래프 타입 설정 가능
- (기본값) FETCH: 설정한 엔티티 애트리뷰트는 EAGER 패치 나머지는 LAZY 패치.
- LOAD: 설정한 엔티티 애트리뷰트는 EAGER 패치 나머지는 기본 패치 전략 따름.
@ManyToOne(fetch = FetchType.LAZY) 라면 코멘트만 가져온다.
예제코드
1.
// 연관관계만 정의
@NamedEntityGraph(name="Comment.post", attributeNodes = @NamedAttributeNode("post"))
@Entity @Getter @Setter
public class Comment {
public interface CommentRepository extends JpaRepository<Comment, Long> {
@EntityGraph(value = "Comment.post")
Optional<Comment> getById(Long id);
- 엔티티에 선언한 @NamedEntityGraph를 메소드에 정의
public interface CommentRepository extends JpaRepository<Comment, Long> { @EntityGraph(attributePaths = "post") Optional<Comment> getById(Long id);
둘 다 동일.
조인해서 post도 가져온다.
스프링 데이터 JPA: Projection
엔티티의 일부 데이터만 가져오기.
인터페이스 기반 프로젝션
- Nested 프로젝션 가능.
- Closed 프로젝션
- 쿼리를 최적화 할 수 있다. 가져오려는 애트리뷰트가 뭔지 알고 있으니까.
- Java 8의 디폴트 메소드를 사용해서 연산을 할 수 있다.
- Open 프로젝션
- @Value(SpEL)을 사용해서 연산을 할 수 있다. 스프링 빈의 메소드도 호출 가능.
- 쿼리 최적화를 할 수 없다. SpEL을 엔티티 대상으로 사용하기 때문에.
클래스 기반 프로젝션
- DTO
- 롬복 @Value로 코드 줄일 수 있음
다이나믹 프로젝션
- 프로젝션 용 메소드 하나만 정의하고 실제 프로젝션 타입은 타입 인자로 전달하기.
<T> List<T> findByPost_Id(Long id, Class<T> type);
Closed 프로젝션
public interface CommentSummary {
String getComment();
int getUp();
int getDown();
}
public interface CommentRepository extends JpaRepository<Comment, Long> {
// 한정적인 Closed 프로젝션 방식, 쿼리 최적화-원하는 것만 가져온다.
List<CommentSummary> findByPost_Id(Long id);
실행결과
Hibernate:
select
comment0_.comment as col_0_0_,
comment0_.up as col_1_0_,
comment0_.down as col_2_0_
from
comment comment0_
left outer join
post post1_
on comment0_.post_id=post1_.id
where
post1_.id=?
Open 프로젝션
// 클래스로 만들어도 동일하나 코드가 더 길어진다. 선언, get,settter + 생성자
public interface CommentSummary {
String getComment();
int getUp();
int getDown();
//@Value("#{target.up + ' ' + target.down}")
//String getVotes();
// 위와
default String getVotes(){
return getUp() + " " + getDown();
}
public interface CommentOnly {
String getComment();
}
public interface CommentRepository extends JpaRepository<Comment, Long> {
<T> List<T> findByPost_Id(Long id, Class<T> type);
// 제네릭하게 사용가능. CommentOnly, CommentSummary
// 하지만 타입을 넘겨줘야 한다.
@Test
public void getComment(){
Post post = new Post();
post.setTitle("jpa");
Post savedPost = postRepo.save(post);
Comment comment = new Comment();
comment.setPost(savedPost);
comment.setComment("spring data jpa projection");
comment.setUp(10);
comment.setDown(1);
commentRepo.save(comment);
commentRepo.findByPost_Id(savedPost.getId(), CommentOnly.class).forEach(c ->{
System.out.println("=======CommentOnly");
System.out.println(c.getComment());
});
commentRepo.findByPost_Id(savedPost.getId(), CommentSummary.class).forEach(c ->{
System.out.println("=======CommentSummary");
System.out.println(c.getVotes());
});
}
실행결과
Hibernate:
select
comment0_.comment as col_0_0_
from
comment comment0_
left outer join
post post1_
on comment0_.post_id=post1_.id
where
post1_.id=?
2020-01-10 21:05:18.408 TRACE 19292 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
2020-01-10 21:05:18.410 TRACE 19292 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([col_0_0_] : [VARCHAR]) - [spring data jpa projection]
=======CommentOnly
spring data jpa projection
Hibernate:
select
comment0_.comment as col_0_0_,
comment0_.up as col_1_0_,
comment0_.down as col_2_0_
from
comment comment0_
left outer join
post post1_
on comment0_.post_id=post1_.id
where
post1_.id=?
2020-01-10 21:05:18.431 TRACE 19292 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
2020-01-10 21:05:18.432 TRACE 19292 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([col_0_0_] : [VARCHAR]) - [spring data jpa projection]
2020-01-10 21:05:18.433 TRACE 19292 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([col_1_0_] : [INTEGER]) - [10]
2020-01-10 21:05:18.433 TRACE 19292 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([col_2_0_] : [INTEGER]) - [1]
=======CommentSummary
10 1