FireDrago

[JPA] 연관관계 관리 본문

프로그래밍/JPA

[JPA] 연관관계 관리

화이용 2024. 4. 1. 12:52

JPA 엔티티는 다양한 관계를 형성할 수 있다. 

1:N 관계는 부서와 직원처럼 한쪽 엔티티가 여러 개의 다른 엔티티를 가질 때 사용된다. 

1:1 관계는 사용자와 프로필처럼 한쪽 엔티티가 하나의 다른 엔티티만 가질 때 사용되며, 

N:M 관계는 프로젝트와 참여 직원처럼 여러 개의 엔티티가 서로 연결될 때 사용된다.

이러한 관계는 객체 간의 참조를 통해 구현되고, JPA는 이 참조관계를 효율적으로 관리하기 위한 다양한 기능을 제공한다. 이번에는 JPA의 지연 로딩, 영속성 관리, 고아 객체 등 주요 기능들을 살펴보며 엔티티 관계 관리에 대해 알아보자

 

 

지연로딩

 

 

위 코드를 보자 Child는 Parent 참조와 연관관계를 맺고 있다. 

Child 엔티티를 불러올때는 이렇게 코드를 작성해야 할 것이다.

Child child = new Child();
// child 따로 영속성 관리
em.persist(child);

Parent parent = new Parent();
parent.addChild(child);
// Parent 따로 영속성 관리
em.persist(parent);

em.flush();
em.clear();

System.out.println("================");
Child findChild = em.find(Child.class, child.getId());
System.out.println("================");
// 트랜잭션 커밋
tx.commit();

 

 

================

Hibernate: 
    select
        c1_0.id,
        c1_0.name,
        p1_0.id,
        p1_0.name 
    from
        Child c1_0 
    left join
        Parent p1_0 
            on p1_0.id=c1_0.parent_id 
    where
        c1_0.id=?
================

 

이렇게 Parent 와 join 된 형태로 Select 문이 작성되는데,

연관관계 갯수만큼의 조인이 추가된다. 너무 복잡한 쿼리가 생성될 수 있는것이다.

 

이를 해결하기위해 JPA는 지연로딩 기능을 제공한다.

// 지연로딩 
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private Parent parent;

 Child 엔티티의 @ManyToOne 어노테이션에 (fetch = FectType.LAZY) 로 설정한다. 

================
Hibernate: 
    select
        c1_0.id,
        c1_0.name,
        c1_0.parent_id 
    from
        Child c1_0 
    where
        c1_0.id=?
================

조인 없는 깔끔한 쿼리가 작성된다. 만약 Parent를 호출하면, 그때서야 Parent를 DB에서 호출하는 쿼리가 실행된다.

이게 어떻게 가능한 것일까? 지연로딩 기능은 내부에 프록시 객체를 등록한다.

 

프록시 객체는 원래 객체를 상속받고, 내부에 원객체의 참조를 가진다. 

외부에서 원객체를 호출하게 되면, JPA는 그때 DB에서 조회하여 객체를 초기화한다.

외부에서 객체가 호출되지 않는다면, 프록시 객체를 등록하여 복잡한 조인 쿼리가 작성되는것을 방지할 수 있다.

 

※ 주의

실무에서 반드시 지연로딩 사용할것 (@ManyToOne, @OneToOne 기본값이 즉시로딩 반드시 지연로딩설정)

 

 

영속성 전이

 

연관관계에 따라 영속성 상태변화를 자동으로 전달하는 기능

 @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    private List<Child> childList = new ArrayList<>();

--------------------------------------------------------------
Child child = new Child();

Parent parent = new Parent();
parent.addChild(child);
// Cascade 설정으로 Parent 호출, Child 영속성 등록 자동
em.persist(parent);

위 예시처럼 Parent 클래스의 childList에 cascade = CascadeType.All 설정을 하면

Child 객체는 별도의 persist() 호출없어도 Parent 가 persist() 호출 될때 같이 영속성 관리상태가 된다.

 

ALL 모든 영속성 상태변화 전달 (모든 변화 )
PERSIST 영속성 등록 상태변화 전달 (Parent 등록시 Child 자동 등록)
REMOVE 영속성 등록 삭제 전달 (Parent 삭제시 Child 자동삭제)

 

 

고아객체

부모 객체와 연관관계가 끊어진 엔티티를 자동삭제하는 기능

// 고아객체 삭제 설정
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> childList = new ArrayList<>();

-------------------------------------------------------------
 Child child = new Child();

Parent parent = new Parent();
parent.addChild(child);
// Cascade 설정으로 Parent 호출, Child 영속성 등록 자동
em.persist(parent);

Parent findParent = em.find(Parent.class, parent.getId());
// 연관관계 삭제시 DELETE 쿼리가 실행된다. 
findParent.getChildList().remove(0);

Hibernate: 
    /* delete for hellojpa.jpabook.jpashop.domain.Child */delete 
    from
        Child 
    where
        id=?

 

삭제된 childList 가 삭제되는 delete 쿼리가 작성된다.

고아 객체 제거 기능을 활성화 하면, 부모를 제거할 때 자식도 함께
제거된다. 이것은 CascadeType.REMOVE처럼 동작한다.

※ 주의

참조하는 곳이 하나일때만 사용해야한다. (여러 참조가 있다면, 다른 연관관계를 삭제해버린다.)

특정 엔티티가 개인소유하는 경우에 사용하자

'프로그래밍 > JPA' 카테고리의 다른 글

[JPA] @PersistenceContext 사용해야 하는 이유  (0) 2024.04.08
[JPA] JPQL 기본문법 2  (0) 2024.04.06
[JPA] JPQL 기본문법 1  (0) 2024.04.04
[JPA] 엔티티 매핑  (0) 2024.03.27
[JPA] 영속성 관리  (0) 2024.03.26