본문 바로가기
Spring/JPA

JPA 효과적인 양방향 연관관계 구성방법

by 흑시바 2024. 10. 16.

양방향 @OneToMany 연관관계는 JPA(Java Persistence API)에서 부모 엔티티와 자식 엔티티 간의 일대다 관계를 설정할 때 사용된다. 이 관계를 효과적으로 구성하기 위해서는 여러 가지 고려사항이 있다.

1. 부모 측에서 전이 옵션을 사용해야 한다.

전이(cascading)는 항상 부모 엔티티가 자식 엔티티의 생명주기를 관리하도록 한다. 예를 들어, 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장되도록 하려면 CascadeType.ALL 또는 필요한 전이 타입을 설정해야 한다.

 

@Entity
public class Parent {
    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Child> children = new ArrayList<>();
}

2. 부모 측에 mappedBy를 지정한다.

부모 측에 설정된 mappedBy 속성으로 양방향 연관관계의 특성을 부여한다. 양방향 @OneToMany 연관관계에서 부모 측 @OneToMany에 mappedBy가 지정된다. mappedBy를 통해 양방향 @OneToMany가 자식 측 매핑을 미러링 한다는 표현을 한다.

 

@Entity
public class Child {
    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Parent parent;
}

3. 부모 측에 orphanRemoval을 지정한다.

orphanRemoval 속성을 true로 설정하면 부모 엔티티에서 자식 엔티티를 제거할 때 데이터베이스에서도 자식 엔티티가 삭제된다.

 

@Entity
public class Parent {
    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Child> children = new ArrayList<>();
}

4. 연관관계의 양측을 부모 측 도우미 메서드를 이용해 동기화 상태로 유지시킨다.

부모 엔티티와 자식 엔티티 간의 연관관계를 설정할 때, 도우미 메서드를 사용하여 양측의 상태를 동기화하는 것이 좋다.

 

public void addChild(Child child) {
    children.add(child);
    child.setParent(this);
}

public void removeChild(Child child) {
    children.remove(child);
    child.setParent(null);
}

5. equals()와 hashCode()를 오버라이딩한다.

엔티티의 equals()와 hashCode() 메서드를 오버라이딩하여 엔티티의 동일성을 정의한다. 주로 자동 생성된 데이터베이스  식별자 필드를 기준으로 오버라이딩한다.

 

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Parent parent = (Parent) o;
    return Objects.equals(id, parent.id);
}

@Override
public int hashCode() {
    return Objects.hash(id);
}

6. 연관관계 양측에서 지연 로딩을 사용한다.

지연 로딩(Lazy Loading)을 사용하면 실제로 필요한 시점에만 데이터를 로딩하여 성능을 최적화할 수 있다.

7. toString() 오버라이딩 방법에 주의한다.

toString() 메서드를 오버라이딩할 때 연관관계 필드를 포함하면 별도 SQL문 실행 또는 순환 참조 문제가 발생할 수 있다. 따라서 연관관계 필드를 제외하거나 적절히 처리해야 한다. 데이터베이스에 있는 기본 속성만 포함하는 게 좋다. Lombok 라이브러리 사용보다는 직접 정의하는 게 좋다.

 

@Override
public String toString() {
    return "Parent{" +
            "id=" + id +
            ", name='" + name + '\'' +
            '}';
}

8. @JoinColumn을 사용해 조인 컬럼을 명시적으로 지정한다.

@JoinColumn을 사용하여 외래 키 컬럼의 이름을 명시적으로 지정할 수 있다. 이는 데이터베이스 스키마를 명확하게 정의하는 데 도움이 된다.

 

@Entity
public class Child {
    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Parent parent;
}

9. Cascade.REMOVE 옵션을 통한 엔터티 삭제 처리에 주의한다.

여러 엔터티를 삭제하는 경우 다량의 SQL문이 발생할 수 있다. 한 번에 삭제가 가능한 벌크 처리를 사용하는 것이 좋다.

결론

@OneToMany 연관관계를 효과적으로 구성하기 위해서는 위에서 설명한 여러 가지 고려사항을 종합적으로 적용해야 한다. 이를 통해 데이터의 일관성을 유지하고 성능을 최적화할 수 있다. JPA를 사용한 엔티티 설계는 복잡할 수 있지만, 이러한 원칙을 잘 이해하고 적용하면 보다 안정적이고 효율적인 애플리케이션을 개발할 수 있다.

댓글