[Spring Boot #26] Flyway를 이용한 데이터 마이그레이션

2021. 3. 25. 01:17 Spring Framework/Spring boot #2

| Flyway란?

  • Flyway는 오픈소스 마이그레이션 툴입니다. 
  • 자바나 c++같은 프로그램의 소스 코드는 svn, git과 같은 형상관리 툴로 쉽게 관리할 수 있지만 테이블의 스키마나 데이터는 위와 같은 툴로 변경이력을 관리할 수 없습니다. 따라서 SQL 스크립트문을 실행하거나 직접 DB 콘솔이나 Toad 같은 툴을 통해 직접 수동으로 처리해줘야 하는 단점이 있습니다.
  • Flyway는 버전 관리 목적인 SCHEMA_VERSION 테이블을 통해 SQL 스크립트의 변화를 추적하면서 자동적으로 관리하는 방법으로 위와 같은 문제를 해결합니다.

| 스프링 부트에서 Flyway 사용하기

 

의존성 추가

<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>

 

프로젝트 구조

├── pom.xml
├── spring-boot-tutorial.iml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── tutorial
│   │   │           └── springboottutorial
│   │   │               ├── Account.java
│   │   │               ├── AccountRepository.java
│   │   │               └── SpringBootTutorialApplication.java
│   │   └── resources
│   │       ├── application.properties
│   │       ├── db
│   │       │   └── migration
│   │       │       ├── V1__init.sql
│   │       └── templates

 

  • resource 디렉터리에 db.migration 디렉터리를 추가해서 V1__init.sql 파일을 만듭니다. 이 파일은 Flyaway가 관리하는 SCHEMA_VERSION역할을 하는 테이블이 됩니다. 
  • 위 SCHEMA_VERSION의 역할을 하는 테이블은 꼭 따라야하는 네이밍 컨벤션이 있습니다. 그에 관련된 내용은 여기 링크에서 확인하시면 더 자세히 나와있습니다. 

 

V1__init.sql

--V1_init.sql
drop table account if exists;
drop sequence if exists hibernate_sequence;
create sequence hibernate_sequence start with 1 increment by 1;
create table account (id bigint not null, email varchar(255), password varchar(255), username varchar(255), primary key (id));
  • 파일에 작성되어 있는 SQL문을 가지고 버전관리를 하게 됩니다.

 

application.properties

# application.properties
spring.datasource.hikari.maximum-pool-size=4

spring.datasource.url=jdbc:postgresql://localhost:5432/springboot
spring.datasource.username=saelobi
spring.datasource.password=pass

# 드라이버가 createClub을 지원하지 않아서 warning 뜨는 것을 방지
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true

spring.jpa.hibernate.ddl-auto=validate
spring.jpa.generate-ddl=false
spring.jpa.show-sql=true

 

소스 코드

@Entity
public class Account {

    @Id
    @GeneratedValue
    private Long id;

    private String username;

    private String password;

    private String email;

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = 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 interface AccountRepository extends JpaRepository<Account, Long> {

    Optional<Account> findByUsername(String username);
}
@SpringBootApplication
public class SpringBootTutorialApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootTutorialApplication.class, args);
    }
}

 

다음 postgres DB를 docker를 통해 구동합니다. 구동 방법은 아래 포스팅을 참조하시면 됩니다.

 

[Spring Framework/Spring boot2] - [Spring Boot #23] 스프링 부트 PostgreSQL 연동하기

 

위 프로젝트를 실행하게 되면 Flyway 어플리케이션이 실행되면서 V1__init.sql에 있는 SQL문을 실행한 뒤 postgres DB에 스키마를 생성하게 됩니다. 따라서 엔티티와 테이블간의 매핑이 이루어져 Spring-Data-JPA에 의한 유효성 검사도 자연스럽게 통과가 됩니다.

 

List of relations
Schema |         Name          | Type  |  Owner
--------+-----------------------+-------+---------
public | account               | table | saelobi
public | flyway_schema_history | table | saelobi
(2 rows)

flyway_schema_history는 Flyway가 SCHEMA_VERSION 테이블을 실행했을 시 관련 히스토리 정보를 관리하는 테이블입니다.

springboot=# select * from flyway_schema_history;
installed_rank | version | description | type |    script    |  checksum  | installed_by |        installed_on        | execution_time | success
----------------+---------+-------------+------+--------------+------------+--------------+----------------------------+----------------+---------
1 | 1       | init        | SQL  | V1__init.sql | 1467134063 | saelobi      | 2019-01-11 01:42:22.067858 |             25 | t
(1 row)

 

| Flyway를 사용하여 스키마 변경이력 관리하기

 

이제 엔티티 역할을 하는 Account 클래스에 변수를 하나 추가합니다.

@Entity
public class Account {

    @Id
    @GeneratedValue
    private Long id;

    private String username;

    private String password;

    private String email;

    private boolean active;

    public String getEmail() {
        return email;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }

    public void setEmail(String email) {
        this.email = 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);
    }
}

 

만일 이 상태로 어플리케이션을 실행했을 경우 DB에 있는 account 테이블과 매핑이 되지 않기 때문에 유효성 검사에서 에러를 내게 되어 어플리케이션이 제대로 구동되지 않을 것입니다.

 

따라서 이러한 변경사항을 반영할 수 있도록 Flyway의 SCHEMA_VERSION sql 파일을 하나 추가해야합니다. 한 번 적용이 된 V1__init.sql 파일은 절대로 변경해서는 안됩니다.

│   │       ├── db
│   │       │   └── migration
│   │       │       ├── V1__init.sql
│   │       │       └── V2__add_active.sql

 

V2_add_active.sql

ALTER TABLE account ADD COLUMN active BOOLEAN;

 

어플리케이션을 실행하면 다음과 같이 DB에 정상적으로 SQL문이 반영된 것을 알 수 있습니다.

springboot=# select * from account;
id | email | password | username | active
----+-------+----------+----------+--------
(0 rows)

springboot=# select * from flyway_schema_history;
installed_rank | version | description | type |       script       |  checksum  | installed_by |        installed_on        | execution_time | success
----------------+---------+-------------+------+--------------------+------------+--------------+----------------------------+----------------+---------
1 | 1       | init        | SQL  | V1__init.sql       | 1467134063 | saelobi      | 2019-01-11 01:42:22.067858 |             25 | t
2 | 2       | add active  | SQL  | V2__add_active.sql | 1099198921 | saelobi      | 2019-01-11 01:58:32.238285 |              4 | t
(2 rows)

 

참고자료 : https://www.inflearn.com/course/스프링부트



출처: https://engkimbs.tistory.com/795?category=767865 [새로비]