JPA Cascade Type : PERSIST vs MERGE (Why choose what?)

tony·2024년 2월 4일
0

판단여행기

목록 보기
1/6

Intro (Business Context)

주문에 대한 로직 수정 중, Detached Entity Passed to Persist를 만나보았다.

우리의 엔티티에 대해서 돌아보게 되었다.

그러던 중 우리가 왜 MERGE 대신 PERSIST 를 사용하게 되었는지 기억이 안 났다.

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "tb_order")
@Getter
@DynamicUpdate
@DynamicInsert
public class Order extends BaseEntity {
	,,,

    @JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
    private User buyer;

    @JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
    private User seller;
}

어떤 차이점이 있을까??

PERSIST VS MERGE

3줄 요약
1. 연관엔티티들이 하나의 트랜잭션 내에 있고 연관관계에 대해서 새로운 레코드 저장을 한다 => PERSIST
2. Detached Entity 이거나 연관관계에 대해서 수정을 해야한다 => MERGE
3. 동일한 엔티티에 대해 여러 엔티티가 PERSIST를 사용하고 있다면, 삭제를 의도하더라도 PERSIST가 걸려있기에 삭제가 되지 않는다.

3-line summary
1. related entities are in one transaction and a new record is to be saved for the relationship => PERSIST
2. it is a Detached Entity or the relationship needs to be modified => MERGE
3. If multiple entities are using PERSIST for the same entity, even if you want to delete it, it will not be deleted because PERSIST is in place.

PERSIST

Intention

The persist method is intended to add a new entity instance to the persistence context, i.e. transitioning an instance from a transient to persistent state.

Person person = new Person();
person.setName("John");
session.persist(person);

What happens after we call the persist method?
The person object has transitioned from a transient to persistent state.
The object is in the persistence context now, but not yet saved to the database.
The generation of INSERT statements will occur only upon committing the transaction, or flushing or closing the session.

Features

  • Notice that the persist method has a void return type. It operates on the passed object “in place,” changing its state. The person variable references the actual persisted object.
  • if an instance is already persistent, then this call has no effect for this particular instance (but it still cascades to its relations with cascade=PERSIST or cascade=ALL).
  • if an instance is detached, we’ll get an exception, either upon calling this method, or upon committing or flushing the session.

When to use

Use PERSIST when you are certain that the entity is transient and you want to persist it as a new record in the database.
Using PERSIST on an already managed entity will result in an exception.

MERGE

Intention

The main intention of the merge method is to update a persistent entity instance with new field values from a detached entity instance.

For updates, we need to get a persistent entity instance from a persistence context and update its fields with new values from this detached instance.

So the merge method does exactly that:

  • finds an entity instance by id taken from the passed object (either an existing entity instance from the persistence context is retrieved, or a new instance loaded from the database)
  • copies fields from the passed object to this instance
  • returns a newly updated instance
Person person = new Person(); 
person.setName("John"); 
session.save(person);

session.evict(person);
person.setName("Mary");

Person mergedPerson = (Person) session.merge(person);

Features

  • Note that the merge method returns an object.
    It’s the merged Person object we loaded into the persistence context and updated, not the person object that we passed as an argument.
    They’re two different objects, and we usually need to discard the person object.
  • if the entity is detached, it copies upon an existing persistent entity.
  • if the entity is transient, it copies upon a newly created persistent entity.
    this operation cascades for all relations with cascade=MERGE or cascade=ALL mapping.
  • if the entity is persistent, then this method call doesn’t have an effect on it (but the cascading still takes place).

When to use

Use MERGE when you have a detached entity that you want to reattach to the current persistence context.
MERGE will copy the state of the detached entity to a managed entity (either an existing managed entity or a new one if none exists) and return the managed entity.

So Why did we use PERSIST??

우리가 PERSIST 를 사용하게 된 이유는 간단하다. Order 에 대한 User는 수정/삭제될 일이 거의 없기 때문이다.

이 때, 다음 의문점이 들 수 있다. 하나씩 짚어보도록 하자.

1. Order 를 취소하고 싶다면 삭제처리를 해야하지 않나??

맞다. Order 를 취소하고 싶다면 삭제처리를 해야한다. 다만 우리는 Soft Delete 를 지원하고 있으므로, Order 데이터 자체를 삭제하지 않는다.

다만 soft delete 플래그를 변경해줌으로써 Soft Delete 처리를 한다. 따라서 User의 연관관계와는 무관한 삭제처리를 한다.

2. User에 대한 PERSIST를 사용하게 되면 다른 엔티티에서 User를 PERSIST로 걸게 되는 경우 삭제가 불가능하지 않나?

앞서 강조했듯이, User 데이터에 대한 삭제처리를 하지 않아 해당 부분은 걱정할 필요가 없게 되었다.

3. PERSIST 를 사용하게 되었을 때의 이점은??

이는 명확하다.

MERGE 가 상대적으로 성능이 떨어지기 때문이다.

앞서 봤듯이, MERGE는 모든 필드값들을 복사하여 새로운 인스턴스를 생성해낸다.

정말 업데이트의 목적을 위한 전략인 것이다.

하지만 우리는 User를 새로 추가할 뿐, 수정할 일이 없다.
(주문건에 대해서 판매자와 구매자를 변경할 일이 없기 때문)

따라서 우리의 비즈니스 컨텍스트에는 부합하지 않는다.

Reference

Hibernate: save, persist, update, merge, saveOrUpdate
[jpa] CascadeType.PERSIST를 함부로 사용하면 안되는 이유

profile
내 코드로 세상이 더 나은 방향으로 나아갈 수 있기를

0개의 댓글