티스토리 뷰

728x90
반응형

목차

     

    Spring Data JPA는 데이터를 다루는 데 있어 강력한 도구다. 

     

    그중에서도 새로운 Entity인지 여부를 판단하는 기능은 데이터 처리 효율성과 무결성을 보장하는 데 중요한 역할을 한다. 

     

    이번 글에서는 Spring Data JPA가 새로운 Entity를 어떻게 식별하는지, 

     

    그리고 이를 구현하기 위해 필요한 핵심 원리를 간단히 정리해 보자.

     

    선 요약

     

    • 새로운 Entity 식별 기준

      • 기본적으로 JpaEntityInformation 인터페이스의 isNew() 메서드로 판단.
      • @Version 필드와 @Id 필드의 값으로 새 객체 여부를 결정.
    • @Version 필드의 역할

      • 필드 없음: 기본 로직으로 판단.
      • Wrapper 타입: null 여부 확인.
      • Primitive 타입: 기본값 여부 확인.
    • @Id 필드와 키 생성 전략

      • @GeneratedValue 사용 시 ID가 없는 상태면 새 객체로 간주.
      • 직접 ID를 할당하면 새 객체로 간주되지 않음.
    • 직접 ID 할당 문제

      • ID가 있으면 merge를 수행, 비효율 발생 가능.
      • Persistable 인터페이스를 구현해 해결 가능.
    • isNew() 메서드 활용

      • SimpleJpaRepository의 save()에서 persist와 merge 작업을 결정.

     

    새로운 Entity를 식별하는 원리

     

    Spring Data JPA는 기본적으로 JpaEntityInformation 인터페이스를 통해 새로운 Entity 여부를 판단한다. 

     

    이 인터페이스는 Entity의 메타데이터를 분석하며, 구현체로는 주로 JpaMetamodelEntityInformation 클래스가 동작한다.

     

    @Version 필드의 역할

     

    Entity 클래스에 @Version 어노테이션이 적용된 필드는 새로운 Entity인지 판단하는 데 중요한 기준이 된다.

    • 필드가 없는 경우: AbstractEntityInformation의 기본 로직이 동작한다.
    • Wrapper 타입인 경우: null 여부를 확인한다.
    • Primitive 타입인 경우: 기본값인지 여부를 확인한다.
    @Override
    public boolean isNew(T entity) {
        if (versionAttribute.isEmpty() || 
            versionAttribute.map(Attribute::getJavaType).map(Class::isPrimitive).orElse(false)) {
            return super.isNew(entity);
        }
    
        BeanWrapper wrapper = new DirectFieldAccessFallbackBeanWrapper(entity);
    
        return versionAttribute.map(it -> wrapper.getPropertyValue(it.getName()) == null).orElse(true);
    }

     

    @Id 필드와 키 생성 전략

     

    @Id 필드의 값도 새로운 Entity 판단 기준에 포함된다.

    • GeneratedValue 어노테이션 사용 - 데이터베이스에 저장되기 전에는 ID가 비어 있으므로, 새 Entity로 간주된다.
    • 직접 ID 할당 - ID가 비어 있지 않으므로 새 Entity로 간주되지 않는다.
    @Override
    public boolean isNew(T entity) {
        Id id = getId(entity);
        Class<ID> idType = getIdType();
    
        if (!idType.isPrimitive()) {
            return id == null;
        }
    
        if (id instanceof Number) {
            return ((Number) id).longValue() == 0L;
        }
    
        throw new IllegalArgumentException(String.format("Unsupported primitive id type %s", idType));
    }

     

     

    직접 ID 할당 시 발생하는 문제와 해결 방법

     

    키 생성 전략을 사용하지 않고 ID를 직접 할당하면 

     

    JpaMetamodelEntityInformation이 Entity를 새로운 객체로 간주하지 않는다. 

     

    이 경우, Spring Data JPA는 DB를 조회 후 merge를 수행하며, 이는 불필요한 성능 저하를 초래할 수 있다.

     

    해결책: Persistable 인터페이스 구현

     

    직접 ID를 할당해야 하는 경우, 

     

    Persistable 인터페이스를 구현하면 JpaPersistableEntityInformation이 동작하도록 설정할 수 있다. 

     

    이를 통해 효율적으로 새로운 Entity를 식별할 수 있다.

    public class MyEntity implements Persistable<Long> {
    
        private Long id;
    
        @Override
        public Long getId() {
            return id;
        }
    
        @Override
        public boolean isNew() {
            return id == null;
        }
    }

     

     

    isNew() 메서드의 실제 사용 예시

     

    Spring Data JPA의 SimpleJpaRepository 클래스는 

     

    새로운 Entity인지 여부를 판단하여 persist 또는 merge 작업을 결정한다. 

     

    이때, isNew() 메서드가 호출된다.

    @Override
    @Transactional
    public <S extends T> S save(S entity) {
        Assert.notNull(entity, "Entity must not be null");
    
        if (entityInformation.isNew(entity)) {
            entityManager.persist(entity);
            return entity;
        } else {
            return entityManager.merge(entity);
        }
    }
    • isNew()가 true를 반환하면 persist 실행.
    • 그렇지 않으면 merge 실행.

     

    왜 중요한가?

     

    새로운 Entity를 정확히 판단하는 것은 데이터 처리에서 불필요한 DB 조회를 방지하고, 성능과 데이터 무결성을 보장한다. 

     

    특히, ID를 직접 할당하거나 외부 시스템과의 연동이 필요한 복잡한 환경에서는 이를 명확히 이해하고 구현하는 것이 필수적이다.

     

    결론

     

    Spring Data JPA가 새로운 Entity를 판단하는 원리를 이해하면, 데이터 저장 및 갱신 작업을 보다 효율적으로 처리할 수 있다. 

     

    솔직히 쓰기만 했지 뒤에서 어떤 일들이 벌어지는지는 궁금하지도 않았는데,

     

    한 번 정리를 하고 나니 조금은 좋은 개발자가 된 것 같은 기분이 든다.

     

    파고들면 끝이 없구나..

     

    이번 글은 끝!

    반응형
    댓글
    공지사항
    최근에 올라온 글
    최근에 달린 댓글
    Total
    Today
    Yesterday
    링크
    «   2025/01   »
    1 2 3 4
    5 6 7 8 9 10 11
    12 13 14 15 16 17 18
    19 20 21 22 23 24 25
    26 27 28 29 30 31
    글 보관함