[Spring Data JPA] Id가 자동 생성이 아닌 Entity의 save

김형진·2023년 5월 8일
0
	@Transactional
	@Override
	public <S extends T> S save(S entity) {

		Assert.notNull(entity, "Entity must not be null");

		if (entityInformation.isNew(entity)) {
			em.persist(entity);
			return entity;
		} else {
			return em.merge(entity);
		}
	}

Spring Data JPA이용 시 객체 저장하기 위해서는 save를 호출하게 되는데, 실제 구현체의 로직은 이러하다.
isNew라는 메소드를 통해 새로 생성된 객체인지 아닌지 먼저 판단한 후 새로운 객체인 경우 바로 persist를 호출하고 아닌 경우 merge를 실행한다.

새로운 객체인지 아닌지 판단하는 기준은
식별자가 객체인 경우 null인 경우와 원시타입인 경우 0값인 경우이다.

그런데 문제는 ID가 DB에서 자동생성되지 않고 어플리케이션단에서 먼저 아이디를 부여하는 경우이다.

실제로는 아직 DB에 등록되지 않은 새로 생성된 객체이지만 어플리케이션단에서 이미 ID를 주입받았기 때문에 새로운 객체임에도 isNew 메소드의 결과가 false로 나와 merge를 실행하게 되는데,

merge는 DB에서 데이터를 한 번 조회해온 뒤 해당 데이터를 덮어쓰는 방식이기 때문에 불필요한 조회가 한 번 발생한다는 문제가 있다.

이러한 문제를 해결하려면


@Entity
@EntityListeners(AuditingEntityListener.class)
@AllArgsConstructor
@NoArgsConstructor
public class Test extends BaseEntity implements Persistable<String> {

    @Id
    String id; // UUID

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

    @Override
    public boolean isNew() {
        return getCreatedDate() == null;
    }
}

와 같이 Persistable 인터페이스를 구현하여 isNew() 메소드를 재정의해주면 된다.
지금 같은 경우엔 Test엔티티가 BaseEntity를 상속받아 createDate 필드를 갖고 있고

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String updatedBy;
}

createdDate필드는 Spring Data가 제공하는 @CreatedDate 어노테이션을 사용하고 있으므로 persist되기 직전 createdDate필드가 채워지게 되기 때문에,

isNew()가 호출되는 시점에는 아직 createdDate가 없다는 점을 이용하여 새 객체인지 아닌지 판단할 수 있다.

profile
히히

0개의 댓글