Spring

Spring, Lazy Loading 사용하는 방법 / 외래키로 연결된 테이블의 데이터 같이 가져오는 방법(JPA)

greenyellow-s 2025. 3. 18. 09:14
728x90
반응형

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 방지하는 방법

  1. DTO에서 Lazy Loaded 필드를 제외: account와 같은 필드를 DTO에서 제외하면 Lazy Loading을 발생시키지 않습니다.
  2. Fetch Join 사용: account를 포함한 엔티티를 JOIN FETCH로 즉시 로딩하여 Lazy Loading을 방지할 수 있습니다.
  3. @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,
      ...
      }
  }
}

 

728x90
반응형