Database Migrations with Flyway – 用Flyway进行数据库迁移

最后修改: 2016年 9月 29日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this tutorial, we’ll explore key concepts of Flyway and how we can use this framework to continuously remodel our application’s database schema reliably and easily. In addition, we’ll present an example of managing an in-memory H2 database using a Maven Flyway plugin.

在本教程中,我们将探讨Flyway的关键概念,以及我们如何利用这个框架可靠、轻松地持续重塑我们应用程序的数据库架构。此外,我们将介绍一个使用Maven Flyway插件管理内存H2数据库的例子。

Flyway updates a database from one version to the next using migrations. We can write migrations either in SQL with database-specific syntax, or in Java for advanced database transformations.

Flyway使用迁移将数据库从一个版本更新到下一个版本。我们可以使用数据库特定语法的SQL编写迁移,也可以使用Java编写高级数据库转换。

Migrations can either be versioned or repeatable. The former has a unique version and is applied exactly once. The latter doesn’t have a version. Instead, they are (re-)applied every time their checksum changes.

迁移可以是版本的,也可以是可重复的。前者有一个唯一的版本,并被精确地应用一次。后者则没有一个版本。相反,每当它们的校验和发生变化时,它们就被(重新)应用。

Within a single migration run, repeatable migrations are always applied last, after pending versioned migrations have been executed. Repeatable migrations are applied in order of their description. For a single migration, all statements are run within a single database transaction.

在一次迁移运行中,可重复迁移总是在执行完待定的版本迁移后最后进行。可重复迁移是按照其描述的顺序进行的。在一次迁移中,所有的语句都在一个数据库事务中运行。

In this tutorial, we’ll mainly focus on how we use the Maven plugin to perform database migrations.

在本教程中,我们将主要讨论如何使用Maven插件来进行数据库迁移。

2. Flyway Maven Plugin

2.Flyway Maven插件

To install a Flyway Maven plugin, let’s add the following plugin definition to our pom.xml:

要安装Flyway Maven插件,让我们在pom.xml中添加以下插件定义:</em

<plugin>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-maven-plugin</artifactId>
    <version>8.5.13</version> 
</plugin>

The latest version of the plugin is available at Maven Central.

该插件的最新版本可在Maven Central获得。

We can configure this Maven plugin in four different ways. In the following sections, we’ll go over each of these options.

我们可以通过四种不同方式配置这个Maven插件。在下面的章节中,我们将逐一介绍这些选项。

Please refer to the documentation to get a list of all configurable properties.

请参考文档以获得所有可配置属性的列表。

2.1. Plugin Configuration

2.1.插件配置

We can configure the plugin directly via the <configuration> tag in the plugin definition of our pom.xml:

我们可以直接通过<configuration>标签在我们的pom.xml的插件定义中配置该插件:

<plugin>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-maven-plugin</artifactId>
    <version>8.5.13</version>
    <configuration>
        <user>databaseUser</user>
        <password>databasePassword</password>
        <schemas>
            <schema>schemaName</schema>
        </schemas>
        ...
    </configuration>
</plugin>

2.2. Maven Properties

2.2.Maven属性

We can also configure the plugin by specifying configurable properties as Maven properties in our pom:

我们还可以通过在pom中指定可配置的属性作为Maven properties来配置该插件。

<project>
    ...
    <properties>
        <flyway.user>databaseUser</flyway.user>
        <flyway.password>databasePassword</flyway.password>
        <flyway.schemas>schemaName</flyway.schemas>
        ...
    </properties>
    ...
</project>

2.3. External Configuration File

2.3.外部配置文件

Another option is to provide plugin configuration in a separate .conf file:

另一个选择是,在一个单独的.conf文件中提供插件配置:

flyway.user=databaseUser
flyway.password=databasePassword
flyway.schemas=schemaName
...

The default configuration file name is flyway.conf and by default it loads configuration files from:

默认的配置文件名称是flyway.conf,默认情况下,它从以下地方加载配置文件:

  • installDir/conf/flyway.conf
  • userhome/flyway.conf
  • workingDir/flyway.conf

Encoding is specified by the flyway.encoding property (UTF-8 is the default one).

编码是由flyway.encoding属性指定的(UTF-8是默认的)。

If we use any other name (e.g customConfig.conf) as the configuration file, then we have to specify this explicitly when invoking the Maven command:

如果我们使用其他名称(如customConfig.conf)作为配置文件,那么我们必须在调用Maven命令时明确说明。

$ mvn -Dflyway.configFiles=customConfig.conf

2.4. System Properties

2.4.系统属性

Finally, all configuration properties can also be specified as system properties when invoking Maven on the command line:

最后,在命令行上调用Maven时,所有配置属性也可以作为系统属性指定:

$ mvn -Dflyway.user=databaseUser -Dflyway.password=databasePassword 
  -Dflyway.schemas=schemaName

The following is an order of precedence when a configuration is specified in more than one way:

当一个配置以多种方式被指定时,以下是优先顺序。

  1. System properties
  2. External configuration file
  3. Maven properties
  4. Plugin configuration

3. Example Migration

3.迁移实例

In this section, we’ll walk through the required steps to migrate a database schema to an in-memory H2 database using the Maven plugin. We use an external file to configure Flyway.

在本节中,我们将通过必要的步骤,使用Maven插件将数据库模式迁移到内存中的H2数据库。

3.1. Update POM

3.1.更新POM

First, let’s add H2 as a dependency:

首先,让我们把H2作为一个依赖项添加。

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.196</version>
</dependency>

Again, we can check the latest version of the driver available on Maven Central. We also add the Flyway plugin as explained earlier.

同样,我们可以查看Maven Central上提供的最新版本的驱动程序。我们还可以按照前面的解释添加Flyway插件。

3.2. Configure Flyway Using an External File

3.2.使用外部文件配置 Flyway

Next, we create myFlywayConfig.conf in $PROJECT_ROOT with the following content:

接下来,我们在$PROJECT_ROOT中创建myFlywayConfig.conf,内容如下。

flyway.user=databaseUser
flyway.password=databasePassword
flyway.schemas=app-db
flyway.url=jdbc:h2:mem:DATABASE
flyway.locations=filesystem:db/migration

The above configuration specifies that our migration scripts are located in the db/migration directory. It connects to an in-memory H2 instance using databaseUser and databasePassword.

上面的配置指定了我们的迁移脚本位于db/migration目录下。它使用databaseUserdatabasePassword连接到内存中的H2实例。

The application database schema is app-db.

应用程序的数据库模式是app-db

Of course, we replace flyway.user, flyway.password, and flyway.url with our own database username, database password, and database URL, respectively.

当然,我们把flyway.user, flyway.password,flyway.url分别替换成我们自己的数据库用户名、数据库密码和数据库URL。

3.3. Define First Migration

3.3.定义第一次迁移

Flyway adheres to the following naming convention for migration scripts:

Flyway遵守以下迁移脚本的命名惯例:

<Prefix><Version>__<Description>.sql

<前缀><版本>__<描述>.sql

Where:

在哪里?

  • <Prefix> – The default prefix is V, which we can change in the above configuration file using the flyway.sqlMigrationPrefix property.
  • <Version> – Migration version number. Major and minor versions may be separated by an underscore. The migration version should always start with 1.
  • <Description> – Textual description of the migration. A double underscore separates the description from the version numbers.

Example: V1_1_0__my_first_migration.sql

例子。V1_1_0__my_first_migration.sql

So let’s create a directory db/migration in $PROJECT_ROOT with a migration script named V1_0__create_employee_schema.sql containing SQL instructions to create the employee table:

因此,让我们在$PROJECT_ROOT中创建一个目录db/migration,其中有一个名为V1_0__create_employee_schema.sql的迁移脚本,包含创建雇员表的SQL指令。

CREATE TABLE IF NOT EXISTS `employee` (

    `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `name` varchar(20),
    `email` varchar(50),
    `date_of_birth` timestamp

)ENGINE=InnoDB DEFAULT CHARSET=UTF8;

3.4. Execute Migrations

3.4.执行迁移

Next, we invoke the following Maven command from $PROJECT_ROOT to execute database migrations:

接下来,我们从$PROJECT_ROOT调用以下Maven命令来执行数据库迁移。

$ mvn clean flyway:migrate -Dflyway.configFiles=myFlywayConfig.conf

This should result in our first successful migration.

这应该会导致我们的第一次成功迁移。

The database schema should now look like this:

现在的数据库模式应该是这样的。

employee:
+----+------+-------+---------------+
| id | name | email | date_of_birth |
+----+------+-------+---------------+

We can repeat the definition and execution steps to do more migrations.

我们可以重复定义和执行的步骤来做更多的迁移。

3.5. Define and Execute Second Migration

3.5.定义和执行第二次迁移

Let’s see what a second migration looks like by creating a second migration file with the name V2_0_create_department_schema.sql containing the following two queries:

让我们看看第二次迁移是什么样子的,创建第二个迁移文件,文件名为V2_0_create_department_schema.sql,包含以下两个查询。

CREATE TABLE IF NOT EXISTS `department` (

`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` varchar(20)

)ENGINE=InnoDB DEFAULT CHARSET=UTF8; 

ALTER TABLE `employee` ADD `dept_id` int AFTER `email`;

Then we’ll execute a similar migration like we did the first time.

然后,我们将像第一次那样执行类似的迁移。

Now our database schema has changed to add a new column to employee and a new table:

现在我们的数据库模式已经改变,增加了一个新的列employee和一个新的表:br/>

employee:
+----+------+-------+---------+---------------+
| id | name | email | dept_id | date_of_birth |
+----+------+-------+---------+---------------+
department:
+----+------+
| id | name |
+----+------+

Finally, we can verify that both migrations were indeed successful by invoking the following Maven command:

最后,我们可以通过调用以下Maven命令来验证这两次迁移是否确实成功。

$ mvn flyway:info -Dflyway.configFiles=myFlywayConfig.conf

4. Generate Versioned Migrations in IntelliJ IDEA

4.在IntelliJ IDEA中生成版本化的迁移程序

Writing migrations manually takes a lot of time; instead, we can generate them based on our JPA entities. We can achieve this by using a plugin for IntelliJ IDEA called JPA Buddy.

手动编写迁移需要很多时间;相反,我们可以根据我们的JPA实体来生成迁移。我们可以通过使用IntelliJ IDEA的插件JPA Buddy来实现。

To generate a differential versioned migration, simply install the plugin and call the action from the JPA Structure panel.

要生成一个差异化的版本迁移,只需安装插件并从JPA结构面板中调用该动作。

We simply select which source we want to compare (database or JPA entities) with which target (database or data model snapshot). Then JPA Buddy will generate the migration as shown in the animation:

我们只需选择我们要比较的源(数据库或JPA实体)和目标(数据库或数据模型快照)。然后JPA Buddy将生成迁移,如动画中所示。

flyway migration generation jpa

Another advantage of JPA Buddy is the ability to define mappings between Java and database types. Also, it works correctly with Hibernate custom types and JPA converters.

JPA Buddy的另一个优点是能够定义Java和数据库类型之间的映射。此外,它还能与Hibernate的自定义类型和JPA转换器正常工作。

5. Disabling Flyway in Spring Boot

5.在Spring Boot中禁用Flyway

Sometimes we may need to disable Flyway migrations under certain circumstances.

有时我们可能需要在某些情况下禁用Flyway迁移

For example, it’s common practice to generate database schema based on the entities during tests. In such a situation, we can disable Flyway under the test profile.

例如,在测试过程中,基于实体生成数据库模式是很常见的做法。在这种情况下,我们可以在test配置文件下禁用Flyway。

Let’s see how easy it is in Spring Boot.

让我们看看在Spring Boot中是多么容易

5.1. Spring Boot 1.x

5.1.Spring Boot 1.x

All we need to do is set the flyway.enabled property in our application-test.properties file:

我们需要做的是application-test.properties文件中设置flyway.enabled属性

flyway.enabled=false

5.2. Spring Boot 2.x

5.2.Spring Boot 2.x

In the more recent versions of Spring Boot, this property has been changed to spring.flyway.enabled:

在最近的Spring Boot版本中,该属性已被改为spring.flyway.enabled

spring.flyway.enabled=false

5.3. Empty FlywayMigrationStrategy

5.3.空的FlywayMigrationStrategy

If we only want to disable automatic Flyway migration on startup, but still be able to trigger the migration manually, then using the properties described above isn’t a good choice.

如果我们只想在启动时禁用自动Flyway迁移,但仍然能够手动触发迁移,那么使用上述属性并不是一个好的选择。

That’s because in such a situation, Spring Boot will not auto-configure the Flyway bean anymore. Consequently, we’d have to provide it on our own, which isn’t very convenient.

这是因为在这种情况下,Spring Boot不会再自动配置Flyway bean。因此,我们必须自己提供它,这不是很方便。

So if this is our use case, we can leave Flyway enabled and implement an empty FlywayMigrationStrategy:

因此,如果这是我们的用例,我们可以不启用Flyway并实现一个空的FlywayMigrationStrategy

@Configuration
public class EmptyMigrationStrategyConfig {

    @Bean
    public FlywayMigrationStrategy flywayMigrationStrategy() {
        return flyway -> {
            // do nothing  
        };
    }
}

This will effectively disable Flyway migration on application startup.

这将有效地禁用应用程序启动时的Flyway迁移

However, we’ll still be able to trigger the migration manually:

然而,我们仍然能够手动触发迁移。

@RunWith(SpringRunner.class)
@SpringBootTest
public class ManualFlywayMigrationIntegrationTest {

    @Autowired
    private Flyway flyway;

    @Test
    public void skipAutomaticAndTriggerManualFlywayMigration() {
        flyway.migrate();
    }
}

6. How Flyway Works

6.Flyway是如何运作的

To keep track of which migrations we’ve already applied and when, it adds a special bookkeeping table to our schema. This metadata table also tracks migration checksums, and whether or not the migrations were successful.

为了记录我们已经应用了哪些迁移,以及何时应用,它在我们的模式中添加了一个特殊的记账表。这个元数据表还记录了迁移的校验值,以及迁移是否成功。

The framework performs the following steps to accommodate evolving database schemas:

该框架执行以下步骤以适应不断变化的数据库模式。

  1. It checks a database schema to locate its metadata table (SCHEMA_VERSION by default). If the metadata table doesn’t exist, it will create one.
  2. It scans an application classpath for available migrations.
  3. It compares migrations against the metadata table. If a version number is lower or equal to a version marked as current, it’s ignored.
  4. It marks any remaining migrations as pending migrations. These are sorted based on the version number and are executed in order.
  5. As each migration is applied, the metadata table is updated accordingly.

7. Commands

7.命令

Flyway supports the following basic commands to manage database migrations:

Flyway支持以下基本命令来管理数据库迁移。

  • Info: Prints current status/version of a database schema. It prints which migrations are pending, which migrations have been applied, the status of applied migrations, and when they were applied.
  • Migrate: Migrates a database schema to the current version. It scans the classpath for available migrations and applies pending migrations.
  • Baseline: Baselines an existing database, excluding all migrations, including baselineVersion. Baseline helps to start with Flyway in an existing database. Newer migrations can then be applied normally.
  • Validate: Validates current database schema against available migrations.
  • Repair: Repairs metadata table.
  • Clean: Drops all objects in a configured schema. Of course, we should never use clean on any production database.

8. Conclusion

8.结论

In this article, we learned how Flyway works and how we can use this framework to remodel our application database reliably.

在这篇文章中,我们了解到Flyway是如何工作的,以及我们如何使用这个框架来可靠地重塑我们的应用数据库。

The code accompanying this article is available over on GitHub.

本文所附的代码可在GitHub上找到