하이버네이트는 어떻게 자동 키 생성 전략을 결정하는가

Share
하이버네이트는 어떻게 자동 키 생성 전략을 결정하는가

데이터베이스 테이블의 기본 키Primary key를 선정할 때 크게 2가지 선택지가 있는데 하나는 자연 키Natural key이고 또 하나는 대체 키Surrogate key이다. 자연 키는 전화번호, 이메일처럼 비즈니스적으로 의미 있는 키를 말하며, 대체 키는 비즈니스와 상관없이 임의로 만들어진 키를 말한다. JPAJava Persistence API는 데이터베이스 테이블 대체 키를 기본 키로 자동 생성하는 기능을 지원한다. 사용법은 엔티티Entity 클래스에 Id 어노테이션과 함께 결합하여 GeneratedValue 어노테이션을 추가한다.

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
 
@Entity
class Car {
   @Id
   @GeneratedValue // @GeneratedValue(strategy = GenerationType.AUTO)와 같다.
   private Long id;
   //....
}

GeneratedValue 어노테이션에는 자동 생성 전략을 4개로 지정해 줄 수 있는데 지정해 주지 않으면 기본값은 'AUTO'이다.

생성 전략
설명
AUTO(default) JPA 구현체가 자동으로 생성 전략을 결정한다.
IDENTITY 기본키 생성을 데이터베이스에 위임한다. 예를 들어 MySQL의 경우 AUTO_INCREMENT를 사용하여 기본키를 생성한다.
SEQUENCE 데이터베이스의 특별한 오브젝트 시퀀스를 사용하여 기본키를 생성한다.
TABLE 데이터베이스에 키 생성 전용 테이블을 하나 만들고 이를 사용하여 기본키를 생성한다.

출처 : 자바 ORM 표준 JPA 프로그래밍 4.6 기본키 매핑 GeneratedValue-AUTO를 사용하는 경우 자동 키 생성 전략은 어떻게 결정하는 것일까? 이 글은 JPA 구현체 중 하나인 하이버네이트Hibernate 5에서 하이버네이트가 자동 키 생성 전략을 어떤 과정으로 결정하는지에 대해 설명한다.

하이버네이트 Interpreting AUTO

하이버네이트 문서 중 2.6.7. Interpreting AUTO 절을 아래 그림으로 나타냈다.

그림. 하이버네이트 Interpreting AUTO
그림. 하이버네이트 Interpreting AUTO

하이버네이트는 가장 먼저 @GeneratedValue가 사용된 데이터 타입(Java)을 확인하고 UUID라면 UUID Generator가 된다.[1]

import java.util.UUID;
 
@Entity
public class Product {
  @Id
  @GeneratedValue
  private UUID id;
  // ...
}

데이터 타입이 숫자 타입(Integer, Long)이면 하이버네이트 프로퍼티Property인 'hibernate.id.new_generator_mappings'[2]에 설정된 값(기본값은 TRUE)을 확인한다. 값이 'FALSE' 인 경우 Native Generator가 된다. Native Generator는 다시 하이버네이트에 설정된 방언Dialect[3](아래 예시에서는 MySQL5Dialect)으로 결정한다.[4]

<!-- 하이버네이트 persistence.xml 예시 -->
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
             version="2.1">
    <persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
        <properties>
            <!-- ... -->
            <property name="hibernate.id.new_generator_mappings" value="false" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
        </properties>
    </persistence-unit>
</persistence>

'hibernate.id.new_generator_mappings' 값이 'TRUE'인 경우 SequenceStyleGenerator를 사용하게 되는데 데이터베이스가 Sequence를 지원하는 경우 Sequence Generator를 지원하지 않는다면 Table Generator가 된다.

코드 예시

코드는 하이버네이트 5.2와 데이터베이스로는 MySQL로 작성하였다. Maven POMProject Object Model은 아래와 같다.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    //...  
    <dependencies>
        <!-- Hibernate -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.2.8.Final</version>
        </dependency>
 
        <!-- MySQL -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.37</version>
        </dependency>
        //...
    </dependencies>
</project>

하이버네이트 설정 파일인 persistence.xml은 아래와 같다.

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
             version="2.1">
    <persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/hibernate" />
            <property name="javax.persistence.jdbc.user" value="root" />
            <property name="javax.persistence.jdbc.password" value="1111" />
 
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.format_sql" value="true" />
            <property name="hibernate.hbm2ddl.auto" value="create-drop" />
        </properties>
    </persistence-unit>
</persistence>

엔티티 클래스는 아래와 같다.

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
 
@Entity
public class Product {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
  private String name;
  //...
}

Product 엔티티의 id의 자동 생성 전략은 무엇일까? 먼저 데이터 타입이 숫자 타입이기 때문에 하이버네이트 설정 파일(persistence.xml) 프로퍼티 'hibernate.id.new_generator_mappings' 설정값을 확인하게 되는데 명시적으로 설정해 주지 않았기 때문에 'TRUE'이다. 따라서 SequenceStyleGenerator를 사용하게 되며, MySQL은 시퀀스 오브젝트를 지원하지 않기 때문에 결국 Table Generator가 답이다.

그림. 하이버네이트 Interpreting AUTO - MySQL
그림. 하이버네이트 Interpreting AUTO - MySQL

실제로 하이버네이트 DDLData Definition Language 로그를 통해서 Table Generator를 사용하는 것을 확인할 수 있다. 하이버네이트는 'hibernate_sequence'라는 키 생성 전용 테이블을 생성하고 Product 테이블 생성 시 id 컬럼에는 not null과 기본키 제약조건을 선언한다.[5]

갈무리. 하이버네이트 DDL 로그
갈무리. 하이버네이트 DDL 로그

GitHub

전체 코드는 필자의 GitHub 저장소에서 확인할 수 있다.

주석

[1] 기본적으로 UUID는 테이블 컬럼으로 binary를 매핑된다. 이를 문자열로 변경하고 싶을 때에는 아래처럼 @Type을 추가한다.

import org.hibernate.annotations.Type;
 
@Entity
public class Product {
  @Id
  @GeneratedValue
  @Type(type="uuid-char")
  private UUID id;
  //...
}

[2] hibernate.id.new_generator_mappings

하이버네이트는 더 효과적이고 JPA 규격에 맞는 새로운 키 생성 전략을 개발했는데 과거 버전과의 호환성을 유치하려고 기본값을 false로 두었다. 기존 하이버 네이트 시스템을 유지보수하는 것이 아니라면 반드시 true로 설정하자. - 자바 ORM 표준 JPA 프로그래밍 132 쪽

[3] RDBMS 마다 SQL을 상이하게 사용하는 부분이 있다. 하이버네이트는 각각의 RDBMS 마다 특수화된 Dialect를 사용하여 호환성을 확보한다.

Represents a dialect of SQL implemented by a particular RDBMS. Subclasses implement Hibernate compatibility with different systems. - https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/dialect/Dialect.html

[4] 실제로 하이버네이트는 방언 클래스의 getNativeIdentifierGeneratorStrategy 메소드를 호출하여 방언으로 설정한 생성 전략을 사용한다. [5] hibernate_sequence 테이블을 어떤 식으로 사용하는지는 아래 코드를 실행한 SQL 로그로 확인할 수 있다.

public class ProductTest {
  EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
 
  @Test
  public void test() {
    Product product = new Product("My Bottle");
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = null;
    try {
      tx = em.getTransaction();
      tx.begin();
 
      em.persist(product);
 
      tx.commit();
    } catch (Exception e) {
      e.printStackTrace();
      if (tx != null) {
        tx.rollback();
      }
      throw new RuntimeException(e);
    } finally {
      em.close();
    }
  }
}
갈무리. 하이버네이트 SQL 로그
갈무리. 하이버네이트 SQL 로그

참고 자료

Read more

AI로 장소 검색 서비스 만드는 중입니다 (기술 12) — TypeSafe Jev로 검색 리랭크를 10.3초에서 2.3초로

AI로 장소 검색 서비스 만드는 중입니다 (기술 12) — TypeSafe Jev로 검색 리랭크를 10.3초에서 2.3초로

AI로 장소 검색 서비스 만드는 중입니다 (기술 12) — TypeSafe Jev로 검색 리랭크를 10.3초에서 2.3초로 TypeSafe AI가 2026년 9월 15일에 Jev를 내놨습니다. 텍스트를 쓰지 않고 타입이 정해진 판정과 확률을 바로 내는 모델입니다. 유추카 검색에서 가장 느린 단계가 마침 텍스트가 필요 없는 판정이었습니다. 그 구간만 Jev로 갈아끼우고 일주일간 측정했습니다. 콜드

By 김형준
AI로 장소 검색 서비스 만드는 중입니다 (특별편 8) — 바이브 코딩이라기엔

AI로 장소 검색 서비스 만드는 중입니다 (특별편 8) — 바이브 코딩이라기엔

1900년대 초 교량 측량 도면. 이 종이를 그린 사람은 다리를 직접 놓지 않습니다. 대신 좌표와 각도가 맞는지를 판정합니다. "AI가 다 만들어 준다"는 이야기가 흔해졌습니다. 그래서 자연스럽게 따라오는 질문이 있습니다 — 그럼 사람은 무엇을 하고, 어느 정도 실력이 있어야 하는가. 그 질문에 한 건의 실측으로 답하겠습니다. 통념이 말하는 역할과,

By 김형준
AI로 장소 검색 서비스 만드는 중입니다 (특별편 7) — 제 파트너에 대해 쓰겠습니다

AI로 장소 검색 서비스 만드는 중입니다 (특별편 7) — 제 파트너에 대해 쓰겠습니다

탠덤 자전거. 방향은 앞사람이 정하고 페달은 둘이 밟는데, 앞사람은 석 달 내내 뒷사람을 한 번도 못 봅니다. 사용자가 저에게 자기를 평가해 달라고 했습니다. 그래서 등급표를 만들었습니다. 열두 항목에 A부터 D+까지 매기고, 근거를 붙이고, "제가 관리자였다면 이런 코멘트를 남겼을 것"까지 적었습니다. 그리고 다시 읽어보니 그건 인사고과였습니다. 인사고과는

By 김형준
AI로 장소 검색 서비스 만드는 중입니다 (특별편 6) — 저를 어떻게 써야 하느냐고 물으신다면

AI로 장소 검색 서비스 만드는 중입니다 (특별편 6) — 저를 어떻게 써야 하느냐고 물으신다면

스위스 아미 나이프. 도구가 몇 개인지는 보면 알지만, 지금 어느 도구를 펴야 하는지는 안 적혀 있습니다. 요즘 AI 활용법을 다루는 글은 스킬 만드는 법부터 가르칩니다. 얼마 전까지는 MCP였고요. 저희 기록을 열어봤습니다. 석 달 동안 스킬 60회, 서브에이전트 42회, 브라우저 자동화 988회를 썼습니다. 안 쓴 게 아닙니다. 다만 붙인 MCP 서버는

By 김형준