본문 바로가기

스터디 정리

[JPA] 데이터베이스 스키마 자동생성

JPA의 설정 옵션 중에 hibernate.hbm2ddl.auto 라는 것이 있다. 이게 무슨 설정이나면 JPA에서 제공하고 있는 애노테이션으로 매핑이 되어있는 테이블들을 데이터베이스 방언(dialect)을 활용하여 애플리케이션 실행시점에 자동으로 DB에 생성해주는 것이다.

옵션 설명
create 기존테이블 삭제 후 다시 생성 (DROP + CREATE)
create-drop create와 같으나 종료시점에 테이블 DROP
update 변경분만 반영(운영DB에는 사용하면 안됨)
validate 엔티티와 테이블이 정상 매핑되었는지만 확인
none 사용하지 않음

이제 이 옵션들을 하나하나 어떻게 실행되는지 확인해보겠다.
예제를 간단히 하기 위해 Member, Item 객체만 생성하여 돌려보았다.

create

콘솔화면부터 확인할 수 있듯이 애플리케이션을 실행하면 모든 테이블을 DROP 한 후 새롭게 테이블을 생성한다. 따라서 이 옵션을 사용하여 애플리케이션을 시작하면 이전에 추가했던 데이터가 다 삭제되어 버린다는 것을 의미한다.

 

※콘솔에서 나타난 ddl 부분 이외의 내용은 생략하였고, 애플리케이션 시작, 실행, 종료 세단계로 표시하여 어느단계에 테이블 생성, 삭제가 되는지 구분하였습니다.

 

console

<시작>
Hibernate: 

    drop table Item if exists
Hibernate: 

    drop table Member if exists
Hibernate: 

    drop sequence if exists hibernate_sequence
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: 

    create table Item (
       ITEM_ID bigint not null,
        ITEM_NAME varchar(255),
        primary key (ITEM_ID)
    )
Hibernate: 

    create table Member (
       MEMBER_ID bigint not null,
        USERNAME varchar(255),
        primary key (MEMBER_ID)
    )

<실행>

<종료>

create-drop

이름만 봐도 감이 온다. create가 drop->create 였다면 이 옵션은 애플리케이션 시작할 때 테이블을 create 했다가 종료시점에 drop하여 테이블을 다 날려버린다. create 옵션과 마찬가지로 애플리케이션을 종료하면 그동안 추가되었던 데이터가 다 삭제되어버린다는 점을 주의해야한다.

 

console

<시작>
Hibernate: 

    drop table Item if exists
Hibernate: 

    drop table Member if exists
Hibernate: 

    drop sequence if exists hibernate_sequence
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: 

    create table Item (
       ITEM_ID bigint not null,
        ITEM_NAME varchar(255),
        primary key (ITEM_ID)
    )
Hibernate: 

    create table Member (
       MEMBER_ID bigint not null,
        USERNAME varchar(255),
        primary key (MEMBER_ID)
    )

<실행> 

<종료>
Hibernate: 

    drop table Item if exists
Hibernate: 

    drop table Member if exists
Hibernate: 

    drop sequence if exists hibernate_sequence

update

아래의 엔티티 2개가 있다고 가정하고, 정말 변경분만 반영이 되는지 확인해보겠다.

@Entity
public class Item {

    @Id
    @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;

    @Column(name = "ITEM_NAME")
    private String name;

    ... (getter, setter 생략)

}
@Entity
public class Member {

    @Id
    @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USER_NAME")
    private String name;

    ... (getter, setter 생략)

}

update로 설정하고 데이터베이스에 아무런 테이블이 없는 상태에서 애플리케이션을 처음 실행했다.

 

console

<시작>
Hibernate: 

    create table Item (
       ITEM_ID bigint not null,
        ITEM_NAME varchar(255),
        primary key (ITEM_ID)
    )
Hibernate: 

    create table Member (
       MEMBER_ID bigint not null,
        USERNAME varchar(255),
        primary key (MEMBER_ID)
    )
Hibernate: create sequence hibernate_sequence start with 1 increment by 1

<실행>

<종료>

데이터베이스가 새로 생성이 되었다.
똑같은 내용을 다시 한번 실행해보았다.

<시작>

<실행>

<종료>

테이블관련 아무런 쿼리가 없다.
이번엔 Member 엔티티에 address 컬럼을 추가하고 실행해보았다.

@Entity
public class Member {
    @Id
    @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String name;

    @Column
    private String address;

    ... (getter, setter 생략)
}

 

console

<시작>
Hibernate: 

    alter table Member 
       add column address varchar(255)

<실행>

<종료>

변경했던 내용만 업데이트 된것을 확인할 수 있다.
테이블이 생성되었다가 삭제가 된다던지 하는 내용은 없고, 기존 데이터도 유지된다.

validate

테이블이 정상적으로 매핑 되었는지만 확인해준다. 따라서 위의 옵션들처럼 테이블을 조작하는 쿼리를 날리지 않는다.

validate로 설정한 뒤 데이터베이스에 아무런 테이블이 만들어지지 않은 상태에서 한번 돌려보았다.

 

console

<시작>
Exception in thread "main" javax.persistence.PersistenceException: [PersistenceUnit: hello] Unable to build Hibernate SessionFactory
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:1016)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:942)
    at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:56)
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:79)
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54)
    at hellojpa.JpaMain.main(JpaMain.java:10)
Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [Item]
    at org.hibernate.tool.schema.internal.AbstractSchemaValidator.validateTable(AbstractSchemaValidator.java:121)
    at org.hibernate.tool.schema.internal.GroupedSchemaValidatorImpl.validateTables(GroupedSchemaValidatorImpl.java:42)
    at org.hibernate.tool.schema.internal.AbstractSchemaValidator.performValidation(AbstractSchemaValidator.java:89)
    at org.hibernate.tool.schema.internal.AbstractSchemaValidator.doValidation(AbstractSchemaValidator.java:68)
    at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:191)
    at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:72)
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:310)
    at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:467)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:939)
    ... 4 more

예외가 발생한다. 읽어보니 Schema-validate: missing table [Item] 이라고 한다. Item 테이블이 없다고 말하는 것 같다.

테이블을 정상적으로 생성을 하고 다시 돌려보았다.

 

console

<시작>

<실행>

<종료>

실행이 잘 된다. 이렇게 자신이 테이블을 조작하지 않고, 테이블 정보가 JPA 엔티티 매핑정보와 잘 맞는지 확인해주는 옵션이다.

none

위의 옵션들 모두 사용하고 싶지 않다면 none으로 설정해놓으면 된다. 이름 그대로 매핑정보를 확인해주거나 대신 조작해주거나 하지 않는다.




hibernate.hbm2ddl.auto 옵션들을 하나하나 살펴보았다.

사용해보면 엇, create 옵션을 사용하니까 테이블이 저절로 생성되니까 편한데? 개발할 때 이걸로 써야겠다.

그러나 김영한 개발자님이 단호하게 하신 말씀이 있으니...

 

 

운영장비에는 절대 create, create-drop, update를 사용하면 안된다.

권장

  • 개발 초기: create or update
  • 테스트 서버: update or validate
  • 스테이징과 운영 서버: validate or none

 

운영단계에서 데이터가 날라가는 것은 기업에게 어마어마한 손실을 안겨주는 것이다.
실무에서 한 팀원이 자신의 로컬에만 쓰자하고 테이블이 자동 삭제, 생성되는 옵션을 설정해서 썼다가 그대로 개발단계로 커밋하여 실행해보니 테이블에 데이터가 다 날라가버리는 사건이 종종 발생된다고 한다.

따라서 엄청 주의해야하고, 실무에서는 웬만하면 이 옵션을 사용하지 말고, 사용하더라도 validate 정도를 사용하는 것이 좋다.

 

하지만 프로젝트 완전 초기단계에서는 데이터베이스에 어떻게 생성되는지 확인해보고 또 구조도 바뀌는 일이 많아서 이 옵션을 사용하면 편리하다. 하지만 운영 단계에서는 이 옵션이 사용되지 않도록 다시 한번 강조해야한다.



여기서 질문 하나!

Q. create 옵션 사용하면 콘솔에 테이블 생성 쿼리 찍히는데 이대로 사용해도 무방한가요?
A. 쿼리를 그대로 사용하면 안된다. 이 쿼리를 참고해서 생성해도 되지만 쿼리를 다듬어서 생성하는 것이 좋다.

 

 

하지만 개인프로젝트할 때 이 쿼리대로 사용해봤는데 연관관계 매핑도 설정이 자동으로 되고, 컬럼 조건도 알아서 설정해주고, 순서도 따로 생각할 필요가 없어서 엄청 편리하긴 했다...