본문 바로가기
Java/ORM

JAVA ORM - JPA(영속성 전이, 임베디드타입, 값타입 컬렉션)

by 티코딩 2024. 8. 7.

엔티티하나를 영속상태로 만들 때 연관된 엔티티도 같이 영속상태로 만드는 것.

@OneToMany(mappedBy="parent", cacade=CascadeType.PERSIST)

이런식으로 cascade 옵션을 사용해서 같이 엮을 수 있다.

 

 

cascade옵션

  1. CascadeType.PERSIST
    • 부모 엔티티가 persist() 메서드에 의해 저장될 때, 관련된 자식 엔티티도 함께 저장.
  2. CascadeType.MERGE
    • 부모 엔티티가 merge() 메서드에 의해 병합될 때, 관련된 자식 엔티티도 함께 병합.
  3. CascadeType.REMOVE
    • 부모 엔티티가 삭제될 때, 관련된 자식 엔티티도 함께 삭제.
  4. CascadeType.REFRESH
    • 부모 엔티티가 새로고침될 때, 관련된 자식 엔티티도 함께 새로고침.
  5. CascadeType.DETACH
    • 부모 엔티티가 영속성 컨텍스트에서 분리될 때, 관련된 자식 엔티티도 함께 분리.
  6. CascadeType.ALL
    • 위의 모든 영속성 전이 옵션을 포함한다. 즉, PERSIST, MERGE, REMOVE, REFRESH, DETACH 모두 적용됨.
    • Child(다) , Parent(일) 관계에서 Parent에 
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> childList = new ArrayList<>();

이렇게 붙혀주면 아래와같이 부모하나를 persist 해줘도 cascade로 엮여있는 child까지 persist 된다.

try{
            Child child1 = new Child();
            Child child2 = new Child();

            Parent parent = new Parent();
            parent.addChild(child1);
            parent.addChild(child2);

            em.persist(parent);

            tx.commit();
            }

그런데 여기서 Child와 또다른 엔티티가 관계를 맺고있다면 cascade를 쓰면 안된다.  

영속성 전이를 사용하는 이유

1. 코드 단순화 : 부모엔티티의 상태 변화에 따라 자동으로 자식엔티티까지 관리되어 코드관리가 간편해진다.

2. 일관성 유지 : 부모-자식 관계의 일관성이 생겨 데이터무결성이 지켜진다.

3. 유지보수가 쉬워짐 : 엔티티간의 상태전이를 명시적으로 선언해 가독성, 유지보수가 쉬워진다.

 

 

자바의 기본타입

int a = 10;
int b = a;

b = 20;

int, float, char,같은 기본타입은 위와같이 저장소를 공유하지않고, a와 b는 다른 저장공간을 가지는 객체가 된다.

 

Integer, String같은 클래스는

Integer a = new Integer(10);
Integer b = a;

 

이렇게 하면 a와 b는 같은 저장소를 공유하는 같은 객체가된다.

 

임베디드 타입

새로운 값 타입을 정의할 수 있다. JPA는 임베디드 타입. int, String과같은 값타입.

@Embeddable 클래스 정의

@Embeddable @Getter @Setter @AllArgsConstructor
public class Address {
    private String street;
    private String city;
    private String state;
    private String zipCode;
	. . . 
}

@Embedded  사용

@Entity @Getter @Setter @AllArgsConstructor
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @Embedded
    private Address address;
	...
}

 

이렇게하면 Customer 엔티티가 Address엔티티를 가져 Address의 속성을 포함하게된다.

@AttributeOverride 를사용해 컬럼이름을 설정할수도 있다.

@Entity
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "street", column = @Column(name = "home_street")),
        @AttributeOverride(name = "city", column = @Column(name = "home_city")),
        @AttributeOverride(name = "state", column = @Column(name = "home_state")),
        @AttributeOverride(name = "zipCode", column = @Column(name = "home_zip"))
    })
    private Address address;
	...

 

임베디드 타입(Address)은 Customer의 일부로 간주되어 DB에서 동일한 테이블에 속성이 매핑된다.

Address같이 공통으로 사용되는 속성들을 임베디드 타입으로 정의해 재사용도 가능하다. 임베디드 타입은 하나의 엔티티가 아니고 항상 다른 엔티티에 포함되어있어야 한다.

그런데 여기서 의문. @MappedSuperclass로 만든 BaseEntity와는 어떻게 다른걸까?

@MappedSuperclass
@Getter
@Setter
public class BaseEntity {
    private String createdBy;
    private LocalDateTime createdDate;
    private String lastModifiedBy;
    private LocalDateTime lastModifiedDate;
}

BaseEntity는 여러 클래스에서 사용되는 공통적인 속성들을 모아놓은 엔티티로 DB에 독립적으로 존재하지않는다.

그리고 상속을 통해서 속성들을 공유한다.

 

그에반해 Embeddable로 구현한 엔티티는 속성을 객체로 구성하기 위한 기능을 제공한다.

이런 차이가 존재한다. @MappedSuperclass 엔티티는 여러엔티티가 공유하는게좋지만, @Embeddable는 여러 엔티티가 공유하면 안된다.

 

값타입 컬렉션

엔티티 필드에 여러개의 값타입 객체를 저장할 수 있는 기능. 

@Embeddable
public class Address {
    private String city;
    private String street;
    private String zipCode;
	...
}

@Entity
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ElementCollection
    @CollectionTable(name = "customer_address", joinColumns = @JoinColumn(name = "customer_id"))
    private List<Address> addresses = new ArrayList<>();
	...
}

 

  • 데이터 변경 시 주의
    • 값 타입 컬렉션은 변경 시 전체를 대체하는 방식으로 관리되므로, 성능에 주의해야 한다.
    • 값타입은 엔티티와 달리 식별자가 없고, 변경하면 추적이어렵다. 변경사항이 발생하면 주인엔티티와 연관된 모든 데이터를 삭제하고 값 타입 컬렉션에 있는 현재값을 모두저장한다.
  • 데이터베이스 매핑
    • 값 타입 컬렉션은 별도의 테이블로 관리되며, 이는 주 테이블과 조인하여 관리됨. 따라서 적절한 인덱스 설정과 데이터 모델링이 필요하다.
  • 고유성 및 정렬
    • @OrderColumn과 @OrderBy 애너테이션을 사용하여 컬렉션의 순서를 관리하거나, 중복 제거 기능을 추가할 수 있다.

 

 

'Java > ORM' 카테고리의 다른 글

JAVA ORM - JPA(Fetch Join)  (0) 2024.08.12
JAVA ORM - JPA(JPQL)  (0) 2024.08.08
JAVA ORM - JPA(proxy, loading)  (0) 2024.08.06
JAVA ORM - JPA(상속을 매핑?)  (0) 2024.08.05
JAVA ORM - JPA(연관관계 매핑2)  (0) 2024.08.03