Flyway
Flyway는 DB의 형상관리를 목적으로 하는 툴이다.
1. DB Migration 필요한 것일까?
배포를 했다고 가정을 해보자,
유지 보수 중 스키마 구조가 바뀌게 된다면 그 상황에서 어떻게 대처할 것인가 ?
예제를 통해 알아보겠습니다.
현재 유지보수 중인 Member Entity가 다음과 같다고 가정하자
package com.example.dockercomposetest.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
@Entity
@Getter
@Setter
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
}
하지만, 새로운요구사항이 추가되었다. hello_new_column필드를 추가해야한다.
그렇다면 Entity에서는 단순히 Private Integer hello_new_column을 추가하면 된다.
하지만 실제 DB상황에서 변화를 어떻게 처리할 것인가 ??? 가 문제된다.
방법1. DB서버에 들어가 테이블 직접수정한다.
해당 DB에 접속하여 다음의 스키마를 입력해준다 하지만 단점이 존재함,
ALTER TABLE Member ADD COLUMN hello_new_column INTEGER DEFAULT 0;
-실수하기 좋다.
-작업하기 번거롭다
-만약 데이터가 미리있고,Entity쪽만 변경(hello_new_column 추가), DB스키마를 수정하지 않았다면?
아래와 같은 에러가 발생한다.
Caused by: java.sql.SQLSyntaxErrorException: Unknown column
방법2. Flyway 툴을 사용하자
궁금적인 목표
- DB에 접속해서 TABLE을 직접 건들지 않으며, hello_new_column 컬럼 포함한 신규데이터를 저장한다.
-이전 컬럼이 포함되지 않은 데이터는 필드를 0으로 초기화한다.
-git으로 관리한다-> 파일로 관리할 수 있어야한다.
Flyway 사용법
1. 의존성 추가 build.gradle
dependencies {
implementation('org.flywaydb:flyway-core:6.4.2')
}
2. 어플리케이션 설정 추가: application.yml
spring:
flyway:
enabled: true
baselineOnMigrate: true
locations: classpath:db/migration/{vendor},classpath:db/seed
profiles: test
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:testdb;MODE=MySQL;
username: sa
password:
h2:
console:
enabled: true
jpa:
database: h2
database-platform: com.ss.camper.config.CustomMySQL5Dialect
generate-ddl: false
open-in-view: true
hibernate:
ddl-auto: validate
3. init 파일 추가: resources/db/migration/
등록하려는 시점의 DB 스키마 구조를 입력해야 한다.
이 파일을 기준으로 flyway가 DB버젼 관리를 하게된다.
버전관리는 다음과 같이 V(숫자)__{설명}.sql으로 생각하면 된다.
V1__init_table.sql
DROP TABLE IF EXISTS Member;
CREATE TABLE member (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL
);
엔티티변경
V2__add_new_column.sql
ALTER TABLE Member ADD COLUMN hello_new_column INTEGER DEFAULT 0;
버전관리는 어떻게 하는가?
Prefix
- V(Versioned): 현재버전을 새로운 버전으로 업데이트
- U(Undo): 현재 버전을 이전버전으로 되돌리는 경우
- R(Repeatable): 버전에 관계 없이 매번 실행하는 경우
Version
스크립트의 버전을 이전보다 꼭 높게 적어야 한다.
- V와 U는 버전을 명시한다.
- R은 버전을 명시하지 않는다. (매번 실행되기 때문이다)
Separator
무조건 __(언더바 2개)로 작성
Description
스크립트 내용에 맞게 자유롭게 적는다.
단어 구분은 _(언더바 1개)로 한다.
트러블슈팅
테스트 환경에서의 Flyway 실패
1. h2 테스트 환경
- h2에서 jdbc:h2:mem:testdb;MODE=MYSQL 을 적용
- V1__init.sql의 engine=InnoDB default charset utf8mb4; 부분에서 에러가 발생함
해결방법
- h2 문법으로 적용하면 성공이 됨 → 스키마를 h2, MySQL 둘다 관리해야 하기 때문에 적용X
- 로컬 test DB를 MySQL로 변경
2. MySQL 테스트 환경
- h2에서 MySQL로 마이그레이션 적용
- 테스트 격리가 안되는 현상 발생 (기존 테스트 실패)
해결방법
- 프로덕션 코드는 build가 통과한다. → 엔티티와 DB스키마가 일치한다 → 테스트에 Flyway 스키마를 적용할 필요가 없다
- Flyway + MySQL : 배포서버, 로컬서버 프로덕션 코드
- h2 : 배포서버, 로컬서버 테스트 코드
3. 테스트 코드 yml 설정
- flyway.enabled=false를 꼭 설정하자
4. 42001 에러
기존 테이블 내용과 새롭게 추가된 스키마 사이 추가되는 과정에서 중복된 내용이 있거나 관리되는 스키마 내용이 다를 때 에러가 발생한다.
- drop table flyway_schema_history 를 적용, ddl auto를 validate 한 뒤 실행해보자.