-
스프링 부트에서 다음 의존성을 추가한다.
build.gradle
buildscript {
ext {
springBootVersion = '1.5.8.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
group = 'io.honeymon'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.flywaydb:flyway-core')
compile('org.springframework.boot:spring-boot-starter-web')
runtime('com.h2database:h2')
compileOnly('org.projectlombok:lombok')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
application.yml
spring:
datasource:
url: jdbc:h2:file:~/demo;DB_CLOSE_ON_EXIT=FALSE;
username: sa
password:
jpa:
hibernate:
ddl-auto: validate // (1)
h2:
console:
enabled: true // (2)
flyway:
enabled: true // (3)
-
ddl 실행옵션으로 (none, update, validate, create, create-drop) 이 있다.
-
h2-console 실행조건으로 기본적으로는 `false`로 되어 있다.
-
flyway 활성화 설정.
true
가 기본
Flyway가 활성화되면, 애플리케이션의 DataSource 를 이용해서 지정된 DB 내에 schema_version
이라고 테이블을 생성한다.
spring:
datasource:
url: jdbc:h2:file:~/demo;DB_CLOSE_ON_EXIT=FALSE;
username: sa
password:
jpa:
hibernate:
ddl-auto: validate
flyway:
enabled: true
version_rank installed_rank version description type script checksum installed_by installed_on execution_time success
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class User extends AbstractPersistable<Long> {
@Column(unique = true)
private String email;
private String name;
private String desc;
public User(String email) {
Assert.hasText(email, "Email is required.");
this.email = email;
}
}
--
-- V1: Create USER TABLE
--
--
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`email` varchar(255) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`desc` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_demo_email` (`email`),
) DEFAULT CHARSET=utf8;
public class User extends AbstractPersistable<Long> {
// 생략
private String descr; // desc -> descr
// 생략
}
spring.jpa.hibername.ddl-auto: validate
상태에서는 엔티티와 DB 엔티티 구조가 달라서 다음과 같이 컬럼을 찾을 수 없다고 에러가 발생
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:954)
// 생략
Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing column [descr] in table [user]
at org.hibernate.tool.schema.internal.SchemaValidatorImpl.validateTable(SchemaValidatorImpl.java:85) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.tool.schema.internal.SchemaValidatorImpl.doValidation(SchemaValidatorImpl.java:50) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.tool.hbm2ddl.SchemaValidator.validate(SchemaValidator.java:91) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:475) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:444) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:879) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
... 27 common frames omitted
위에서 변경했던 descr
필드를 desc
필드로 변경한 후에
--
-- V1: Create USER TABLE
--
--
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`email` varchar(255) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`descr` varchar(255) DEFAULT NULL, // (1)
PRIMARY KEY (`id`),
UNIQUE KEY `UK_demo_email` (`email`),
) DEFAULT CHARSET=utf8;
-
desc
→descr
로 변경
다음과 같은 에러가 발생한다.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Invocation of init method failed; nested exception is org.flywaydb.core.api.FlywayException: Validate failed. Migration Checksum mismatch for migration 1
-> Applied to database : -1341614813
-> Resolved locally : 158332526
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)
// 생략
Caused by: org.flywaydb.core.api.FlywayException: Validate failed. Migration Checksum mismatch for migration 1
-> Applied to database : -1341614813
-> Resolved locally : 158332526
at org.flywaydb.core.Flyway.doValidate(Flyway.java:1108) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.Flyway.access$300(Flyway.java:62) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.Flyway$1.execute(Flyway.java:1012) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.Flyway$1.execute(Flyway.java:1006) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.Flyway.execute(Flyway.java:1418) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.Flyway.migrate(Flyway.java:1006) ~[flyway-core-3.2.1.jar:na]
at org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer.afterPropertiesSet(FlywayMigrationInitializer.java:66) ~[spring-boot-autoconfigure-1.5.8.RELEASE.jar:1.5.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687) ~[spring-beans-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624) ~[spring-beans-4.3.12.RELEASE.jar:4.3.12.RELEASE]
... 23 common frames omitted
flyway 에서 DB에 생성한 schema_version`에 등록되어 있는 해시값과 달라지면 다음과 같이 오류가 발생한다.
이 때에는 `V1init.sql
을 원래대로 복원하고 새로운 파일(예: V2
modify_column.sql
)을 생성하여 변경사항을 반영해야한다.
V1
에서 V3
의 스크립트를 실행하면 정상적으로 실행이 된다.
.V3__add_job_at_user.sql
ALTER TABLE user ADD COLUMN `job` VARCHAR(50) DEFAULT NULL;
그 후 V2
파일을 생성하면 다음과 같은 오류가 발생한다.
.V2__add_company_at_user.sql
ALTER TABLE user ADD COLUMN `company` VARCHAR(50) DEFAULT NULL;
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Invocation of init method failed; nested exception is org.flywaydb.core.api.FlywayException: Validate failed. Detected resolved migration not applied to database: 2
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)
// 생략
Caused by: org.flywaydb.core.api.FlywayException: Validate failed. Detected resolved migration not applied to database: 2
at org.flywaydb.core.Flyway.doValidate(Flyway.java:1108) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.Flyway.access$300(Flyway.java:62) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.Flyway$1.execute(Flyway.java:1012) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.Flyway$1.execute(Flyway.java:1006) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.Flyway.execute(Flyway.java:1418) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.Flyway.migrate(Flyway.java:1006) ~[flyway-core-3.2.1.jar:na]
at org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer.afterPropertiesSet(FlywayMigrationInitializer.java:66) ~[spring-boot-autoconfigure-1.5.8.RELEASE.jar:1.5.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687) ~[spring-beans-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624) ~[spring-beans-4.3.12.RELEASE.jar:4.3.12.RELEASE]
... 23 common frames omitted
V2
를 V4
로 변경하면 정상적으로 수행된다.
ALTER TABLE user ADD COLUMNS `note` VARCHAR(2000) DEFAULT NULL; // (1)
-
COLUMN
를COLUMNS
로 변경하여 오류가 발생하도록 했다.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Invocation of init method failed; nested exception is org.flywaydb.core.internal.dbsupport.FlywaySqlScriptException:
Migration V5__add_note_at_user.sql failed
-----------------------------------------
SQL State : HY004
Error Code : 50004
Message : Unknown data type: "NOTE"; SQL statement:
ALTER TABLE user ADD COLUMNS `note` VARCHAR(2000) DEFAULT NULL [50004-196]
Location : db/migration/V5__add_note_at_user.sql (/Users/jake/workspace/demo/out/production/resources/db/migration/V5__add_note_at_user.sql)
Line : 1
Statement : ALTER TABLE user ADD COLUMNS `note` VARCHAR(2000) DEFAULT NULL
다시 실행하면 메시지는 다음과 같이 변경된다.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Invocation of init method failed; nested exception is org.flywaydb.core.api.FlywayException: Schema "PUBLIC" contains a failed migration to version 5 !
// 생략
Caused by: org.flywaydb.core.api.FlywayException: Schema "PUBLIC" contains a failed migration to version 5 !
at org.flywaydb.core.internal.command.DbMigrate$2.doInTransaction(DbMigrate.java:196) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.internal.command.DbMigrate$2.doInTransaction(DbMigrate.java:156) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.internal.util.jdbc.TransactionTemplate.execute(TransactionTemplate.java:72) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.internal.command.DbMigrate.migrate(DbMigrate.java:156) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.Flyway$1.execute(Flyway.java:1059) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.Flyway$1.execute(Flyway.java:1006) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.Flyway.execute(Flyway.java:1418) ~[flyway-core-3.2.1.jar:na]
at org.flywaydb.core.Flyway.migrate(Flyway.java:1006) ~[flyway-core-3.2.1.jar:na]
at org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer.afterPropertiesSet(FlywayMigrationInitializer.java:66) ~[spring-boot-autoconfigure-1.5.8.RELEASE.jar:1.5.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687) ~[spring-beans-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624) ~[spring-beans-4.3.12.RELEASE.jar:4.3.12.RELEASE]
... 23 common frames omitted
데이터베이스를 보면 다음과 같이 되어 있는 것을 볼 수 있다. V5__add_note_at_user.sql
의 success
가 FALSE
로 되어 있는 것을 확인할 수 있다. 스크립트를 실행 중에 실패했다는 것을 의미한데. V6
버전을 올려도 V5
가 실패하면 V6
가 실행되지 않기 때문에 V5
를 정상적으로 동작하도록 수정을 한 후에 schema_version
테이블에서 실패한 컬럼을 지우고 다시 실행해야 한다.
version_rank installed_rank version description type script checksum installed_by installed_on execution_time success
1 1 1 init SQL V1__init.sql -1341614813 SA 2017-11-03 14:47:55.508 6 TRUE
2 2 3 add job at user SQL V3__add_job_at_user.sql -1714421583 SA 2017-11-03 14:47:55.529 13 TRUE
3 3 4 add company at user SQL V4__add_company_at_user.sql -297101317 SA 2017-11-03 14:47:55.544 7 TRUE
4 4 5 add note at user SQL V5__add_note_at_user.sql -1599470887 SA 2017-11-03 14:47:55.556 5 FALSE
Flyway 에서 변경이력을 관리하기 위한 발급번호는 다음에서 확인이 가능하다.
DELETE FROM "schema_version" WHERE "version_rank" = 4;
실패한 스크립트를 제거하고 스크립트의 오류를 수정하자.
ALTER TABLE user ADD COLUMN `note` VARCHAR(2000) DEFAULT NULL;
애플리케이션을 다시 실행하면 schema_version
테이블의 내용은 다음과 같이 변경될 것이다.
version_rank installed_rank version description type script checksum installed_by installed_on execution_time success
1 1 1 init SQL V1__init.sql -1341614813 SA 2017-11-03 14:47:55.508 6 TRUE
2 2 3 add job at user SQL V3__add_job_at_user.sql -1714421583 SA 2017-11-03 14:47:55.529 13 TRUE
3 3 4 add company at user SQL V4__add_company_at_user.sql -297101317 SA 2017-11-03 14:47:55.544 7 TRUE
4 4 5 add note at user SQL V5__add_note_at_user.sql -1402017088 SA 2017-11-03 15:25:04.175 22 TRUE