티스토리 뷰

프록시란?

  • 엔티티가 실제 사용될 때까지 데이터베이스 조회를 지연하는 방법을 제공하는데 이것을 지연 로딩이라 한다.
  • 지연 로딩 기능을 사용하기 위해 실제 엔티티 객체 대신에 데이터베이스 조회를 지연할 수 있는 가짜 객체가 필요한데 이것을 프록시 객체라 한다.

 

프록시 사용

  • 엔티티를 실제 사용하는 시점까지 데이터베이스 조회를 미루고 싶으면 EntityManager.getReference() 메소드를 사용하면 된다.
Member member = em.getReference(Member.class, "Member1");

 

  • PersistenceUnitUtil.isLoaded(Object entity) 메소드를 사용하면 프록시 인스턴스의 초기화 여부를 확인할 수 있다.
boolean isLoad = em.getEntityManagerFactory().getPersistenceUnitUtil().isLoaded(entity);
//또는 boolean isLoad = emf.getPersistenceUnitUtil().isLoaded(entity);
System.out.println("isLoaded = " + isLoad);  //초기화 여부 확인

 

  • 조회한 엔티티가 진짜 엔티티인지 프록시로 조회한 것인지 확인하려면 클래스명을 직접 출력해보면 된다.
System.out.println("memberProxy = " + member.getClass().getName());
//결과: memberProxy = jpabook.domain.Member_$$_javassist_0
//클래스 명 뒤에 ..javassist..라 되어 있는데 이것으로 프록시인 것을 확인할 수 있다.

 

프록시 특징

  • 프록시 객체는 실제 객체에 대한 참조(target)를 보관한다. 그리고 프록시 객체의 메소드를 호출하면 프록시 객체는 실제 객체의 메소드를 호출한다.

 

  • 프록시 객체는 member.getName() 처럼 실제 사용될 때 데이터베이스를 조회해서 실제 엔티티 객체를 생성하는데 이것을 프록시 객체의 초기화라 한다.

  • 프록시 객체를 초기화한다고 프록시 객체가 실제 엔티티로 바뀌는 것은 아니다. 프록시 객체가 초기화되면 프록시 객체를 통해서 실제 엔티티에 접근할 수 있다.
  • 초기화는 영속성 컨텍스트의 도움을 받아야 가능하다. 따라서 준영속성 상태의 프록시를 초기화하면 문제가 발생한다. (org.hibernate.LazyInitializationException 예외 발생)
  • 프록시 객체는 원본 엔티티를 상속받은 객체이므로 타입 체크 시에 주의해서 사용해야 한다.
  • 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 데이터베이스를 조회할 필요가 없으므로 em.getReference()를 호출해도 프록시가 아닌 실제 엔티티를 반환한다.

 

즉시 로딩

엔티티를 조회할 때 연관된 엔티티도 함께 조회한다.

@Entity
public class Member {
    ...
    @ManyToOne(fetch = FetchType.EAGER)  //fetch 속성을 EAGER로 지정
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    ...
}
-- 즉시 로딩 실행 SQL
SELECT
    M.MEMBER_ID AS MEMBER_ID,
    M.TEAM_ID AS TEAM_ID,
    M.USERNAME AS USERNAME,
    T.TEAM_ID AS TEAM_ID,
    T.NAME AS NAME
FROM
    MEMBER M LEFT OUTER JOIN TEAM T
    ON M.TEAM_ID=T.TEAM.ID
WHERE
    M.MEMBER_ID='member1'

 

지연 로딩

연관된 엔티티를 실제 사용할 때 조회한다.

@Entity
public class Member {
    ...
    @ManyToOne(fetch = FetchType.LAZY)  //fetch 속성을 LAZY로 지정
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    ...
}
-- em.find(Member.class, "member1") 호출 시 실행되는 SQL
SELECT * FROM MEMBER
WHERE MEMBER_ID = 'member1'
-- team.getName() 호출 시 실행되는 SQL
SELECT * FROM TEAM
WHERE TEAM_ID = 'team1'

 

영속성 전이: CASCADE

특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶으면 영속성 전이 기능을 사용한다.

 

CASCADE의 종류

  • ALL: 모두 적용
  • PERSIST: 영속
  • MERGE: 병합
  • REMOVE: 삭제
  • REFRESH: REFRESH
  • DETACH: DETACH

 

CASCADE 활성화

@Entity
public class Parent {
    
    @Id @GeneratedValue
    private Long id;
    
    @OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
    private List<Child> children = new ArrayList<Child>();
    ...
}
@Emtity
public class Child {
    
    @Id @GeneratedValue
    private Long id;
    
    @ManyToOne
    private Parent parent;
    ...
}

 

CASCADE 저장

부모만 영속화하면 CascadeType.PERSIST로 설정한 자식 엔티티까지 함께 영속화해서 저장한다.

private static void saveWithCascade(EntityManager em){
    
    Child child1 = new Child();
    Child child2 = new Child();
    
    Parent parent = new Parent();
    child1.setParent(parent);  //연관관계 추가
    child2.setParent(parent);  //연관관계 추가
    parent.getChildren().add(child1);
    parent.getChildren().add(child2);
    
    //부모 저장, 연관된 자식들 저장
    em.persist(parent);
}

 

CASCADE 삭제

연관된 자식 엔티티도 함께 삭제된다.

Parent findParent = em.find(Parent.class, 1L);
em.remove(findParent);

 

고아 객체 제거

  • 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제한다.
  • 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 보고 삭제한다.
  • 참조하는 곳이 하나일 때만 사용한다. (예: @OneToOne, @OneToMany)
  • 부모를 제거하면 자식도 같이 제거된다. 이것은 CascadeType.REMOVE를 설정한 것과 같다.

 

고아 객체 제거 기능 설정 및 사용

@Entity
public class Parent {
   
   @Id @GeneratedValue
   private Long id;
   
   @OneToMany(mappedBy = "parent", orphanRemoval = true)
   private List<Child> children = new ArrayList<Child>();
   ...
}
Parent parent1 = em.find(Parent.class, id);
parent1.getChildren().remove(0);
//컬렉션에서 첫번째 자식을 제거하면 orphanRemoval = true에 의해서 데이터베이스의 데이터도 삭제된다.

 

 

참고

김영한, 『자바 ORM 표준 JPA 프로그래밍』, 에이콘출판주식회사(2015)

댓글
Total
Today
Yesterday
링크
Apple 2023 맥북 프로 14 M3, 스페이스 그레이, M3 8코어, 10코어 GPU, 512GB, 8GB, 한글