springframework/Spring Data JPA

Fetch 전략

Jungsoomin :) 2020. 11. 13. 23:09

Fetch : 연관 관계에 있는 Entity 를 어떻게 가져올 것인지에 대한 설정

 

  • Eager : 지금 가져온다.
  • Lazy : 나중에 가져온다.

@OneToMany 의 Fetch 정보는 Lazy 이다. 

  • Eager 일 경우 1 릴레이션의 Entity 정보를 가져올때 N 정보를 모두 가져와 불필요한 정보를 객체에 로딩하게 될 가능성이 높아서이다.
@Entity
public class Post {

    @Id @GeneratedValue
    private Long id;

    private String title;

    // Entity 를 관리할 때 참조하는 Comment Entity 도 Persistent 상태로 관리해주렴.
    @OneToMany(mappedBy = "post", cascade = {CascadeType.ALL},fetch = FetchType.LAZY)
    private Set<Comment> comments = new HashSet<>();

    public void addComment(Comment comment){
        comments.add(comment);
        comment.setPost(this);
    }


    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 Set<Comment> getComments() {
        return comments;
    }

    public void setComments(Set<Comment> comments) {
        this.comments = comments;
    }
}

@ManyToOne 의 Fetch 정보는 Eager 이다.

  • N 릴레이션의 Entity 정보를 가져올 때 1릴레이션과 관련된 정보를 가져올 수 있다.
@Entity
public class Comment {

    @Id @GeneratedValue
    private Long id;

    private String comment;

    @ManyToOne(fetch = FetchType.EAGER)
    private Post post;


    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public Post getPost() {
        return post;
    }

    public void setPost(Post post) {
        this.post = post;
    }
}

@OneToMany 의 Fetch 전략은 LAZY 라고 했다.

  • 1 릴레이션을 가지는 Post 를 이용해서 Select 쿼리를 돌리면 Comment Entity 정보는 가져오지 않는다.
@Component
@Transactional
public class JpaParentChileRunner implements ApplicationRunner {

    @PersistenceContext
    private EntityManager entityManager;
    @Override
    public void run(ApplicationArguments args) throws Exception {

        Session session = entityManager.unwrap(Session.class);
        
        Post post = session.get(Post.class, 18L);
        System.out.println("===================================");
        System.out.println(post.getTitle());
        System.out.println("===================================");
    }
}
Hibernate: 
    select
        post0_.id as id1_2_0_,
        post0_.title as title2_2_0_ 
    from
        post post0_ 
    where
        post0_.id=?
===================================
Spring Data JPA 언제 보나.
===================================

Fetch 전략을 EAGER 로 돌린다면 어떻게 되는가.

  • Post Entity 를 이용해서 Select 쿼리를 돌리면 Join 으로 Comment 테이블도 함께 가져와 참조되는 Entity 에도 데이터를 저장하게 된다.
@Entity
public class Post {

    @Id @GeneratedValue
    private Long id;

    private String title;

    // Entity 를 관리할 때 참조하는 Comment Entity 도 Persistent 상태로 관리해주렴.
    @OneToMany(mappedBy = "post", cascade = {CascadeType.ALL},fetch = FetchType.EAGER)
    private Set<Comment> comments = new HashSet<>();

    public void addComment(Comment comment){
        comments.add(comment);
        comment.setPost(this);
    }


    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 Set<Comment> getComments() {
        return comments;
    }

    public void setComments(Set<Comment> comments) {
        this.comments = comments;
    }
}

DDL = left outer join 으로 미리 연관 Entity 정보까지 가져오는 모습을 보여준다.

Hibernate: 
    select
        post0_.id as id1_2_0_,
        post0_.title as title2_2_0_,
        comments1_.post_id as post_id3_1_1_,
        comments1_.id as id1_1_1_,
        comments1_.id as id1_1_2_,
        comments1_.comment as comment2_1_2_,
        comments1_.post_id as post_id3_1_2_ 
    from
        post post0_ 
    left outer join
        comment comments1_ 
            on post0_.id=comments1_.post_id 
    where
        post0_.id=?
===================================
Spring Data JPA 언제 보나.
===================================

@ManyToOne 의 Fetch 전략은 EAGER 라고 했다.

  • 셀렉트 쿼리를 사용할 때 이미 테이블이 Join 되어진 쿼리문을 사용하기 때문에 인스턴스에는 이미 Post에 대한 데이터가 저장되어 있다.
@Component
@Transactional
public class JpaParentChileRunner implements ApplicationRunner {

    @PersistenceContext
    private EntityManager entityManager;
    @Override
    public void run(ApplicationArguments args) throws Exception {

        Session session = entityManager.unwrap(Session.class);

        Comment comment = session.get(Comment.class, 19L);
        System.out.println("===================================");
        System.out.println(comment.getPost().getTitle());
        System.out.println("===================================");
    }
}

DDL = left outer join 으로 1 릴레이션 정보까지 가져오고 있다.

Hibernate: 
    select
        comment0_.id as id1_1_0_,
        comment0_.comment as comment2_1_0_,
        comment0_.post_id as post_id3_1_0_,
        post1_.id as id1_2_1_,
        post1_.title as title2_2_1_ 
    from
        comment comment0_ 
    left outer join
        post post1_ 
            on comment0_.post_id=post1_.id 
    where
        comment0_.id=?
===================================
Spring Data JPA 언제 보나.
===================================

Fetch 전략은 성능에 영향을 미친다.

  • 객체에 불필요한 정보를 너무 많이 담게 되는 것을 주의해야한다.
  • n + 1 문제가 일어날 수 있다.
@Component
@Transactional
public class JpaParentChileRunner implements ApplicationRunner {

    @PersistenceContext
    private EntityManager entityManager;
    @Override
    public void run(ApplicationArguments args) throws Exception {

        Session session = entityManager.unwrap(Session.class);


        Post post = session.get(Post.class, 18L);
        System.out.println("===================================");
        System.out.println(post.getTitle());
        System.out.println("===================================");

        // 순회
        post.getComments().forEach( c -> {
            System.out.println("---------------------");
            System.out.println(c.getComment());
        });
    }
}

DDL = n+1 문제를 일으키지는 않는 상태, 순회 상 하나의 Post 에 맞는 Comment 를 가져오면서 n + 1 문제가 될 것 같았으나 , post 릴레이션의 PK 로 관련 Comment 를 끌어오는 모습

Hibernate: 
    select
        post0_.id as id1_2_0_,
        post0_.title as title2_2_0_ 
    from
        post post0_ 
    where
        post0_.id=?
===================================
Spring Data JPA 언제 보나.
===================================
Hibernate: 
    select
        comments0_.post_id as post_id3_1_0_,
        comments0_.id as id1_1_0_,
        comments0_.id as id1_1_1_,
        comments0_.comment as comment2_1_1_,
        comments0_.post_id as post_id3_1_1_ 
    from
        comment comments0_ 
    where
        comments0_.post_id=?
---------------------
빨리 보고 싶어요..
---------------------
곧 보여드릴게요.

'springframework > Spring Data JPA' 카테고리의 다른 글

Spring Data JPA 의 원리와 소개  (0) 2020.11.14
Query  (0) 2020.11.13
Cascade, Entity 상태  (0) 2020.11.13
관계 맵핑, 1 : N  (0) 2020.11.13
Value 타입 맵핑  (0) 2020.11.13