auto-increment를 사용하기 때문에 데이터베이스에 insert 쿼리가 실행이 되어야 ID값을 알 수 있다.
그래서 JPA에서는 persist()시점에 즉시 insert 쿼리를 실행하고 데이터베이스에서 식별자를 조회하게 된다.
즉, 쓰기 지연의 이점을 누릴 수 없다.
시퀀스 전략은insert 쿼리와 상관없이 데이터베이스에서 시퀀스를 만들어서
그 시퀀스를 호출해서 사용하기 때문에 insert 쿼리가 나가지 않아도 ID를 생성할 수 있다.
이게 가능한 이유는 JPA가 데이버베이스에 있는 시퀀스를 내부적으로 조회하는 기능이 있기에 가능하다.
즉, 쓰기 지연의 이점을 누릴 수 있다.
시퀀스를 가져오기 위해서 DB 요청을 해야하는거 아닌가?
맞다. 시퀀스를 가져오기 위해 INSERT쿼리를 날리지는 않지만, 결국 시퀀스를 조회해야 한다.
그럼 100개의 insert를 날리면, 100번의 시퀀스 요청이 생기는 것이다.
결국 네트워크롤 계속 타기 때문에 성능에 영향이 있는거 아닌가? 라는 의문이다.
JPA는 이걸 initialValue와allocationSize
로 해결했다.
allocationSize
는 데이터베이스에 시퀀스를 설정개수만큼 미리 올려놓고,
그걸 애플리케이션에서 캐싱해놓고, 사용하고 다 쓰면 다시 설정 개수만큼 사용을 하는 방식으로 해결하였다.
→ initialValue
는 시퀀스 초기값 또는 마지막으로 생성된 값이 기준이다.
만약 설정 개수가 1 ~ 50이면, 50개를 미리 올려놓고, 캐싱 후 사용하고 다 사용하면 51 ~ 100까지 올리고, 캐싱하여 사용하는 방식이다.
특이한 점은 시퀀스 콜을 두 번하게 된다. 이유는 시작점과 끝점을 알아야 하기 때문이다.
// call next value for sequence
// call next value for sequence
em.persist(member1); // 1, 51
em.persist(member2); // 메모리
em.persist(member3); // 메모리
// 51을 만나게 되면 다시 미리 끝지점을 다시 알아야 하기 때문에 끝 지점은 100을 알아야 하기 때문에
// 다시 call을 하게 된다.
그럼 allocationSize
를 아주 크게 만개 정도 하면 안되나?
그러면 나쁘지는 않겠지만, 애플리케이션 서버를 내릴 경우 구멍이 생기므로 데이터베이스 입장에서 효율적이지 못하다.
그렇지만 구멍이 나도 크게 상관은 없다고 한다.
근데 동시성 이슈는?
미리 캐싱을 하면, 다른 쓰레드에서 사용하면 겹치는거 아닌가?
문제가 없다.
결론
데이터베이스에 맞춰서 사용하면 된다.
영속성 컨텍스트는 pk가 있어야 영속성 컨텍스트에 관리될 수 있기 때문에 반드시 pk가 필요하다.
https://jojoldu.tistory.com/295
스프링 부트는 Hibernate의 id 생성 전략을 그대로 따라갈지 말지 결정하는 useNewIdGeneratorMappings
설정이 있다.
스프링 부트2.0과 스프링 부트1.5는 설정이 다르다.
HibernateProperties.java(2.x)
/**
* Whether to use Hibernate's newer IdentifierGenerator for AUTO, TABLE and SEQUENCE.
* This is actually a shortcut for the "hibernate.id.new_generator_mappings" property.
* When not specified will default to "true".
*/
private Boolean useNewIdGeneratorMappings;
private void applyNewIdGeneratorMappings(Map<String, Object> result) {
if (this.useNewIdGeneratorMappings != null) {
result.put(AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, this.useNewIdGeneratorMappings.toString());
}
else if (!result.containsKey(AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS)) {
result.put(AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "true");
}
}
javadoc 설명을 보면 hibernate의 id 생성 규칙을 따르고, 기본값이 true
라는 것을 알 수 있다.
반면 부트 1.5
는 디폴트가 false
이고, false일 경우 hibernate5
의 설정을 따라가지 않는다고 한다.
Hibernate5부터는 MySQL에서의 AUTO는 IDENTITY가 아닌 TABLE전략
을 기본 전략으로 가져가게 된다.
Hibernate5이전에는 AUTO는 IDENTITY가 기본 전략이었다.
이런 이유로 부트 2.0 사용하고, Hibernate5 이상을 사용 시 TABLE 전략으로 적용되면서 시퀀스 증가가 안되는 버그가 발생할 수 있기 때문에
AUTO는 지양하는 것이 좋다.
useNewIdGeneratorMappings
설정이 있다.use-new-id-generator-mappings
을 false로 설정한다@GeneratedValue
의 전략을 GenerationType.IDENTITY
로 지정한다개인적인 의견은 설정을 변경하는 것 보다는 Hibernate전략과 부트의 전략을 맞추는게 좋을 것 같아서
GenerationType.IDENTITY
가 좋을 것 같다.
아무래도 버전이 변경되거나 할 경우 hibernate의 버전과 부트의 버전에 따라서 설정을 해주기는 번거롭기 때문에
명시적으로나 간편함나 더 유용할 것 같다고 생각해서이다.