Projection 이란
- 엔티티의 속성 중 원하는 속성만 select 해오는 기능
- Closed Projecton , Open Projection 으로 나뉜다.
- Interface , Class 기반의 2가지 방법을 제공한다.
해당 코드는 Projection 미사용 시 모든 레코드를 조회 * 하고 있는 상황을 보여준다.
public interface CommentRepository extends JpaRepository<Comment,Long> {
List<Comment> findByPost_Id(Long id);
}
@Test
public void getComment(){
commentRepository.findByPost_Id(1L);
}
/// 모든 레코드를 다 가져옴
Hibernate:
select
comment0_.id as id1_1_,
comment0_.best as best2_1_,
comment0_.comment as comment3_1_,
comment0_.down as down4_1_,
comment0_.post_id as post_id7_1_,
comment0_.title as title5_1_,
comment0_.up as up6_1_
from
comment comment0_
left outer join
post post1_
on comment0_.post_id=post1_.id
where
post1_.id=?
2020-11-19 14:48:05.131 TRACE 18272 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
Closed Projection : 원하는 레코드만을 가져오는 방법
- 인터페이스 or 클래스로 getter 를 선언하여 원하는 컬럼을 정한다.
- JpaRepository 상속 클래스 메서드의 리턴타입 제네릭에 Projection 인터페이스 를 선언한다.
// 프로젝션 인터페이스에 원하는 레코드의 Getter 선언
public interface CommentSummary {
String getComment();
int getUp();
int getDown();
}
public interface CommentRepository extends JpaRepository<Comment,Long> {
// 리턴타입 제네릭에 프로젝션 인터페이스 선언
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=?
2020-11-19 14:49:31.979 TRACE 8152 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
Open Projection
- @Value + SpEL 을 사용하여 원하는 프로퍼티를 조합한 결과를 만들어낸다.
- 결과적으로 컬럼을 전부 추출하고 조합하는 형태를 가지고 있다.
- 성능최적화는 이룰 수 없으나, 원하는 데이터를 조합할 수 있게 된다.
public interface CommentSummary {
String getComment();
int getUp();
int getDown();
//Open Projection , @Value 에 표현식을 지정하여 가져온 Entity 객체의 프로퍼티를 조합시키고 있음
@Value("#{target.up + ' ' +target.down}")
String getVotes();
}
@Test
public void getComment() {
Post post = new Post();
post.setTitle("JPA");
Post savedPost = postRepository.save(post);
Comment comment = new Comment();
comment.setTitle("Spring");
comment.setPost(savedPost);
comment.setUp(10);
comment.setDown(1);
commentRepository.save(comment);
commentRepository.flush();
postRepository.flush();
List<CommentSummary> selectedComment = commentRepository.findByPost_Id(1L);
selectedComment.forEach( c -> {
System.out.println(c.getVotes());
});
}
/// 모든 컬럼을 다 select 하게 된다.
Hibernate:
select
comment0_.id as id1_1_,
comment0_.best as best2_1_,
comment0_.comment as comment3_1_,
comment0_.down as down4_1_,
comment0_.post_id as post_id7_1_,
comment0_.title as title5_1_,
comment0_.up as up6_1_
from
comment comment0_
left outer join
post post1_
on comment0_.post_id=post1_.id
where
post1_.id=?
2020-11-19 16:03:47.999 TRACE 9416 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
2020-11-19 16:03:48.003 TRACE 9416 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([id1_1_] : [BIGINT]) - [2]
10 1
Closed Projection 의 원하는 데이터 추출과 Open Projection 의 데이터 조합을 같이 사용할 수는 없는가.
- Java 8 부터 제공되는 인터페이스의 default 메서드를 사용한다.
- 해당 메서드에 원하는 Getter 를 선언해서 데이터를 조합하는 방식이다.
// Closed Projection 메서드 를 default 메서드에서 호출하여 원하는 결과를 뽑아 냄.
public interface CommentSummary {
String getComment();
int getUp();
int getDown();
default String getVote(){
return getUp()+ " "+getDown();
}
}
// 리포지토리 메서드의 리턴타입 제네릭에 선언하여 CommentSummary 를 사용하도록 함
public interface CommentRepository extends JpaRepository<Comment,Long> {
List<CommentSummary> findByPost_Id(Long id);
}
//테스트 코드, Closed Projection 으로 컬럼을 추출한 뒤 추출한 컬럼을 조합하는 방식
@Test
public void getVote() {
Post post = new Post();
post.setTitle("JPA");
Post savedPost = postRepository.save(post);
Comment comment = new Comment();
comment.setTitle("Spring");
comment.setPost(savedPost);
comment.setUp(10);
comment.setDown(1);
commentRepository.save(comment);
List<CommentSummary> selectedComment = commentRepository.findByPost_Id(1L);
selectedComment.forEach(c -> {
System.out.println(c.getVote());
});
}
/// 원하는 컬럼만을 추출하고 추출한 컬럼을 조합하는 모습을 보여줌
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-11-19 16:08:47.801 TRACE 18424 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
2020-11-19 16:08:47.808 TRACE 18424 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([col_0_0_] : [VARCHAR]) - [null]
2020-11-19 16:08:47.815 TRACE 18424 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([col_1_0_] : [INTEGER]) - [10]
2020-11-19 16:08:47.817 TRACE 18424 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([col_2_0_] : [INTEGER]) - [1]
10 1
하나의 메서드에 여러가지 Projection 을 받기 위해서는 제네릭 사용이 필요하다.
- 리턴 타입은 메서드 오버로딩에 영향을 주지 못한다.
- 그러므로 파라미터에 제네릭타입의 클래스를 던져주는 것이 좋다.
public interface CommentSummary {
String getComment();
int getUp();
int getDown();
default String getVote(){
return getUp()+ " "+getDown();
}
}
//Repository 인터페이스 에 리턴타입을 제네릭으로 선언한 후 파라미터에 Class 타입을 선언하도록 변경시킨 모습
public interface CommentRepository extends JpaRepository<Comment,Long> {
<T> List<T> findByPost_Id(Long id,Class<T> type);
}
// 테스트 코드에는 메서드 리턴타입에 대한 제네릭에 클래스 를 정하는 파라미터가 추가됬을 뿐이다.
@Test
public void getVoteGeneric() {
Post post = new Post();
post.setTitle("JPA");
Post savedPost = postRepository.save(post);
Comment comment = new Comment();
comment.setTitle("Spring");
comment.setComment("Hibernate");
comment.setPost(savedPost);
comment.setUp(10);
comment.setDown(1);
commentRepository.save(comment);
List<CommentOnly> selectedComment = commentRepository.findByPost_Id(1L, CommentOnly.class);
selectedComment.forEach(c -> {
System.out.println(c.getComment());
});
}
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-11-19 16:16:32.170 TRACE 12820 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
2020-11-19 16:16:32.177 TRACE 12820 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([col_0_0_] : [VARCHAR]) - [Hibernate]
Hibernate
클래스로 Projection을 만들 수 있으나 코드가 장황해진다.
public class CommentSummaryClass {
private String comment;
private int up;
private int down;
public CommentSummaryClass(String comment, int up, int down) {
this.comment = comment;
this.up = up;
this.down = down;
}
public String getVote(){
return getUp() + " " + getDown();
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public int getUp() {
return up;
}
public void setUp(int up) {
this.up = up;
}
public int getDown() {
return down;
}
public void setDown(int down) {
this.down = down;
}
}
'springframework > Spring Data JPA' 카테고리의 다른 글
Spring Data JPA : Query By Example (0) | 2020.11.19 |
---|---|
Spring Data JPA : Specifications (0) | 2020.11.19 |
Spring Data JPA : Entity Graph (0) | 2020.11.19 |
Spring Data JPA : Update 쿼리 (0) | 2020.11.18 |
Spring Data JPA : Named Parameter , SpEL (0) | 2020.11.18 |