개발/java

Spring Data JPA 관계 매핑하기

코딩하는꽃개 2023. 5. 20. 23:33
반응형

JPA를 이용하기 위해서는 도메인 영역에 해당하는 엔티티를 정의해야 합니다.

이 엔티티는 테이블에 종속될 수도 혹은 분리해서 정의할 수도 있습니다. 

엔티티 활용법에 대해서는 다음에 다시 알아보고 이번에는 관계 매핑에 대해서 알아보겠습니다.

 

JPA의 관계 매핑은 아래와 같은 종류가 있습니다.

  • ManyToOne
  • OneToMany
  • OneToOne

또한 관계 매핑된 엔티티들은 상호 관계를 모두 설정해 주어야 합니다.

예를 들어 A와 B가 ManyToOne이면 B와 A는 OneToMany 이므로 추가로 설정해 주는 방식입니다.

 

각 관계들의 설정 방법을 한눈에 파악할 수 있도록 예제를 만들었습니다.

@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "resource")
public class ResourceEntity {
    // 리소스 ID
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "n4_id")
    private Integer id;

    // 리소스 이름
    @Column(name = "str_resource_name")
    private String resourceName;

    // UUID
    @Column(name = "str_uuid")
    private String uuid;

    // 생성일
    @CreationTimestamp
    @Column(name = "dt_created_date")
    private LocalDateTime createdDate;

    // 변경일
    @UpdateTimestamp
    @Column(name = "dt_updated_date")
    private LocalDateTime updatedDate;

    // 서브 리소스 01
    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "n4_resource_id", referencedColumnName = "n4_id")
    private SubResource01Entity subResource01Entity;

    // 서브 리소스 02
    @OneToMany(mappedBy = "resourceEntity", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Collection<SubResource02Entity> subResource02Entities;

    // 서브 리소스 03
    @OneToOne(mappedBy = "resourceEntity", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private SubResource03Entity subResource03Entity;
}
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "sub_resource_01")
public class SubResource01Entity {
    // 리소스 ID
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "n4_id")
    private Integer id;

    @Column(name = "n4_resource_id", insertable = false, updatable = false)
    private Integer resourceId;

    // 리소스 이름
    @Column(name = "str_resource_name")
    private String resourceName;

    // 생성일
    @CreationTimestamp
    @Column(name = "dt_created_date")
    private LocalDateTime createdDate;

    // 변경일
    @UpdateTimestamp
    @Column(name = "dt_updated_date")
    private LocalDateTime updatedDate;

    // 리소스
    @OneToMany(mappedBy = "subResource01Entity", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Collection<ResourceEntity> resourceEntities;
}
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "sub_resource_02")
public class SubResource02Entity {
    // 리소스 ID
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "n4_id")
    private Integer id;

    @Column(name = "n4_resource_id", insertable = false, updatable = false)
    private Integer resourceId;

    // 리소스 이름
    @Column(name = "str_resource_name")
    private String resourceName;

    // 생성일
    @CreationTimestamp
    @Column(name = "dt_created_date")
    private LocalDateTime createdDate;

    // 변경일
    @UpdateTimestamp
    @Column(name = "dt_updated_date")
    private LocalDateTime updatedDate;

    // 리소스
    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "n4_resource_id", referencedColumnName = "n4_id")
    private ResourceEntity resourceEntity;
}
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "sub_resource_03")
public class SubResource03Entity {
    // 리소스 ID
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "n4_id")
    private Integer id;

    @Column(name = "n4_resource_id", insertable = false, updatable = false)
    private Integer resourceId;

    // 리소스 이름
    @Column(name = "str_resource_name")
    private String resourceName;

    // 생성일
    @CreationTimestamp
    @Column(name = "dt_created_date")
    private LocalDateTime createdDate;

    // 변경일
    @UpdateTimestamp
    @Column(name = "dt_updated_date")
    private LocalDateTime updatedDate;

    // 리소스
    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private ResourceEntity resourceEntity;
}

 

 

관계 매핑시 cascade 옵션을 지정할 수 있는데 옵션은 아래와 같습니다.

  1. CascadeType.ALL : 모든 작업을 전파합니다. 부모 엔티티에 대해 수행된 작업이 자식 엔티티에도 모두 적용됩니다. (예: 부모 엔티티가 삭제되면 자식 엔티티도 함께 삭제됩니다.)
  2. CascadeType.PERSIST: 영속화 작업을 전파합니다. 부모 엔티티가 영속화될 때 자식 엔티티도 함께 영속화됩니다.
  3. CascadeType.MERGE: 병합 작업을 전파합니다. 부모 엔티티의 변경 내용이 병합될 때 자식 엔티티도 함께 병합됩니다.
  4. CascadeType.REMOVE: 삭제 작업을 전파합니다. 부모 엔티티가 삭제될 때 자식 엔티티도 함께 삭제됩니다.
  5. CascadeType.REFRESH: 새로고침 작업을 전파합니다. 부모 엔티티가 새로고침될 때 자식 엔티티도 함께 새로고침됩니다.
  6. CascadeType.DETACH: 분리 작업을 전파합니다. 부모 엔티티가 분리될 때 자식 엔티티도 함께 분리됩니다.

 

 

Fetch Type은 JPA에서 엔티티 간 연관 관계에서 데이터를 가져오는 방식을 지정하는 옵션입니다. Fetch Type은 주로 연관된 엔티티의 로딩 방식을 결정하는 데 사용됩니다.

 

JPA에서는 두 가지 Fetch Type을 제공합니다:

  1. FetchType.EAGER: EAGER 타입은 연관 엔티티를 즉시 로딩하는 방식입니다. 부모 엔티티를 조회할 때 연관된 모든 엔티티도 함께 로딩됩니다. EAGER 타입을 사용하면 연관 엔티티의 데이터를 사용할 수 있지만, 불필요한 데이터를 로딩하거나 성능에 영향을 줄 수 있으므로 신중하게 사용해야 합니다.
  2. FetchType.LAZY: LAZY 타입은 연관 엔티티를 지연 로딩하는 방식입니다. 부모 엔티티를 조회할 때 연관 엔티티는 로딩되지 않고, 실제로 해당 엔티티를 사용하는 시점에서 로딩됩니다. LAZY 타입은 연관 엔티티의 데이터가 필요한 경우에만 로딩되므로 성능에 유리합니다. 또한, 연관 엔티티가 큰 경우나 여러 개의 연관 엔티티가 있는 경우에 유용하게 사용될 수 있습니다.

Fetch Type은 @ManyToOne, @OneToOne 관계에서 기본적으로 LAZY로 설정되며, @OneToMany, @ManyToMany 관계에서는 기본적으로 LAZY로 설정됩니다. FetchType을 명시적으로 지정하지 않으면 기본 Fetch Type이 적용됩니다.

Fetch Type을 설정할 때에는 실제 사용 사례와 성능 요구사항을 고려해야 합니다. EAGER 타입은 필요한 경우에만 사용하고, 대부분의 경우에는 LAZY 타입을 사용하여 필요한 데이터만 로딩하는 것이 권장됩니다.

 

 

 

외래키에 해당하는 컬럼은 insertable = false, updatable = false 옵션을 넣어주어야 합니다.

 

또한 관계가 매핑된 엔티티를 외래키 컬럼의 경우 데이터 커밋 전까지는 PK가 생성되지 않으므로 외래키 컬럼이 아닌 관

계 매핑된 변수를 설정해 주어야 합니다.

 

@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "resource")
public class ResourceEntity {
    ...(중략)
    
    /**
     * 서브 리소스 03 설정
     * @param resourceDTO 리소스 정보
     */
    public void setSubResource03(ResourceDTO resourceDTO) {
        // 서브 리소스 03 정보 설정
        SubResource03Entity subResource03Entity = SubResource03Entity.builder()
                .resourceName(resourceDTO.getResourceName())
                .resourceEntity(this) // 서버 리소스 엔티티 03에 리소스 엔티티 를 설정
                .build();

        this.subResource03Entity = subResource03Entity; // 서버 리소스 엔티티에 서버 리소스 엔티티 03을 설정
    }
}
반응형