Skip to content

Latest commit

 

History

History
93 lines (70 loc) · 2.61 KB

Persistable.md

File metadata and controls

93 lines (70 loc) · 2.61 KB

CrudRepository의 기본 구현인 SimpleJpaRepositoryy의 save 메서드는 이렇게 구현되어있다.

// SimpleJpaRepository의 save method
	@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);
		}
	}

new인 경우에는 insert 쿼리를 날리고, 그렇지 않은 경우에는 update 쿼리를 날린다. R2DBC도 똑같다.

// SimpleR2dbcRepository의 save method
	@Override
	@Transactional
	public <S extends T> Mono<S> save(S objectToSave) {

		Assert.notNull(objectToSave, "Object to save must not be null!");

		if (this.entity.isNew(objectToSave)) {
			return this.entityOperations.insert(objectToSave);
		}

		return this.entityOperations.update(objectToSave);
	}

AbstractEntityInformation의 isNew에서는

  • 타입이 null이거나

  • Number이면서 값이 0 인 경우 true를 반환하고,

  • primitive 타입이 아니면서 값이 존재하면 false를 반환하며, primitive 타입 필드면 에러를 던지는 것이 기본 전략이다.

	@Override
	public boolean isNew(Object entity) {

		Object value = valueLookup.apply(entity);

		if (value == null) {
			return true;
		}

		if (valueType != null && !valueType.isPrimitive()) {
			return false;
		}

		if (value instanceof Number) {
			return ((Number) value).longValue() == 0;
		}

		throw new IllegalArgumentException(
				String.format("Could not determine whether %s is new; Unsupported identifier or version property", entity));
	}

여기서, ID를 직접 지정하기 위해 자동 생성 전략을 선택하지 않았을 경우엔 insert이전에 select 쿼리가 한번 나가는 것을 확인할 수 있다. ID 필드에 값을 세팅해주면 isNew()에서 (ID 필드가 null이 아니라) false를 반환하고, 그 결과 merge()가 호출되기 때문이다.

변경을 위해선 변경 감지(dirty-checking)를, 저장을 위해선 persist()만이 호출되도록 유도해야 실무에서 성능 이슈 등을 경험하지 않을 수 있다. 위와 같이 merge가 호출되지 않도록 하려면 enitty에서 Persistable 인터페이스를 상속받게 하고, overriding해주는 방법이 있다.

public interface Persistable<ID> {

	/**
	 * Returns the id of the entity.
	 *
	 * @return the id. Can be {@literal null}.
	 */
	@Nullable
	ID getId();

	/**
	 * Returns if the {@code Persistable} is new or was persisted already.
	 *
	 * @return if {@literal true} the object is new.
	 */
	boolean isNew();
}