Lazy Loading
Lazy Loading(지연 로딩)은 JPA에서 연관된 엔터티를 실제 사용할 때 로드하는 방식
즉, 처음에는 프록시 객체만 생성해 두고, 해당 필드에 접근할 때 쿼리를 실행한다.
Lazy Loading을 사용하려면 @ManyToOne, @OneToMany, @OneToOne, @ManyToMany 관계에서
fetch = FetchType.LAZY 옵션을 설정하면 된다.
(기본적으로 @ManyToOne과 @OneToOne은 FetchType.EAGER, @OneToMany와 @ManyToMany는 FetchType.LAZY입니다.)
FetchType.LAZY 는 필요할 때만 조인 테이블을 조회하며, 기본 키는 바로 접근할 수 있어서 불필요한 조인을 피할 수 있다.
▹ 조회되는 모든 행마다 SELECT 하는게 아니라 한번만 가져온다.
★ 실행되는 시점
ModelMapper를 사용하여 엔티티 객체를 DTO로 매핑할 때, 연관된 엔티티가 Lazy Loading으로 설정된 경우, DTO에서 해당 필드를 참조하면 Lazy Loading이 발생하여 추가적인 쿼리가 실행된다.
동작 시점
- ModelMapper를 호출한 시점에 매핑이 실행됩니다.
- 매핑된 DTO에서 account와 같은 Lazy Loaded 필드를 참조하면, 해당 필드를 로딩하기 위해 쿼리가 실행됩니다.
- 반대로, DTO에서 account 필드를 참조하지 않으면 Lazy Loading 쿼리는 실행되지 않는다.
DTO에서 Lazy Loading을 발생시키는 상황
- DTO가 account를 참조하는 경우, 해당 연관 관계를 로딩하기 위해 Lazy Loading이 트리거됩니다.
- 이 때, ReportAndAccountDTO가 account를 포함하고 있다면, 해당 필드를 매핑하는 과정에서 Lazy Loading이 발생합니다.
Lazy Loading을 방지하는 방법
- DTO에서 Lazy Loaded 필드를 제외: account와 같은 필드를 DTO에서 제외하면 Lazy Loading을 발생시키지 않습니다.
- Fetch Join 사용: account를 포함한 엔티티를 JOIN FETCH로 즉시 로딩하여 Lazy Loading을 방지할 수 있습니다.
- @EntityGraph 사용: 엔티티에 @EntityGraph를 적용하여 연관된 엔티티를 즉시 로딩할 수 있습니다.
※ ModalMapper는 보통 @Bean으로 등록하여 전역에서 사용할 수 있게 하기도 한다.
사용 방법
조건.
[report] 테이블의 "account_id"는 [account] 테이블의 "account_id"와 외래키로 연결되어 있다.
문제.
[report] 의 목록을 조회
출력 형식 :
Page<{...Report, account: {...Account}}>
<Entity>
@NotNull
@Column(name = "account_id")
private Integer accountId;
/* Join */
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "account_id", referencedColumnName = "account_id", insertable = false, updatable = false)
private AccountEntity account;
1. [ReportEntity] 에서 [AccountEntity]와 "account_id"로 연결한다는 JoinColumn을 추가한다.
2. fetch = FetchType.LAZY를 선언하므로서 필요할 때만 조회하도록 한다.
<DTO>
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class ReportDTO{
@Schema(description = "계정 내부 아이디")
private Integer accountId;
...
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ReportAndAccountDTO extends ReportDTO{
@Schema(description = "계정, Account")
private Account.AccountDTO account;
}
3. DTO에 [Account] 를 담아갈 수 있는 컬럼 추가
ReportDTO는 CreditMonthlyReportEntity에서 필요한 정보만 포함
ReportAndAccountDTO는 ReportDTO를 상속하여 기본 정보를 포함하면서, 추가로 AccountDTO를 가짐
<Service>
@Transactional
public class ReportService {
...
public Page<Report.ReportAndAccountDTO> findPage(Pageable pageable) {
...
Page<Report.ReportAndAccountDTO> ReportDtoPage = ReportEntityPage
.map(entity -> modelMapper.map(entity, Report.ReportAndAccountDTO.class));
}
4. Service에 트랜잭션이 활성화되도록 @Transactional 선언
5. modalMapper로 entity를 DTO로 바꾸면서 [Account] 가 포함된 DTO 호출시 Lazy Loading이 발생한다.
* 트랜잭션 내에서 실행해야 오류 없음
<결과>
{
content {
report_id : 00,
account_id : 1,
...
account : {
account_id : 1,
...
}
}
}
'Spring' 카테고리의 다른 글
Spring, Pageable/List 기본 정렬 기준 (0) | 2025.03.18 |
---|---|
Spring, Service 자주 사용하는 어노테이션 정리 (0) | 2025.03.18 |
Spring, Controller 어노테이션 정리 (0) | 2025.03.17 |
Spring, Entity 구조 정리 / JPA에서 자주 사용되는 어노테이션 (0) | 2025.03.17 |
Spring, Entity와 DTO의 차이점 및 구조 파악 / 변환 방법 (0) | 2025.03.13 |