CrudRepository의 기본 구현인 SimpleJpaRepositoryy의 save 메서드는 이렇게 구현되어있다.
new인 경우에는 insert 쿼리를 날리고, 그렇지 않은 경우에는 update 쿼리를 날린다. R2DBC도 똑같다.
AbstractEntityInformation의 isNew에서는
타입이 null이거나
Number이면서 값이 0
인 경우 true를 반환하고,
primitive 타입이 아니면서 값이 존재하면
false를 반환하며, primitive 타입 필드면 에러를 던지는 것이 기본 전략이다.
여기서, ID를 직접 지정하기 위해 자동 생성 전략을 선택하지 않았을 경우엔 insert이전에 select 쿼리가 한번 나가는 것을 확인할 수 있다. ID 필드에 값을 세팅해주면 isNew()에서 (ID 필드가 null이 아니라) false를 반환하고, 그 결과 merge()가 호출되기 때문이다.
변경을 위해선 변경 감지(dirty-checking)를, 저장을 위해선 persist()만이 호출되도록 유도해야 실무에서 성능 이슈 등을 경험하지 않을 수 있다. 위와 같이 merge가 호출되지 않도록 하려면 enitty에서 Persistable 인터페이스를 상속받게 하고, overriding해주는 방법이 있다.