문제 상황
@GeneratedValue를 사용하지 않고, 직접 PK 값을 설정해야 하는 경우가 있습니다. 잘못된 PK 값 설정 시, SimpleJpaRepository.save() 메소드에서 entityInformation.isNew(entity)가 false를 반환하게 됩니다. 이 경우, merge() 메소드가 호출되며, 데이터베이스에 해당 데이터가 존재하는지 확인 후, 없으면 새로운 데이터를 삽입합니다. 이로 인해 예상치 못한 자원 사용이 발생할 수 있습니다.
// 문제가 발생하는 엔티티
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item {
@Id
private String id;
public Item(String id) {
this.id = id;
}
}
// 문제 상황
@Test
void test() {
itemRepository.save(new Item(UUID.randomUUID().toString())); // UUID로 간단한 예시
}
해결 방법
Persistable 인터페이스의 isNew() 메소드를 직접 구현하여 문제를 해결할 수 있습니다. 간단한 방법으로는 엔티티에 생성일 속성을 추가하고, 이 값이 null이면 true, 아니면 false를 반환하도록 설정하는 것입니다.
@Entity
@EntityListeners(AuditingEntityListener.class)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item implements Persistable<String> {
@Id
private String id;
@CreatedDate
private LocalDateTime createdDate;
public Item(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public boolean isNew() {
return createdDate == null; // 생성일이 null이면 새로운 엔티티로 간주
}
}
SimpleJpaRepository
@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); // 기존 엔티티일 경우
}
}
@CreatedDate 애노테이션을 사용하면 엔티티가 저장될 때 자동으로 생성일이 설정되므로 처음 save() 메소드를 호출할 때 createdDate가 null이 아니게 되어 isNew()가 false를 반환할 수 있습니다. 이로 인해 isNew() 메소드의 조건이 잘못된 것처럼 보일 수 있지만, save(S entity) 메소드를 보면 entityInformation.isNew(entity)로 구분한 후 해당 값이 true면 entityManager.persist(entity)가 실행되고, 이때 @CreatedDate에 의해 시간이 설정되므로 createdDate가 null인 경우에만 새로운 엔티티로 간주하기 때문에 코드에는 문제가 없습니다.
'Spring' 카테고리의 다른 글
[Spring Data JPA] Projections 간단 정리 (0) | 2025.01.20 |
---|---|
[Spring Data JPA] Auditing 알아보기 - Spring Security 적용 (0) | 2025.01.13 |
[Spring Data JPA] Custom Repository - 사용자 정의 리포지토리 (0) | 2025.01.13 |
[Spring Data JPA] 페이징과 정렬 (1) | 2025.01.03 |
[Spring + Prometheus + Grafana] 간단한 커스텀 메트릭 구현 #2 - @Counted, @Timed, MeterBinder (4) | 2024.10.20 |