[bootBoard] N:1(다대일) 연관관계: 6-2. @ManyToOne과 Lazy loading
이 포스팅에서는 Lazy Loading의 효과를 알아본다.
1. Board.java를 수정하여 Lazy Loading을 설정
우선 Board 클래스를 아래와 같이 수정한다.
//Board.java
package com.example.bootboard.entity;
import lombok.*;
import javax.persistence.*;
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@ToString(exclude = "writer") //이렇게 해준 이유는 아래에서 설명함
public class Board extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long bno;
private String title;
private String content;
@ManyToOne(fetch = FetchType.LAZY) //명시적으로 Lazy Loading 지정
private Member writer; //FK 연관관계 지정
//Board클래스는 Member클래스의 email(PK)를 FK로 참조하는 구조이다.
}
fetch는 데이터를 가져오는 방식을 의미한다. 여기서는 fetch를 이용해서 @ManyToOne의 파라미터를 조정해 주었다.
설정을 완료했으면 BoardRepositoryTests.java의 testRead1() 을 실행한다.
System.out.println(board);까지는 오류가 발생하지 않는다.
System.out.println(board.getWriter());에서 오류가 발생하는 것이다. 이 값을 가져오려면 member 테이블을 로딩해야 하는데 그 전에 이미 DB 연결이 끊어졌기 때문이다. no Session 메시지는 이런 경우에 발생한다.
이 문제를 해결하려면 다시 한 번 DB가 연결되어야 한다. @Transactional 어노테이션을 사용하면 이를 해결할 수 있다.
@Transactional은 해당 메서드를 하나의 트랜젝션으로 처리하도록 만든다. 이 어노테이션을 해당 메서드 선언부 상단에 위치시킨다.
- 먼저 board 테이블을 로딩하여 처리한다.
- 연결이 끝나면 board.getWriter()를 처리하기 위해 member 테이블을 로딩한다.
Eager loading을 사용 시 조인하여 한 번에 가져오는 방식과는 다르다.
2. 연관관계에서의 @ToString
엔티티 간 연관관계를 지정하는 경우 @ToString()을 주의할 것.
@ToString은 해당 클래스의 모든 멤버 변수를 출력한다. Board 객체를 @ToString()하면 연관관계 설정을 위해 기입한 private Member member 역시 출력한다. 이 과정에서 Member의 ToString() 역시 호출된다. 이 때 DB 연결이 필요하게 된다.
이런 문제를 방지하기 위해서, 연관관계에 있는 엔티티 클래스의 경우 @ToString()을 할 때는 exclude 속성을 사용한느 것이 좋다. exclude는 해당 속성 값으로 지정된 변수는 toString()에서 제외하지 때문이다. 지연로딩 사용시 아래와 같이 지정해 주는 것이 좋다.
@ToString(exclude ="writer") : writer 변수는 toString에서 제외한다.
3. Lazy Loading의 장단점
장점
- 지연로딩은 조인을 하지 않는다. 필요한 순간에 쿼리를 실행한다. 따라서 하나의 테이블을 이용하는 경우 즉시로딩보다 처리속도가 상대적으로 빠르다.
단점
- 연관관계가 복잡한 경우 여러 번의 쿼리가 실행된다.
보편적으로는 지연로딩을 기본으로 사용한다. 상황에 따라 지연로딩이 아닌 다른 방법을 고려해야 하는 경우도 존재한다.