본문 바로가기

JPA

[JPA] 지연로딩과 영속성전이

JPA 연관관계 관리

비즈니스로직을 분석해보니, 대부분의 로직에선 엔티티 A만 필요했다. 이런 상황에서, 엔티티 A에 연관되어있는 엔티티 B까지 같이 메모리에 올리는것은 비 효율적이며, 성능또한 느리게 한다. 

 

JPA는 즉시로딩과 지연로딩이라는 기법을 통해, 연관 엔티티의 로딩시점을 정할수있도록 해준다.

JPA 프록시

지연로딩을 이해하기위해선, 하이버네이트에서 제공하는 프록시를 이해해야한다.

public void 테스트1(){
    EntityA entityA = em.getReference(EntityA.class,"e1");
}

public void 테스트2(){
    EntityA entity1 = em.find(EntityA.class, "e1");
    EntityA entity2 = em.getReference(EntityA.class,"e1");
}

위는 DB에서 EntityA를 조회하는 코드다.

테스트1()의 entityA에는 EntityA클래스가 저장되는것이 아닌, EntityA 클래스를 흉내내는 EntityA프록시 객체가 저장된다.

이 후, entityA의 메소드를 사용할때, 프록시 객체가 초기화되어, 실제 entityA의 값을 사용할 수 있게 된다.

 

테스트2() 의 경우, "e1"식별자를 갖고있는 EntityA를 로딩했다. 이후, "e1"식별자를 갖고있는 EntityA의 프록시객체를 로딩하는 코드를 만들었다.

이때, entity2에 저장되는값은 프록시 객체가아닌, 실제 EntityA객체이다.

이유는 간단한데, 앞서 이미 EntityManager에 "e1"식별자를 갖고있는 EntityA클래스가 캐시되었으므로, 프록시 객체로 로딩할 이유가 없는것이다. 

지연로딩과 즉시로딩

이제, JPA에서 실제 지연로딩과 즉시로딩을 설정하는법을 알아보자.

  • 지연로딩 : 엔티티 조회시 연관된 엔티티를 같이 로딩하지않는다. 연관 엔티티는 실제 사용되는 시점에 로딩된다.
  • 즉시로딩 : 엔티티를 조회시 연관된 엔티티도 함께 로딩된다.

지연로딩과 즉시로딩은 FetchType의 EAGER, LAZY를 통해 설정할수있다. 아래 코드를 보자.

@Entity
@Table(name = "ENTITY_A")
public class EntityA{
    
    @Id
    @GeneratedValue
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ENTITY_B_ID")
    private EntityB entityB;
    
}

@Entity
@Table(name = "ENTITY_B")
public class EntityB{
    
    @Id
    @GeneratedValue
    @Column(name = "ENTITY_B_ID")
    private Long id;
    
}

위 코드를 보면, EntityA내부 @ManyToOne의 fetch전략이 LAZY로 설정되어있는것을 볼 수 있다.

즉시로딩은 마찬가지로 fetch전략을 EAGER로 설정한다.

 

JPA 조인 어노테이션의 기본 페치전략

@ManyToOne, @OneToOne : 즉시로딩

@OneToMany, @ManyToMany : 지연로딩

 

JPA는 최적화를 위해 ,어노테이션이 매핑된 필드가 자바 콜렉션(OneToMany, ManyToMany)라면 기본적으로 지연로딩을 사용하며, 아니라면 즉시로딩을 사용한다.

이유는 

영속성 전이

영속성 전이(Cascade)는 연관된 엔티티를 모두 영속성으로 만든다.

public void 영속성_예제(){
    EntityA a = new EntityA();
    em.persist(a);
    EntityB b = new EntityB();
    em.persist(b)
    a.addB(b);
    EntityC c = new EntityC();
    em.persist(c);
    a.addC(c);
}

JPA에서 엔티티를 저장하기 위해선 위 와 같은 코드를 작성해야했다. 코드도 더럽고 번거롭다.

 

JPA의 CASCADE를 사용하면, JPA가 연관된 엔티티를 자동으로 영속상태로 만든다.

@Entity
@Table(name = "ENTITY_A")
public class EntityA{
    
    @Id
    @GeneratedValue
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
    @JoinColumn(name = "ENTITY_B_ID")
    private EntityB entityB;
    
}

@Entity
@Table(name = "ENTITY_B")
public class EntityB{
    
    @Id
    @GeneratedValue
    @Column(name = "ENTITY_B_ID")
    private Long id;
    
}

@ManyToOne 어노테이션의 cascade가 추가되었다.

cascade전략이 PERSIST와 REMOVE로 설정되어있으므로, 엔티티가 삭제되거나 영속화 될때, 자동으로 연관 엔티티도 영속화 혹은 삭제 된다.

 

cascade의 전략 종류는 아래와 같다.

  • ALL
  • PERSIST
  • MERGE
  • REMOVE
  • REFRESH
  • DETACH

'JPA' 카테고리의 다른 글

[JPA] SpringDataJPA - OSIV  (0) 2021.12.05
[JPA] 고급 매핑 - 상속 관계, 복합키와 식별 관계  (0) 2021.10.17
[JPA] 연관관계 매핑  (0) 2021.10.14
[JPA] 필드와 컬럼 매핑  (0) 2021.10.12
[JPA] JPA 영속성 컨텍스트  (0) 2021.10.11