개발/Java & Kotlin

[JPA] Spring Data JPA에서 새로운 Entity인지 판단하는 방법 (2)

devhooney 2025. 7. 1. 08:44
728x90

기존 포스팅이 있지만 한번 더 작성해보았다.

 

[JPA] Spring Data JPA에서 새로운 Entity 판단하는 방법

 

[JPA] Spring Data JPA에서 새로운 Entity 판단하는 방법

Spring Data JPA에서 새로운 Entity를 저장할 때 public interface SaveTestRepository extends JpaRepository {} save @Transactional @Override public S save(S entity) { Assert.notNull(entity, "Entity must not be null."); if (entityInformation.isNew(e

devhooney.tistory.com

 

 

 

save()를 썼는데 insert가 안 돼요! update 된 것 같아요!
이거 왜 이런 걸까요?

Spring Data JPA를 쓰다 보면, 엔티티를 save() 했는데 INSERT가 아니라 UPDATE 되는 상황을 마주칠 수 있다.
그럴 땐 이렇게 질문이 들 수 있다.

“Spring Data JPA는 어떻게 새로운 엔티티인지 판단하는 거지?”

이번 포스팅에서는 그 기준과 해결 방법을 정리해보려 한다.

 

 

 


 

 

 

728x90

 

 

✅ 기본 규칙: @Id가 null이면 새로운 Entity


Spring Data JPA는 내부적으로 JPA의 EntityManager를 사용해 persist() 또는 merge()를 호출한다.

👉 동작 방식:
- @Id == null → persist() → INSERT
- @Id != null → merge() → UPDATE

즉, 엔티티의 ID 값이 null이면 새로운 객체(persist) 로 간주하고,
ID 값이 있으면 기존 객체(merge) 로 간주해서 update를 시도한다.

 

 

📌 예제

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

    private String name;
}
User u = new User();
u.setName("홍길동");

userRepository.save(u); // id가 null → INSERT

 

 


 

 

 

❗ 문제가 생기는 상황

예를 들어, ID가 자동 생성이 아닌 비즈니스 식별자일 경우엔 어떻게 될까?

@Entity
public class Product {
    @Id
    private String skuCode; // 비즈니스 키

    private String name;
}

 

이 경우 skuCode가 존재해도, 이게 새로운 건지 기존 건지 판단할 방법이 없다.
왜냐하면 null이 아니니까 Spring Data JPA는 무조건 merge(update) 를 시도한다.

그래서 save()를 호출했는데도 INSERT가 안 되고, UPDATE가 발생하는 상황이 생기게 된다.

 

 

 

 


 

✅ 해결책: Persistable<T> 인터페이스 사용하기

이런 상황에서는 Spring Data JPA가 엔티티를 저장할 때
"이 객체는 새로운 것이야" 라고 명확히 알려줄 필요가 있다.

그때 사용하는 게 바로 org.springframework.data.domain.Persistable<T> 인터페이스이다.

 

 

🧩 예제

@Entity
public class Product implements Persistable<String> {

    @Id
    private String skuCode;

    private String name;

    @Transient
    private boolean isNew = true;

    @Override
    public String getId() {
        return skuCode;
    }

    @Override
    public boolean isNew() {
        return isNew;
    }

    @PostLoad
    @PostPersist
    public void markNotNew() {
        this.isNew = false;
    }
}

 

✔️ 핵심 포인트
- isNew()가 true면 → persist() → INSERT
- false면 → merge() → UPDATE
- @PostLoad, @PostPersist를 통해 영속화되면 자동으로 isNew = false로 바꿈

 


 

🔍 save() 내부 동작이 궁금하다면?

Spring Data JPA의 SimpleJpaRepository는 내부에서 이렇게 판단한다.

public <S extends T> S save(S entity) {
    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}

 

여기서 entityInformation.isNew()가 바로 위에서 설명한 isNew()이다.

 

 


 

🧪 테스트 코드 예시

@Test
void insertOrUpdateTest() {
    Product p = new Product("ABC123", "신상 티셔츠");
    productRepository.save(p); // isNew=true → INSERT

    // 불러오고 다시 저장
    Product loaded = productRepository.findById("ABC123").get();
    productRepository.save(loaded); // isNew=false → UPDATE
}

 

 

728x90