Table of contents
스프링부트가 지원하는 데이터베이스 마이그레이션 툴과의 연동에 대해서 살펴보자.
데이터베이스 마이그레이션
데이터베이스 마이그레이션 툴은 스키마 변경이나 데이터 변경에 대한 이력을 버전관리시스템을 사용하듯이 관리할 수 있는 툴이다.
Flyway와 Liquibase라는 제품이 대표적인데, 여기서는 Flyway에 대해서만 살펴본다.
Flyway
Flyway는 기본적으로 SQL 파일을 사용한다. 먼저 의존성을 추가하자.
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
다음으로 마이그레이션 디렉토리와 파일을 만들어야 한다.
디렉토리
- db/migration 또는 db/migration/{vendor}
- spring.flyway.locations 프로퍼티즈로 변경 가능
파일
파일명은 반드시 "V숫자__이름.sql" 와 같아야 한다.
- V는 반드시 대문자
- 숫자는 순차적으로 작성하며 타임스탬프를 이용하는 것을 권장
- 숫자와 이름 사이에 언더바 두 개가 필요
drop table if exists account;
drop sequence if exists hibernate_sequence;
create sequence hibernate_sequence start with 1 increment by 1;
create table account ( id bigint not null, password varchar(255), username varchar(255) not null, email varchar(255), primary key (id) );
테스트를 위한 도메인 클래스를 생성하자.
@Entity
public class Account {
@Id
@GeneratedValue
private Long id;
private String username;
private String password;
private String email;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Account account = (Account) o;
return Objects.equals(id, account.id) &&
Objects.equals(username, account.username) &&
Objects.equals(password, account.password);
}
@Override
public int hashCode() {
return Objects.hash(id, username, password);
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
우선 데이터베이스 스키마를 비워둔 상태에서 하이버네이트의 ddl-auto를 validate로 설정하여 검증만 하도록 한다.
spring.jpa.generate-dll=false
spring.jpa.hibernate.ddl-auto=validate
정상적인 테스트를 하기 전에, 스키마가 없는 상태이므로 하이버네이트에서 스키마 검증에 대한 에러를 발생하는지 테스트하기 위해서 V1__init.sql에서 v를 소문자로 변경한 뒤 애플리케이션을 실행해보자. Flyway에서 디렉토리가 존재하지 않으면 에러가 발생하기 때문에 resources/db/migration 폴더는 존재해야 한다.
디렉토리가 없는 경우 다음과 같은 에러로그가 출력된다.
Cannot find migrations location in: [classpath:db/migration] (please add migrations or check your Flyway configuration)
데이터베이스 스키마에 테이블이 없고, 스키마를 생성하기 위한 쿼리가 실행되지 않기 때문에 하이버네이트에서 검증을 하다가 에러가 발생한다.
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [account]
다시 v를 대문자로 변경하여 Falyway가 정상적으로 해당 쿼리를 실행할 수 있도록 해보자. 애플리케이션을 실행하면 컨텍스트를 띄우면서 Falyway를 실행한다. Falyway가 해당 쿼리를 실행하여 스키마를 생성하고 하이버네이트가 스키마를 검증한 뒤, 에러 없이 애플리케이션이 실행된다.
DBMS에 접속헤서 스키마를 확인해보면 2개의 테이블이 생성된 것을 알 수 있다. 하나는 Flyway가 정보를 관리하는 테이블이며, 다른 하나는 Flyway 스키마 생성 쿼리를 실행하여 생성된 테이블이다.
Flyway가 정보를 관리하는 flyway_schema_history 테이블에서 버전, 타입, 이름, 성공 여부 등을 알 수 있다.
이번에는 새로운 컬럼을 추가해보자.
package jch.inflearn.springbootjpa.account;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.Objects;
@Entity
public class Account {
@Id
@GeneratedValue
private Long id;
private String username;
private String password;
private String email;
// add
private boolean active;
//... getter, setter 등
// add
public boolean isActive() {
return active;
}
// add
public void setActive(boolean active) {
this.active = active;
}
}
ddl-auto가 validate로 설정되었기 때문에 하이버네이트에서 매핑이 되지 않는 컬럼에 대한 검증 에러가 발생한다.
이를 해결하기 위해서는 새로운 sql 파일을 추가해야한다. 기존의 V1__init.sql는 이미 적용이 된 상태이기 때문에 절대 수정하거나 삭제해서는 안된다.
V2__add_active.sql 라는 이름의 파일을 생성하자.
ALTER TABLE account ADD COLUMN active BOOLEAN;
다시 애플리케이션을 실행하면 Flyway가 두번째 스크립트를 적용하고 하이버네이트가 검증을 완료하여 이상이 없으므로 정상적으로 실행된다. DBMS로 접속해서 스키마를 확인해보자. account 테이블에는 active 컬럼이 추가되었고, flyway_schema_history 테이블에는 이력이 추가된 것을 알 수 있다.
Flyway에서 사용하는 스크립트 파일에는 스키마 관리 뿐 아니라 데이터도 추가/수정/삭제할 수 있다.
해당 포스팅은 스프링 부트 개념과 활용 강의 내용을 토대로 작성하였습니다.