아카이브

[스프링 데이터 JPA] 스프링 데이터 JPA 8. Projection 본문

Spring/스프링 데이터 JPA

[스프링 데이터 JPA] 스프링 데이터 JPA 8. Projection

주멘이 2021. 1. 17. 12:45

엔티티의 일부 데이터만 가져오기

 

인터페이스 기반 Projection

  • Nested 프로젝션 가능
  • Closed 프로젝션
    • 일부만 가져오기 
    • 쿼리를 최적화할 수 있다. 가져오려는 애트리뷰트가 뭔지 알고 있으니까
    • Java 8의 디폴트 메서드를 사용해서 연산을 할 수 있다
  • Open 프로젝션
    • 전부 다 가져와서 가공하기
    • @Value(SpEL)을 사용해서 연산할 수 있다. 스프링 빈의 메서드 호출도 가능
    • 쿼리 최적화를 할 수 없다. SpEL을 엔티티 대상으로 사용하기 때문에

클래스 기반 Projection

  • DTO
  • Lombok @Value로 코드 줄일 수 있음  

다이내믹 Projection

  • 프로젝션용 메서드 하나만 정의하고, 실제 프로젝션 타입은 파라미터로 전달하기
<T> List<T> findByPost_Id(Long id, Class<T> type);

 

1. Closed Projection 실습

Comment 엔티티에 필드 추가

@NamedEntityGraph(name = "Comment.post", attributeNodes = @NamedAttributeNode("post"))
@Entity
@Getter
@Setter
public class Comment {

    @Id
    @GeneratedValue
    private Long id;

    private String comment;

    /*
     * ManyToOne에서 fetch default = EAGER
     * */
    @ManyToOne(fetch = FetchType.LAZY)
    private Post post;

    private Date created;

    private Integer likeCount = 0;
	
    /* 필드 추가 */
    private int up;

    private int down;

    private boolean best;


}

CommentSummary 인터페이스 추가

public interface CommentSummary {

    String getComment();

    int getUp();

    int getDown();

}

CommentRepository에 Closed Projection 방식의 메서드 추가하기

public interface CommentRepository extends MyRepository<Comment, Long> {
      /* CommentSummary로 한정적인 필드들만 받아온다  */
      List<CommentSummary> findByPost_id(Long id);

}

 

2. Open Projection 실습

CommentSummary에 Open Projection 필드 추가하기

@Value("#{target.up + ' ' + target.down}")
String getVotes();
  • target==comment. 모든 필드를 가져와서 조합하기 때문에 Open Projection이라 칭한다.

 

3. Open Projection의 장점 + Closed Projection 사용하기

CommentSummary에 default method 추가

  • Java8부터 인터페이스에 default 메서드를 추가할 수 있다.
  • 이에 따라 Closed Projection 인터페이스에 default 메서드를 추가하여
    • 필드를 한정지어 가져오고(쿼리 최적화)
    • 조합이나 연산이 가능하게 할 수 있다(Open Projection의 장점)
  • 가장 좋은 방법으로 권장.
package me.jumen.springdatajpa;

public interface CommentSummary {

    String getComment();

    int getUp();

    int getDown();

    /**
     * Open Projection의 장점을 살리면서 Closed 로 동작하게 하는 방법
     * 쿼리 최적화 + custom 구현체
     * 가장 좋은 방법이다.
     * */
    default String getVotes() {
        return getUp() + " " + getDown();

    }

}

 

4. 다이나믹 Projection

  • 프로젝션용 메서드 하나만 정의하고, 실제 프로젝션 타입은 파라미터로 전달한다.
  • Repository에 쿼리 메서드를 더 추가하지 않고, 간결하게 다양화할 수 있다.

CommentOnly 인터페이스 추가

public interface CommentOnly {
    String getComment();
}

 

CommentRepository에 프로젝션 타입 메서드 추가하기

    /* Dynamic Projection */
    <T> List<T> findByPost_id(Long id, Class<T> type);

테스트 코드

    @Test
    public void getCommentSummaryByPostId() {
        Post post = new Post();
        post.setTitle("jpa");
        Post savedPost = postRepository.save(post);

        Comment comment = new Comment();
        comment.setComment("spring data jpa projection");
        comment.setPost(savedPost);
        comment.setUp(10);
        comment.setDown(1);
        commentRepository.save(comment);

        /**
         * Dynamic Projection
         * Projection 메서드 하나만 정의하고, 실제 Projection type은 parameter로 전달하여
         * 별도의 쿼리 메서드를 추가하지 않고 다양하게 만들 수 있다.
         * */

        /* CommentSummary */
        commentRepository.findByPost_id(1l, CommentSummary.class).forEach(c -> {
            System.out.println("=====================");
            System.out.println(c.getVotes());
        });

        /* CommentOnly */
        commentRepository.findByPost_id(1l, CommentOnly.class).forEach(c -> {
            System.out.println("=====================");
            System.out.println(c.getComment());
        });
    }