특정 게시글을 클릭 할 때,
게시글과, 각 게시글에 딸린 첨부파일들을 데이터베이스에서 로딩하여 엔티티객체에 저장하여야하는데,
구현한 로직해서 게시글을 누르면
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
'Language > Java, Android' 카테고리의 다른 글
[MacOS 개발환경 #9] DynamoDB 로컬(Local) 환경 설치 - Java (0) | 2021.11.23 |
---|---|
[JPA] 양방향 관계에서 infinite recursion 해결법 (0) | 2021.11.08 |
[JPA] @OneToOne, 일대일[1:1] 관계 (0) | 2021.11.05 |
[IntelliJ] java: cannot find symbol(클래스 import가 안될 때) (0) | 2021.11.05 |
IntelliJ cannot resolve symbol.. 에러 해결 (0) | 2021.11.04 |