Framework & Library/Spring

[Spring Data JPA] jpa.generate-ddl과 jpa.hibernate.ddl-auto 프로퍼티

sechoi 2023. 10. 4. 23:53

참고

https://docs.spring.io/spring-boot/docs/2.0.6.RELEASE/reference/html/howto-database-initialization.html 

https://docs.jboss.org/hibernate/orm/5.0/manual/en-US/html/ch03.html

 

JPA를 사용한 데이터베이스 초기화

JPA에는 DDL 생성 기능이 있으며, 어플리케이션 시작 시 실행되도록 설정할 수 있다. 이를 다루는 두 가지 속성이 spring.jpa.generate-ddlspring.jpa.hibernate.ddl-auto 이다.

 

HBM2DDL_AUTO 프로퍼티

위 두 프로퍼티는 Hibernate의 HBM2DDL_AUTO 프로퍼티 값을 결정한다. 그렇다면 이 HBM2DDL_AUTO 프로퍼티는 무엇일까?

 

Automatically validates or exports schema DDL to the database when the SessionFactory is created. With create-drop, the database schema will be dropped when the SessionFactory is closed explicitly.

해당 프로퍼티는 Hibernate의 SessionFactory가 생성될 때, 데이터베이스 스키마 관리 작업(DDL)을 관리하고 제어하는 데 사용된다. 쉽게 얘기하면 데이터베이스 초기화 방식을 담당하는 프로퍼티라 할 수 있다.

 

spring.jpa.generate-ddl를 사용한 데이터베이스 초기화

Whether to initialize the schema on startup.

spring.jpa.generate-ddl은 boolean 값으로 어플리케이션이 시작할 때 스키마를 초기화할 것인지 결정한다. 

 

이는 jpa 공급업체(vendor)에 독립적이다. 이 때 스프링에서 정한 기본 vendor는 Hibernate 이므로 HibernateJpaVendorAdapter 클래스를 살펴보면 다음과 같이 해당 프로퍼티가 true일 경우, HBM2DDL_AUTO를 update로 지정한다.

private Map<String, Object> buildJpaPropertyMap(boolean connectionReleaseOnClose) {
    Map<String, Object> jpaProperties = new HashMap<>();

    if (isGenerateDdl()) {
        jpaProperties.put(AvailableSettings.HBM2DDL_AUTO, "update");
    }
    
    return jpaProperties;
}

 

Hibernate를 사용한 데이터베이스 초기화

DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" property. Defaults to "create-drop" when using an embedded database and no schema manager was detected. Otherwise, defaults to "none".

spring.jpa.hibernate.ddl-auto는 enum 값으로 DDL을 세밀한 방식으로 제어하는 Hibernate의 기능이다. enum은 다음과 같이 다섯 가지가 있다.

  • CREATE: Create the schema and destroy previous data.
  • CREATE-DROP: Create and then destroy the schema at the end of the session.
  • UPDATE: Update the schema if necessary.
  • NONE: Disable DDL handling
  • VALIDATE: Validate the schema, make no changes to the database.

만약 임베디드 데이터베이스를 사용할 경우 기본 값은 CREATE-DROP이고, 나머지 경우에는 NONE이 기본 값이라고 한다.

 

HibernateProperties 클래스에서 해당 프로퍼티로 HBM2DDL_AUTO의 값을 결정한다. 만약 NONE 외의 다른 값을 지정해주었을 경우, 해당 값이 들어가며 이외의 경우에는 값 지정이 아예 되지 않는다(속성이 사라진다).

private Map<String, Object> getAdditionalProperties(Map<String, String> existing, HibernateSettings settings) {

    String ddlAuto = determineDdlAuto(existing, settings::getDdlAuto);
    if (StringUtils.hasText(ddlAuto) && !"none".equals(ddlAuto)) {
        result.put(AvailableSettings.HBM2DDL_AUTO, ddlAuto);
    }
    else {
        result.remove(AvailableSettings.HBM2DDL_AUTO);
    }
    return result;
}

 

HBM2DDL_AUTO 값 결정 과정

여기까지 살펴봤으면 다음과 같은 의문이 생긴다. 두 프로퍼티로 HBM2DDL_AUTO 값을 지정해줄 때, 충돌이 일어나면 어떻게 될까?

예를 들어 jpa.generate-ddl은 true로, jpa.hibernate.ddl-auto은 create로 지정해줬다면 전자는 update이고 후자는 create로 값의 충돌이 일어날 것임을 추측해볼 수 있다.

 

이를 알아보기 위해 위 예시 상황으로 각각의 값을 지정한 후 HBM2DDL_AUTO 값 결정 과정을 디버깅해보았고, AbstractEntityManagerFactoryBean 클래스의 afterPropertiesSet 메서드에 도달했다. 

 

@Override
public void afterPropertiesSet() throws PersistenceException {

    JpaVendorAdapter jpaVendorAdapter = getJpaVendorAdapter();
    if (jpaVendorAdapter != null) {
        
        Map<String, ?> vendorPropertyMap = (pui != null ? jpaVendorAdapter.getJpaPropertyMap(pui) :
                jpaVendorAdapter.getJpaPropertyMap());
                
        if (!CollectionUtils.isEmpty(vendorPropertyMap)) {
            vendorPropertyMap.forEach((key, value) -> {
                if (!this.jpaPropertyMap.containsKey(key)) {
                    this.jpaPropertyMap.put(key, value);
                }
            });
        }
    }
}

vendorPropertyMap(spring.jpa.generate-ddl)에서는 UPDATE, jpaPropertyMap(jpa.hibernate.ddl-auto)에서는 CREATE로 각자 설정값을 가져왔다.

 

그리고 vendorPropertyMap이 비어있지 않으면 jpaPropertyMap에 key가 존재하지 않을 때만 value을 옮긴다.

 

💡 즉, jpa.hibernate.ddl-auto가 NONE인 경우(key가 존재하지 않음)에는 spring.jpa.generate-ddl이 지정한 값을 따르고, 이외의 경우에는 jpa.hibernate.ddl-auto가 지정한 값을 따른다.

 

따라서 위 상황에서는 CREATE로 결정된다.