본문 바로가기

Language/Java, Android

[Java, JPA] 도메인 객체간 연관관계에서 즉시로딩과 지연로딩 and 프록시 객체

특정 게시글을 클릭 할 때,

게시글과, 각 게시글에 딸린 첨부파일들을 데이터베이스에서 로딩하여 엔티티객체에 저장하여야하는데,

구현한 로직해서 게시글을 누르면

 

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: kr.com.test.jpa.domain.Board.board_files, could not initialize proxy - no Session 

 

위와 같이 LazyInitializationException이 발생하였다.

에러를 구글링해보니 엔티티 간 연관관계 설정시에 즉시로딩, 지연로딩에 관한 설정이 필요했다.

 

 

보통 엔티티를 설계할 때, 엔티티와 연관된 또다른 엔티티들이 같이 포함되어 설계된다.

 

 

필자의 경우, "게시글" 엔티티를 작성할 때,

한 게시글 엔티티당, "댓글"리스트와 "첨부파일" 리스트라는 연관된 엔티티를 함께 포함시켰다.

 

엔티티 설계 코드를 보자.

 

@Entity
@Table(name="board")
public class Board {
	
	@Id
	@Column(name="idx")
	@GeneratedValue
	private int idx;
	
	@Column(name="category", nullable=false)
	private char category;
	
	@Column(name="subject", nullable=false)
	private String subject;
	
	@Column(name="contents", nullable=false)
	private String contents;
	
	...
    ...
    ...
	
	@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)	// 지연 로딩. 연관된 엔티티를 실제 사용할 시점에 조회
	@JoinColumn(name="board_idx")
	private List<Comment> comments = new ArrayList<Comment>();
	
	@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)	// 즉시 로딩. Board 엔티티를 조회할때 Board_file 엔티티도 함께 조회
	@JoinColumn(name="board_idx")
	private List<Board_file> board_files = new ArrayList<Board_file>();
	
	@OneToOne
	@JoinColumn(name="member_idx")
	private Member member;
 
    ...
    ...

 

 

* 위와 같은 경우 엔티티에서 연관관계에 있는 다른 엔티티를 로딩 해야 한다.

JPA는 개발자가 연관된 엔티티의 조회시점을 선택할 수 있도록 두가지 방법을 제공한다.

 

 

- 즉시 로딩 : 엔티티를 조회할 때, 연관된 엔티티도 함께 조회한다.

위 예제에서는 특정 게시글 엔티티 1개가 조회될 때마다, 그와 연관된 다수의 첨부파일 엔티티들이 동시에 조회된다.

 

- 지연 로딩 : 연관된 엔티티를 실제 사용할 때 조회한다.

 

지연로딩의 시점이 정확히 이해가 되지 않아서, JPA 책 내용을 조금 참조해보았다.

연관개체가 실제 사용할 때의 시점이 정확이 어떤 의미인가?

 

아래의 지연로딩 실행 코드를 보자. (위 엔티티 예제를 기반으로 작성)

 

Board board = em.find(Board.class, "board_id");
List<Comment> comments = board.getComments();    // 임시로 프록시 객체를 넣어둠.
...
...
comments.getContents();     // 실제 댓글 객체 사용 시점

 

 

위 코드의 2때 줄을 보면, 연관 댓글 객체를 바로 로딩한것 처럼 생각할 수 있지만,

이 시점에서 연관객체를 데이터베이스에서 로딩하지않고, 프록시 객체를 넣어둔다.

 

* 프록시 객체는 연관된 객체에 대한 참조를 보관한다. 실제 댓글 엔티티 객체 대신에 데이터베이스 조회를 지연할 수 있는 일종의 가짜 객체라고 생각하면 된다.

 

그리고 3째줄을 보면, 위 프록시 객체의 getter메소드를 호출한다.

이 시점에서 실제 데이터베이스에서 연관된 comments 객체를 로딩하게 된다.

 

즉 2째줄까지는 가짜 엔티티 객체(프록시 객체), 즉 엔티티를 참조만 하고 있는 객체를 저장해두었다가, 실제로 Comments 엔티티 객체를 생성하는 시점은 3째줄이다.

 

 

 

 

참조 객체, 가짜 객체라고 하는 프록시 객체의 구조는 다음과 같다.

 

Class CommentProxy extends Comment {
  Comment target = null;  // 실제 엔티티 객체 참조

  public String getContents() {
    if(target == null) {
      // 이 시점에서 실제 엔티티가 비어있으므로, 
      // 데이터베이스에서 Comment 객체를 조회하고, contents 값을 리턴한다.
    }
  }

  ..
  ..
  ..
}

 

 

 

 

 

프록시 관련하여 참고한 책 :

https://book.naver.com/bookdb/book_detail.nhn?bid=9252528

 

자바 ORM 표준 JPA 프로그래밍

자바 ORM 표준 JPA는 SQL 작성 없이 객체를 데이터베이스에 직접 저장할 수 있게 도와주고, 객체와 관계형 데이터베이스의 차이도 중간에서 해결해준다. 이 책은 JPA 기초 이론과 핵심 원리, 그리고 실무에 필요한 성능 최적화 방법까지 JPA에 대한 모든 것을 다룬다. 또한, 스프링 프레임워크와 JPA를 함께 사용하는 방법을 설명하고, 스프링 데이터 JPA, QUERYDSL 같은 혁신적인 오픈 소스를 활용해서 자바 웹 애플리케이션을 효과적으로 개발하는

book.naver.com

 

[출처] https://jisooo.tistory.com/entry/Java-JPA-%EB%8F%84%EB%A9%94%EC%9D%B8-%EA%B0%9D%EC%B2%B4%EA%B0%84-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84%EC%97%90%EC%84%9C-%EC%A6%89%EC%8B%9C%EB%A1%9C%EB%94%A9%EA%B3%BC-%EC%A7%80%EC%97%B0%EB%A1%9C%EB%94%A9-and-%ED%94%84%EB%A1%9D%EC%8B%9C-%EA%B0%9D%EC%B2%B4

 

[Java, JPA] 도메인 객체간 연관관계에서 즉시로딩과 지연로딩 and 프록시 객체

특정 게시글을 클릭 할 때, 게시글과, 각 게시글에 딸린 첨부파일들을 데이터베이스에서 로딩하여 엔티티객체에 저장하여야하는데, 구현한 로직해서 게시글을 누르면 org.hibernate.LazyInitializationEx

jisooo.tistory.com