Spring Data Cassandra을 사용하면서, @EnableCassandraAuditing를 적용해도 @CreatedDate 필드에 값이 들어가지 않고 null이 저장되는 문제가 발생했습니다. 이 글에서는 문제 상황 및 원인을 분석하고 해결합니다.
문제 상황: @EnableCassandraAuditing를 적용해도 null이 들어간다.
감사 레코드를 삽입하기 위해 다음과 같이 BaseEntity를 구현했다.
이를 Cassandra DB 테이블에 매핑할 클래스에 상속받았다.
이후 Auditing을 활성화하기 위해 @EnableCassandraAuditing을 적용했다.
또한 @CreatedBy, @LastModifiedBy를 사용하기 위해 getCurrentAuditor를 임시로 구현했다.
테스트를 위해 @Import를 사용하여 위에서 구현한 AuditorConfig를 테스트 컨텍스트에서 사용하도록 했다.
Spirng Data Cassandra는 @GeneratedValue를 지원하지 않기 때문에, 직접 랜덤 값을 생성하고 설정해 주었다.
데이터를 save 하고, 이를 fetch 해서 Debug로 데이터의 내부를 보면 createdDate와 createdBy가 null로 들어가 있다.
하지만 @LastModifiedAt과 @LastModifiedDate로 설정한 값은 잘 들어가 있다.
문제 원인: PersistentEntityIsNewStrategy의 동작
앞 상황에서, Spring Data Cassandra는 해당 데이터가 새로 삽입되는 데이터인지 감지하지 못한다.
이는 PersistentEntityIsNewStrategy의 동작과 관련 있다.
PersistentEntityIsNewStrategy
Spring Data는 내부적으로 엔티티가 새로운지 판단하기 위해 기본적으로 PersistentEntityIsNewStrategy를 사용한다.
PersistentEntityIsNewStrategy는 기본적으로 사용되는 전략으로, Spring Data는 식별자(ID, 예: UUID)의 존재 여부를 통해 엔티티가 새로운지 판단한다. ID가 null이면 새로운 엔티티라고 가정한다.
예를 들어, 가장 흔하게 접할 수 있는 Spring Data JPA의 경우를 살펴보자.
예시 시나리오: 자동 생성된 ID 사용
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Version
private Integer version;
private String name;
// getters and setters
}
- 대부분의 경우, JPA 엔티티는 자동 생성되는 기본 키(예: @GeneratedValue)를 사용한다.
- ID가 null이면 Spring Data JPA는 엔티티가 새로 생성된 것으로 판단하고 데이터베이스에 INSERT 문을 실행한다.
- ID가 null이 아니면, 엔티티가 이미 저장된 것으로 판단하고 UPDATE 문을 실행한다.
- 이외에도, Spring Data JPA는 엔티티가 새 엔티티인지 다음과 같이 판단한다.
- EntityManager: 내부적으로 Spring Data JPA는 EntityManager를 사용하여 엔티티의 상태를 추적한다. EntityManager는 엔티티의 생명주기를 관리하며(persisted 또는 detached), save()를 호출하면 Spring Data가 EntityManager와 상호작용하여 엔티티가 새로운지 여부를 판단한다.
- Persistence Context: 엔티티가 데이터베이스에서 로드되면, 이는 persistence context에 의해 관리된다. 이 엔티티가 수정된 후 save()를 호출하면 JPA는 업데이트 작업을 수행하고, persistence context에 없는 경우 새 엔티티로 처리하여 삽입한다.
자동 생성되지 않는 Primary Key와 PersistentEntityIsNewStrategy의 한계
Spring Data JPA와 달리, Spring Data Cassandra는 @GeneratedValue와 같은 자동 ID 생성 기능을 지원하지 않는다.
따라서 Primary Key를 수동으로 생성해야 하며, ID가 수동으로 설정되었을 경우 Spring Data Cassandra는 PersistentEntityIsNewStrategy를 통해 새로운 엔티티 여부를 감지할 수 없다.
문제 해결: Persistable Interface 구현
Persistable을 구현하고 isNew()에서 true를 반환함으로써, Spring Data는 이 엔티티가 새로 생성된 것으로 인식하고, createdDate 필드를 설정하게 된다.
그리고 @EnableCassandraAuditing이 활성화된 상태에서 적절한 감사 필드(createdDate 등)를 설정한다.
참조