Hello

JPA 기본키 생성 전략 본문

java

JPA 기본키 생성 전략

nari0_0 2023. 9. 7. 19:00
728x90

GenerationType.enum

JPA는 기본키 생성 전략의 타입을 정의한 enum을 제공합니다.

AUTO(default) JPA 제공자가 DB에 대해 적절한 전략을 선택
IDENTITY 데이터베이스 auto-increment 의존
JPA는 insert를 수행한 후 또는 트랜잭션 커밋 시 기본 키 값을 할당
MySQL, SQL Server, PostgreSQL, DB2, Derby, Sybase 지원

SEQUENCE 데이터베이스 SEQUENCE 를 사용해 기본키 생성
트랜잭션 커밋 되고 EntityManager.persist() 호출 후에 기본키를 생성
Oracle, PostgreSQL, DB2 지원
TABLE 키 생성 관리 테이블을 만들어서 SEQUENCE처럼 사용
EntityManager.persist() 호출 후 트랜잭션 커밋되기 전 기본키를 생성

(= 권장하지 않음 이유는 아래 설명,)

2022년에 출시된 3.1.0버전 부터 JPA는 @GeneratedValue에 사용할 수 있는 새로운 GenerationType.UUID를 제공합니다.

UUID 엔터티의 UUID를 자동으로 생성하도록 지시

 

기술스택

spring-boot-2.x

hibernate 5.x

MySql 5.x

 

MySQL을 사용할 때 항상 GenerationType.IDENTITY를 명시적으로 선언해서 사용해 왔습니다. 테스트를 위해 AUTO로 실행해보았는데 JPA가 기본키 생성 전략을 잘 선택해줄 것이라고 기대했으나 hibernate_sequence 테이블이 존재하지 않는다는 에러가 발생했습니다.

stackoverflow에 나와 똑같은 이슈가 있는 사람이 있었고 해당 글의 답변을 보니 AUTO 모드 시 SEQUENCE 모드로 동작한다는 내용이 있었습니다.

왜 sequence 라는 단어가 들어간 것인지??

왜 hibernate_sequence 라는 테이블이 존재하지 않는다는 에러가 발생했는지??

왜 기대와 다르게 동작하는 것인지?? 

왜 이런 의문이 들었었고 이해한 내용을 정리해보려고 합니다.

 

GenerationType에 대해 알아보기 전에 먼저 보아야할 내용이 있습니다.

hibernate는 IdGeneratorStrategyInterpreter 인터페이스가 식별자 생성기 정보를 해석하기 위한 전략을 선택하도록 합니다. 아래 두 구현체를 제공해 버전에 따라 맞는 Interpreter를 선택 해야합니다.

determineGeneratorName 메소드가 GenerationType을 기반으로 사용해야하는 기본키 generator명을 결정 할 수 있도록 합니다. SpringBoot 버전에 따라 GenerationType.AUTO 일 때 결정되는 ID Generator가 다릅니다.

 

5.0 이전 : LegacyFallbackInterpreter 기본 전략. 실제 식별자 생성기를 해결하기 위해 Dialog#getNativeIdentifierGeneratorStrategy를 사용하는 네이티브 생성기 전략에 AUTO를 매핑합니다 (identity or sequence)

5.0 이후 : FallbackInterpreter 기본 전략. 기본 데이터베이스가 시퀀스를 지원하는 경우 SEQUENCE 생성기가 사용됩니다. 그렇지 않으면 TABLE 생성기가 대신 사용됩니다.

 

MetadataBuildingOptionsImpl 코드를 보면, idGeneratorTypeInterpreter 필드 선언과 동시에 초기화 합니다. 

MetadataBuildingOptionsImpl

위 에서 값 초기화를 위해 호출된 생성자 클래스를 보면 fallbackInterpreter 필드를 FallbackInterpreter.INSTANCE 로 초기화 한것을 알 수 있습니다.

다른 설정을 하지 않는다면 hibernate는 FallbackInterpreter를 사용하게 되어 있습니다.

IdGeneratorInterpreterImpl

useNewIdentifierGenerators 기본값은 true입니다.

  • Springboot 1.5.x : hibernate.id.new_generator_mappings 속성값이 false
  • Springboot 2.x.x: hibernate.id.new_generator_mappings 속성값이 true

생성자 일부
IdGeneratorInterpreterImpl 코드 일부

위 내용을 통해 기본 설정은 FallbackInterpreter을 선택하는 것을 알 수 있습니다. 그럼, GenerationType이 어떻게 선택되는지 한번 알아보겠습니다.

GenerationType.AUTO인데 SEQUENCE로??

디버깅을 타고타고 올라가서 GeneratedValue 어노테이션을 통해 generatorType을 찾는 코드를 발견했습니다.

AnnotationBinder.processId 일부

1. 첫번째 밑줄친 부분을 보면 GenerationValue 어노테이션을 넘겨 GenerationType 을 찾는다.Id 필드에 GenerationValue 어노테이션이 없다면 "assigned"를 갖는다.

AnnotationBinder.processId 일부

1-1. 우리는 위 과정을 통해 FallbackInterpreter.determineGeneratorName이 호출 된 다는 것을 알 수 있다.

AnnotationBinder.generatorType

1-2. FallbackInterpreter.determineGeneratorName

명시적으로 generationType을 선언한 경우 각각의 타입을 리턴합니다.

default 영역을 살펴보면, {

  //AUTO 주석을 보아 GenerationType.AUTO 일 때 default 영역이 실행되는 것을 짐작할 수 있습니다.

 

  1. "increment".equalsIgnoreCase 조건 통과 시 IncrementGenerator 사용 ex)@GeneratedValue(generator="increment")

  2. @Id가 선언된 필드가 UUID 일 때 UUIDGenerator 사용

  3. 위 조건에 해당되지 않는다면 SequenceStyleGenerator 사용

}

@GeneratedValue(생략) 할 경우 SequenceStyleGenerator 전략을 선택하게됩니다.

IdGeneratorInterpreterImpl.FallbackInterpreter.determineGeneratorName

generatorType는 SequenceStyleGenerator 이 된다.

strategy가 native 인 경우 사용하는 Dialect에 의해 Generator가 결정됩니다.

  - 5.0이후 버전에서는 FallbackInterpreter이 사용되어 사용자가 명시하지 않는 이상 native가 될일이 없으나, LegacyFallbackInterpreter를 사용할 때, DB별 지원하는 전략을 선택합니다.

DefaultIdentifierGeneratorFactory.getIdentifierGeneratorClass

Dialect에서 Sequence기능 지원에 따라 실제 사용하는 DatabaseStructure( Table or Sequence )가 결정됩니다. Sequence기능을 지원하는 경우 SequenceStructure를 사용, 지원하지 않는 경우 TableStructure를 사용합니다.

SequenceStyleGenerator

결론 : 생략하는 것보다 명시적으로 어떤 기본키 생성 전략을 사용할 것인지 작성을 해서 다른사람이 코드를 볼 때도 어떤 방법으로 기본키가 할당되는지 알게 하도록 하는 것이 더 나은거 같단 생각이 들었다.

 

 

참고 :

https://www.baeldung.com/jpa-strategies-when-set-primary-key

https://www.baeldung.com/java-hibernate-uuid-primary-key

https://javaee.github.io/javaee-spec/javadocs/javax/persistence/GenerationType.html

https://docs.jboss.org/hibernate/orm/5.4/javadocs/org/hibernate/id/enhanced/TableGenerator.html#determineSegmentColumnSize-java.util.Properties-

https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators-auto

728x90