Use Liquibase to Safely Evolve Your Database Schema – 使用Liquibase来安全地发展你的数据库模式

最后修改: 2015年 8月 11日

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

1. Overview

1.概述

In this quick tutorial, we’ll make use of Liquibase to evolve the database schema of a Java web application.

在这个快速教程中,我们将利用Liquibase来发展Java Web应用程序的数据库模式

We’ll look at a general Java app first, and we’re also going to take a focused look at some interesting options available for Spring and Hibernate.

我们会先看一个普通的Java应用,我们也会重点看一下Spring和Hibernate的一些有趣的选项。

Very briefly, the core of using Liquibase is the changeLog file, an XML file that keeps track of all changes that need to run to update the DB.

很简单,使用Liquibase的核心是changeLog文件,这是一个XML文件,记录所有需要运行更新DB的变化。

Let’s start with the Maven dependency we need to add into our pom.xml:

我们先来看看需要添加到 pom.xml中的Maven依赖性。

<dependency>
    <groupId>org.liquibase</groupId>
     <artifactId>liquibase-core</artifactId>
      <version>3.4.1</version>
</dependency>

We can also check if there’s a newer version of liquibase-core here.

我们还可以检查是否有liquibase-core的较新版本这里

2. The Database Change Log

2.数据库变更日志

Now let’s take a look at a simple changeLog file.

现在让我们看一下一个简单的changeLog文件。

This one only adds a column “address” to the table “users“:

这个只是在表”用户“中增加了一列”地址“。

<databaseChangeLog 
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog" 
  xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext
   http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd 
   http://www.liquibase.org/xml/ns/dbchangelog 
   http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd">
    
    <changeSet author="John" id="someUniqueId">
        <addColumn tableName="users">
            <column name="address" type="varchar(255)" />
        </addColumn>
    </changeSet>
    
</databaseChangeLog>

Note how the change set is identified by an id and an author to make sure it can be uniquely identified and only applied once.

请注意变更集是如何通过idauthor来识别的,以确保它可以被唯一地识别,并且只应用一次。

Let’s now see how to wire this into our application and make sure that it runs when the application starts up.

现在让我们看看如何将其接入我们的应用程序,并确保它在应用程序启动时运行。

3. Run Liquibase With a Spring Bean

3.用Spring Bean运行Liquibase

Our first option to run the changes on application startup is via a Spring bean.

我们的第一个选择是通过Spring Bean在应用程序启动时运行变化。

There are of course many other ways, but this is a good, simple way to go if we’re dealing with a Spring application:

当然还有很多其他的方法,但如果我们处理的是一个Spring应用程序,这是一个很好的、简单的方法。

@Bean
public SpringLiquibase liquibase() {
    SpringLiquibase liquibase = new SpringLiquibase();
    liquibase.setChangeLog("classpath:liquibase-changeLog.xml");
    liquibase.setDataSource(dataSource());
    return liquibase;
}

Note how we’re pointing it to a valid changeLog file that needs to exist on the classpath.

注意我们是如何把它指向一个有效的changeLog文件的,该文件需要存在于classpath上。

4. Use Liquibase With Spring Boot

4.在Spring Boot中使用Liquibase

If we’re using Spring Boot, there is no need to define a bean for Liquibase, but we still need to make sure we add the liquibase-core dependency.

如果我们使用Spring Boot,就不需要为Liquibase定义一个bean,但我们仍然需要确保添加liquibase-core的依赖性。

Then all we need is to put our change log in db/changelog/db.changelog-master.yaml, and Liquibase migrations will run automatically on startup.

然后我们只需要把我们的变更日志放在db/changelog/db.changelog-master.yaml中,Liquibase的迁移将在启动时自动运行。

We can change the default changelog file using the liquibase.change-log property:

我们可以使用liquibase.change-log属性改变默认的变更日志文件。

liquibase.change-log=classpath:liquibase-changeLog.xml

5. Disable Liquibase in Spring Boot

5.禁用Spring Boot中的Liquibase

Sometimes, we may need to disable Liquibase migration’s execution on startup.

有时,我们可能需要在启动时禁止Liquibase迁移的执行。

The simplest option we have is to use a spring.liquibase.enabled property. This way, all the remaining Liquibase configuration stays untouched.

我们最简单的选择是使用spring.liquibase.enabled属性。这样,所有剩余的Liquibase配置都不会被触动。

Here’s the example for Spring Boot 2:

下面是Spring Boot 2的例子。

spring.liquibase.enabled=false

For Spring Boot 1.x, we need to use a liquibase.enabled property:

对于Spring Boot 1.x,我们需要使用一个liquibase.enabled属性。

liquibase.enabled=false

6. Generate the changeLog With a Maven Plugin

6.用Maven插件生成changeLog

Instead of writing the changeLog file manually, we can use the Liquibase Maven plugin to generate one and save ourselves a lot of work.

我们可以使用Liquibase Maven插件来生成changeLog文件,而不是手动编写changeLog文件,这样可以节省大量的工作。

6.1. Plugin Configuration

6.1.插件配置

Here are the changes to our pom.xml:

下面是对我们的pom.xml的修改。

<dependency>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-maven-plugin</artifactId>
    <version>3.4.1</version>
</dependency> 
...
<plugins>
    <plugin>
        <groupId>org.liquibase</groupId>
        <artifactId>liquibase-maven-plugin</artifactId>
        <version>3.4.1</version>
        <configuration>                  
            <propertyFile>src/main/resources/liquibase.properties</propertyFile>
        </configuration>                
    </plugin> 
</plugins>

6.2. Generate a ChangeLog From an Existing Database

6.2.从一个现有的数据库中生成一个变更记录

We can use the plugin to generate a changelog from an existing database:

我们可以使用该插件从现有的数据库中生成一个变化日志。

mvn liquibase:generateChangeLog

Here are the liquibase properties:

这里是liquibase属性。

url=jdbc:mysql://localhost:3306/oauth_reddit
username=tutorialuser
password=tutorialmy5ql
driver=com.mysql.jdbc.Driver
outputChangeLogFile=src/main/resources/liquibase-outputChangeLog.xml

The end result is a changeLog file that we can use either to create an initial DB schema or to populate data.

最终的结果是一个changeLog文件,我们可以用它来创建一个初始DB模式或填充数据。

Here’s how that would look:

以下是这一情况。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog ...>
    
    <changeSet author="John (generated)" id="1439225004329-1">
        <createTable tableName="APP_USER">
            <column autoIncrement="true" name="id" type="BIGINT">
                <constraints primaryKey="true"/>
            </column>
            <column name="accessToken" type="VARCHAR(255)"/>
            <column name="needCaptcha" type="BIT(1)">
                <constraints nullable="false"/>
            </column>
            <column name="password" type="VARCHAR(255)"/>
            <column name="refreshToken" type="VARCHAR(255)"/>
            <column name="tokenExpiration" type="datetime"/>
            <column name="username" type="VARCHAR(255)">
                <constraints nullable="false"/>
            </column>
            <column name="preference_id" type="BIGINT"/>
            <column name="address" type="VARCHAR(255)"/>
        </createTable>
    </changeSet>
    ...
</databaseChangeLog>

6.3. Generate a ChangeLog From Diffs Between Two Databases

6.3.从两个数据库之间的差异中生成一个ChangeLog

We can use the plugin to generate a changeLog file from the differences between two existing databases (for example, development and production):

我们可以使用该插件从两个现有数据库(例如,开发和生产)之间的差异生成一个changeLog文件。

mvn liquibase:diff

Here are the properties:

以下是这些属性。

changeLogFile=src/main/resources/liquibase-changeLog.xml
url=jdbc:mysql://localhost:3306/oauth_reddit
username=tutorialuser
password=tutorialmy5ql
driver=com.mysql.jdbc.Driver
referenceUrl=jdbc:h2:mem:oauth_reddit
diffChangeLogFile=src/main/resources/liquibase-diff-changeLog.xml
referenceDriver=org.h2.Driver
referenceUsername=sa
referencePassword=

And here’s a snippet of the generated changeLog:

这里是生成的changeLog的一个片段。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog ...>
    <changeSet author="John" id="1439227853089-1">
        <dropColumn columnName="address" tableName="APP_USER"/>
    </changeSet>
</databaseChangeLog>

This is a super powerful way to evolve our DB by, for example, allowing Hibernate to auto-generate a new schema for development and then using that as a reference point against the old schema.

这是一个超级强大的方式来发展我们的数据库,例如,允许Hibernate为开发自动生成一个新的模式,然后将其作为一个参考点来对抗旧的模式。

7. Use the Liquibase Hibernate Plugin

7.使用Liquibase Hibernate插件

In case our application uses Hibernate, we’re going to take a look at a very useful way of generating the changeLog, which is the liquibase-hibernate plugin.

如果我们的应用程序使用了Hibernate,我们将看一下生成changeLog的一个非常有用的方法,这就是liquibase-hibernate插件

7.1. Plugin Configuration

7.1.插件配置

First, let’s get the new plugin configured and using the right dependencies:

首先,让我们把新的插件配置好,并使用正确的依赖性。

<plugins>
    <plugin>
        <groupId>org.liquibase</groupId>
        <artifactId>liquibase-maven-plugin</artifactId>
        <version>3.4.1</version>
        <configuration>                  
            <propertyFile>src/main/resources/liquibase.properties</propertyFile>
        </configuration> 
        <dependencies>
            <dependency>
                <groupId>org.liquibase.ext</groupId>
                <artifactId>liquibase-hibernate4</artifactId>
                <version>3.5</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>4.1.7.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-jpa</artifactId>
                <version>1.7.3.RELEASE</version>
            </dependency>
        </dependencies>               
    </plugin> 
</plugins>

7.2. Generate a changeLog From Diffs Between a Database and Persistence Entities

7.2.从数据库和持久化实体之间的差异生成changeLog

We can use this plugin to generate a changeLog file from the differences between an existing database (for example, production) and our new persistence entities.

我们可以使用这个插件,从现有数据库(例如,生产)和我们的新持久化实体之间的差异中生成一个changeLog文件。

So, to make things simple, once an entity is modified, we can simply generate the changes against the old DB schema, getting a clean, powerful way to evolve our schema in production.

因此,为了使事情变得简单,一旦实体被修改,我们可以简单地针对旧的DB模式生成变化,获得一种干净、强大的方式来在生产中进化我们的模式。

Here are the liquibase properties:

这里是liquibase属性。

changeLogFile=classpath:liquibase-changeLog.xml
url=jdbc:mysql://localhost:3306/oauth_reddit
username=tutorialuser
password=tutorialmy5ql
driver=com.mysql.jdbc.Driver
referenceUrl=hibernate:spring:org.baeldung.persistence.model
  ?dialect=org.hibernate.dialect.MySQLDialect
diffChangeLogFile=src/main/resources/liquibase-diff-changeLog.xml

Note that the referenceUrl is using package scan, so the dialect parameter is required.

注意,referenceUrl使用的是包扫描,所以dialect参数是必需的。

8. Generate the changeLog in IntelliJ IDEA Using the JPA Buddy Plugin

8.在IntelliJ IDEA中使用JPA Buddy插件生成changeLog

If we’re using a non-Hibernate ORM (e.g., EclipseLink or OpenJPA) or we don’t want to add extra dependencies like the liquibase-hibernate plugin, we can use JPA Buddy. This IntelliJ IDEA plugin integrates useful features of Liquibase into the IDE.

如果我们使用的是非Hibernate ORM(例如EclipseLink或OpenJPA),或者我们不想像liquibase-hibernate插件那样添加额外的依赖,我们可以使用JPA Buddy这个IntelliJ IDEA插件将Liquibase的有用功能集成到IDE中。

To generate a differential changeLog, we simply install the plugin and then call the action from the JPA Structure panel. We select what source we want to compare (database, JPA entities or Liquibase snapshot) with what target (database or Liquibase snapshot).

为了生成一个差异化的changeLog,我们只需安装该插件,然后从JPA结构面板调用该动作。我们选择我们要比较的源(数据库、JPA实体或Liquibase快照)和目标(数据库或Liquibase快照)。

JPA Buddy will generate the changeLog as shown in the animation below:

JPA Buddy将生成changeLog,如下图动画所示

jpabuddy_intellij

Another advantage of JPA Buddy over the liquibase-hibernate plugin is the ability to override default mappings between Java and database types. Also, it works correctly with Hibernate custom types and JPA converters.

liquibase-hibernate插件相比,JPA Buddy的另一个优势是能够覆盖Java和数据库类型之间的默认映射。此外,它还能与Hibernate的自定义类型和JPA转换器正常工作。

9. Conclusion

9.结论

In this article, we illustrated several ways to use Liquibase and get to a safe and mature way of evolving and refactoring the DB schema of a Java app.

在这篇文章中,我们说明了使用Liquibase的几种方法,并以一种安全和成熟的方式进化和重构Java应用程序的DB模式

The implementation of all these examples and code snippets is available over on GitHub.

所有这些例子和代码片断的实现都可以在GitHub上找到