⬛ JPA에서 기본 ID 값은 왜 0으로 지정할까?
Spring JPA를 사용하면서 보통은 DB의 Id 전략을 따른다. (주로 auto increasement)
Long 타입을 Id 값으로 사용 시 왜 기본값으로 0을 설정할까? 아직 값이 지정되지 않았다는 의미로 -1을 사용하면 안 될까?
관성적으로 0을 사용해 왔는데 문득 의문이 들어 코드를 살펴보았다.
⬛ 내부 구현
spring JPA에서 우리가 사용하는 repository들은 JpaRepository 인터페이스를 구현한다. 이때 실 구현체는 SimpleJpaRepository이다.
public class SimpleJpaRepository{
...
@Transactional
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null");
if (this.entityInformation.isNew(entity)) {
this.entityManager.persist(entity);
return entity;
} else {
return (S)this.entityManager.merge(entity);
}
}
...
}
repository.save시 entity의 new 여부에 따라 persist 혹은 merge로 나뉘는 것을 볼 수 있다.
persist는 신규 영속 상태를 만들고 바로 INSERT를 실행. 해당 id가 존재한다면 예외가 발생한다.
merge는 해당 entity를 다시 영속 상태로 만들기 때문에 DB에 값이 있다면 UPDATE, 없을 경우 INSERT가 발생한다.
즉 merge가 발생한다면 2번의 쿼리가 발생하는 것이다.(있는지 조회 + 없다면 업데이트)
⬛ Entity new 여부 판단
그렇다면 전달된 entity의 new 여부는 어떻게 판단할까?
public abstract class AbstractEntityInformation{
...
public boolean isNew(T entity) {
ID id = (ID)this.getId(entity);
Class<ID> idType = this.getIdType();
if (!idType.isPrimitive()) {
return id == null;
} else if (id instanceof Number) {
Number n = (Number)id;
return n.longValue() == 0L;
} else {
throw new IllegalArgumentException(String.format("Unsupported primitive id type %s", idType));
}
}
...
}
코드 내부를 살펴보면 id값에 따라 다르게 처리한다.
기본 자료형이 아닐 때는 null일 때 new로 판단하고, Number 타입일 때는 0일 때만 new로 판단하고 있다.
만약 entity의 id 값을 -1로 지정한다면 여기에서 new로 판단하지 않기 때문에 mege를 하게 되고, id=-1인 entity가 있는지 SELECT 후 없으므로 신규 INSERT를 하게 되는 구조이다.
실제로 디버깅 로그를 보아도 id=0인 entity는 바로 INSERT 쿼리를 날리지만, id=-1인 entity는 UPDATE와 INSERT 쿼리가 각각 발생하게 된다.