> ## Content Index
> Fetch the complete content index at: https://www.popit.kr/llms.txt
> Use this file to discover other available public pages before exploring further.

# 하이버네이트는 어떻게 자동 키 생성 전략을 결정하는가
- URL: https://www.popit.kr/how-hibernate-decides-key-generation-strategy/
- Published: 2018-09-28T00:37:20.000Z
- Updated: 2018-10-04T00:07:58.000Z
- Author: 유 영모
- Tags: #소프트웨어, Java, JPA, Hibernate, ORM

데이터베이스 테이블의 [기본 키Primary key](https://en.wikipedia.org/wiki/Primary%5Fkey?ref=popit.kr)를 선정할 때 크게 2가지 선택지가 있는데 하나는 [자연 키Natural key](https://en.wikipedia.org/wiki/Natural%5Fkey?ref=popit.kr)이고 또 하나는 [대체 키Surrogate key](https://en.wikipedia.org/wiki/Surrogate%5Fkey?ref=popit.kr)이다. 자연 키는 전화번호, 이메일처럼 비즈니스적으로 의미 있는 키를 말하며, 대체 키는 비즈니스와 상관없이 임의로 만들어진 키를 말한다. [JPAJava Persistence API](https://en.wikipedia.org/wiki/Java%5FPersistence%5FAPI?ref=popit.kr)는 데이터베이스 테이블 대체 키를 기본 키로 자동 생성하는 기능을 지원한다. 사용법은 엔티티Entity 클래스에 [Id 어노테이션](https://docs.oracle.com/javaee/6/api/javax/persistence/Id.html?ref=popit.kr)과 함께 결합하여 [GeneratedValue 어노테이션](https://docs.oracle.com/javaee/6/api/javax/persistence/GeneratedValue.html?ref=popit.kr)을 추가한다.

```java
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 프로그래밍](http://www.yes24.com/24/goods/19040233?scode=032&OzSrank=3&ref=popit.kr) 4.6 기본키 매핑 **GeneratedValue-AUTO를 사용하는 경우 자동 키 생성 전략은 어떻게 결정하는 것일까?** 이 글은 JPA 구현체 중 하나인 [하이버네이트Hibernate](http://hibernate.org/orm/?ref=popit.kr) 5에서 하이버네이트가 자동 키 생성 전략을 어떤 과정으로 결정하는지에 대해 설명한다.

# 하이버네이트 Interpreting AUTO

[하이버네이트 문서](https://docs.jboss.org/hibernate/orm/5.2/userguide/html%5Fsingle/Hibernate%5FUser%5FGuide.html?ref=popit.kr) 중 [2.6.7\. Interpreting AUTO 절](https://docs.jboss.org/hibernate/orm/5.2/userguide/html%5Fsingle/Hibernate%5FUser%5FGuide.html?ref=popit.kr#identifiers-generators-auto)을 아래 그림으로 나타냈다.

![그림. 하이버네이트 Interpreting AUTO](https://www.popit.kr/content/images/wp/2018/09/image2018-9-21_9-5-47-1.png)

그림. 하이버네이트 Interpreting AUTO

하이버네이트는 가장 먼저 @GeneratedValue가 사용된 데이터 타입(Java)을 확인하고 [UUID](https://docs.oracle.com/javase/8/docs/api/java/util/UUID.html?ref=popit.kr)라면 UUID Generator가 된다.\[1\]

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

데이터 타입이 숫자 타입(Integer, Long)이면 하이버네이트 프로퍼티Property인 '[hibernate.id.new\_generator\_mappings](https://docs.jboss.org/hibernate/orm/5.2/userguide/html%5Fsingle/Hibernate%5FUser%5FGuide.html?ref=popit.kr#%5Fidentifier%5Foptions)'\[2\]에 설정된 값(기본값은 TRUE)을 확인한다. 값이 'FALSE' 인 경우 Native Generator가 된다. Native Generator는 다시 하이버네이트에 설정된 방언Dialect\[3\](아래 예시에서는 [MySQL5Dialect](https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/dialect/MySQL5Dialect.html?ref=popit.kr))으로 결정한다.\[4\]

```xhtml
<!-- 하이버네이트 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 Mode](https://maven.apache.org/pom.html?ref=popit.kr#What%5Fis%5Fthe%5FPOM)l은 아래와 같다.

```xhtml
<?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](https://docs.jboss.org/hibernate/orm/5.2/userguide/html%5Fsingle/Hibernate%5FUser%5FGuide.html?ref=popit.kr#bootstrap-jpa-compliant)은 아래와 같다.

```xhtml
<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>
```

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

```java
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](https://docs.jboss.org/hibernate/orm/5.2/userguide/html%5Fsingle/Hibernate%5FUser%5FGuide.html?ref=popit.kr#%5Fidentifier%5Foptions)' 설정값을 확인하게 되는데 명시적으로 설정해 주지 않았기 때문에 'TRUE'이다. 따라서 SequenceStyleGenerator를 사용하게 되며, MySQL은 시퀀스 오브젝트를 지원하지 않기 때문에 결국 **Table Generator가 답이다.**

![그림. 하이버네이트 Interpreting AUTO - MySQL](https://www.popit.kr/content/images/wp/2018/09/image2018-9-21_9-9-36.png)

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

실제로 하이버네이트 [DDLData Definition Language](https://en.wikipedia.org/wiki/Data%5Fdefinition%5Flanguage?ref=popit.kr) 로그를 통해서 Table Generator를 사용하는 것을 확인할 수 있다. 하이버네이트는 'hibernate\_sequence'라는 키 생성 전용 테이블을 생성하고 Product 테이블 생성 시 id 컬럼에는 not null과 기본키 제약조건을 선언한다.\[5\]

![갈무리. 하이버네이트 DDL 로그](https://www.popit.kr/content/images/wp/2018/09/image2018-9-21_8-41-14.png)

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

# GitHub

전체 코드는 필자의 [GitHub 저장소](https://github.com/YooYoungmo/article-hibernate-generated-value-auto?ref=popit.kr)에서 확인할 수 있다.

# 주석

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

```java
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 프로그래밍](http://www.yes24.com/24/goods/19040233?scode=032&OzSrank=3&ref=popit.kr) 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](https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/dialect/Dialect.html?ref=popit.kr)

\[4\] 실제로 하이버네이트는 방언 클래스의 [getNativeIdentifierGeneratorStrategy](https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/dialect/Dialect.html?ref=popit.kr#getNativeIdentifierGeneratorStrategy--) 메소드를 호출하여 방언으로 설정한 생성 전략을 사용한다. \[5\] hibernate\_sequence 테이블을 어떤 식으로 사용하는지는 아래 코드를 실행한 SQL 로그로 확인할 수 있다.

```java
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 로그](https://www.popit.kr/content/images/wp/2018/09/image2018-9-21_8-44-17.png)

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

# 참고 자료

- [Hibernate User Guide](https://docs.jboss.org/hibernate/orm/5.2/userguide/html%5Fsingle/Hibernate%5FUser%5FGuide.html?ref=popit.kr)
- [자바 ORM 표준 JPA 프로그래밍](http://www.yes24.com/24/goods/19040233?scode=032&OzSrank=3&ref=popit.kr) \- 김영한 저