하이버네이트 데이터베이스 스키마 자동 생성 시 키워드/예약어 충돌 문제 해결하기

엔터티 클래스 이름으로 일반적으로 많이 사용하는 Order나 Group으로 만들 때가 있다. 하이버네이트Hibernate는 JPA 엔터티를 기준으로 데이터베이스 스키마를 자동 생성해 주는 기능을 제공한다. 개발 초기에는 스키마가 자주 변경되기 때문에 이 기능을 사용하면 매우 편리하다.(설정값에 따라 테이블을 DROP 하기도 하니 사용 시 주의 필요)

하이버네이트는 스키마를 자동으로 생성할 때 기본적으로 엔터티 클래스 이름을 사용한다. 이때 클래스 이름이 데이터베이스 키워드/예약어(예. ORDER, GROUP, SELECT, WHERE 등)와 동일한 경우 스키마 생성에 실패한다.

이 글은 위와 같이 데이터베이스 키워드/예약어 충돌로 스키마 생성이 실패하는 문제에 대한 해결 방법을 소개한다.

코드 예시

JPA 엔터티 코드는 아래와 같다.

1
2
3
4
5
6
7
8
@Entity
public class Group {
  @Id
  @GeneratedValue
  private Long id;
  private String name;
  // ...
}

하이버네이트 데이터베이스 스키마 자동 생성 기능을 사용해 보자.

하이버네이트 설정 파일을 persistence.xml로 사용하는 경우 'hibernate.hbm2ddl.auto' 프로퍼티로 아래와 같이 추가한다.

1
2
3
4
5
6
7
8
9
10
<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">
        <properties>
            <!-- ... -->
            <property name="hibernate.hbm2ddl.auto" value="create" />
        </properties>
    </persistence-unit>
</persistence>

스프링 부트 JPA를 사용하는 경우 application.properties에 아래와 같이 추가한다.

1
spring.jpa.hibernate.ddl-auto=create

앞서 언급했던 대로 Group 엔터티 클래스명은 데이터베이스 예약어/키워드와 충돌하기 때문에 애플리케이션 실행 시 하이버네이트는 스키마 생성에 실패하며 아래와 같은 오류를 보여준다.

1
2
3
4
5
6
Caused by: org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "
    CREATE TABLE GROUP[*] (
       ID BIGINT NOT NULL,
        NAME VARCHAR(255),
        PRIMARY KEY (ID)
    ) "; expected "identifier"; SQL statement:

해결 방법 1 - 데이터베이스 키워드/예약어 피하기

엔터티에 Table 어노테이션을 추가하면 테이블명을 지정해 줄 수 있다. 간단한 방법은 Table 어노테이션을 사용하여 데이터베이스 키워드/예약어 충돌을 피하는 것이다. 아래와 같이 추가하면 하이버네이트는 Group 엔터티를  groups 테이블로 생성한다.

1
2
3
4
5
@Entity
@Table(name = "groups")
public class Group {
    // ...
}

하이버네이트가 실행한 SQL은 아래와 같다.

1
2
3
4
5
6
Hibernate:
    create table groups (
       id bigint not null,
        name varchar(255),
        primary key (id)
    )

엔터티 클래스명은 단수로 하고 실제 매핑될 테이블명은 복수하고 싶은 경우가 있다. Table 어노테이션으로 모든 테이블을 일일이 바꿔주는 것은 귀찮은 일이다. 하이버네이트의 이름 전략Naming Strategy을 확장하여 한 번에 처리할 수 있다. 자세한 내용은 아래 링크에서 확인할 수 있다.

  • https://www.baeldung.com/hibernate-naming-strategy

해결 방법 2 - GLOBALLY_QUOTED_IDENTIFIERS

다른 방법으로는 하이버네이트 설정에 'GLOBALLY_QUOTED_IDENTIFIERS'을 활성화하는 것이다. 이 값을 활성화 시키면 하이버네이트가 SQL 실행 시 인용 부호로 테이블과 컬럼을 감싸서 실행한다. 따라서 엔터티 클래스명을 Group을 사용하더라도 데이터베이스에서는 키워드/예약어로 인지하지 않기 때문에 엔터티 클래스 이름을 그대로 사용할 수 있다.

하이버네이트 설정 파일을 persistence.xml로 사용하는 경우 'hibernate.globally_quoted_identifiers' 프로퍼티로 아래와 같이 추가한다.

1
2
3
4
5
6
7
8
9
10
<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">
        <properties>
            <!-- ... -->
            <property name="hibernate.globally_quoted_identifiers" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

스프링 부트 JPA를 사용하는 경우 application.properties에 아래와 같이 추가한다.

1
spring.jpa.properties.hibernate.globally_quoted_identifiers=true

하이버네이트가 실행한 SQL은 아래와 같다.

1
2
3
4
5
6
Hibernate:
    create table "Group" (
       "id" bigint not null,
        "name" varchar(255),
        primary key ("id")
    )

마치며

데이터베이스 스키마 자동 생성 기능은 매우 편리한 기능이지만 운영에서 쓰기에는 적합하지 않다. 왜냐하면 스키마 형상 관리가 제대로 되지 않기 때문이다. 따라서 필자는 빠르게 동작하는 코드를 만들거나 개발 초기에 데이터 모델이 불명확할 때만 사용한다.

필자는 이런 용도로 쓰기 때문에  'GLOBALLY_QUOTED_IDENTIFIERS' 를 선호한다. 왜냐하면 데이터베이스 키워드/예약어를 일일이 확인해 가며 엔터티를 만들고 싶지 않기 때문이다.


Popit은 페이스북 댓글만 사용하고 있습니다. 페이스북 로그인 후 글을 보시면 댓글이 나타납니다.