我有一个现有的spring boot项目和一个数据库。现在,我想添加liquibase来处理进一步的数据库迁移。正确的步骤是什么?
我已遵循this article添加liquibase并生成变更日志。我发现的大多数文章都是从头开始谈论在项目中使用liquibase的,或者对实现不太详细。到目前为止,我已完成以下操作:
在pom.xml中添加了依赖项和插件
<dependencies>
//..other dependencies
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>3.6.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>3.6.2</version>
<configuration>
<propertyFile>src/main/resources/liquibase.properties</propertyFile>
</configuration>
</plugin>
</plugins>
</build>
在src / main / resources下添加了liquibase.properties文件
url=jdbc:mysql://localhost:3306/demodb
username=root
password=root
driver=com.mysql.jdbc.Driver
outputChangeLogFile=src/main/resources/db/changelog/changes/demodb-changelog.xml
更新了src / main / resources下的application.properties文件以处理更改日志
#Hibernate
spring.datasource.url=jdbc:mysql://localhost:3306/demodb
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
#Jpa
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
#Liquibase
spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.xml
在src / main / resources / db / changelog下创建了db.changelog-master.xml
文件
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.liquibase.org/xml/ns/dbchangelog/1.9
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd">
</databaseChangeLog>
运行spring boot应用程序,以便在数据库中创建两个新表-DATABASECHANGELOG
(目前为空)和DATABASECHANGELOGLOCK
(目前只有一个空条目)< / p>
从终端生成demodb-changelog.xml
文件,以为数据库的当前状态创建更改日志
mvn liquibase:generateChangeLog
然后在添加的liquibase.properties
中同步已执行的当前变更日志:
changeLogFile=src/main/resources/db/changelog/changes/demodb-changelog.xml
然后从终端运行:
mvn liquibase:changelogSync
现在,DATABASECHANGELOG
表具有已执行的变更日志的条目。
接下来在db.changelog-master.xml
文件中,添加生成的文件:
<include file="db/changelog/changes/demodb-changelog.xml"/>
现在,当我运行应用程序时,出现异常:
Caused by: liquibase.exception.MigrationFailedException:
Migration failed for change set db/changelog/changes/demodb-changelog.xml
Reason: liquibase.exception.DatabaseException: Table 'abc' already exists
因此,这试图再次运行变更日志文件。如何配置为仅运行那些尚未运行的变更集?我以为DATABASECHANGELOG
的功能是处理已执行的变更集,但是我想我错了。
我可以在没有include
中的db.changelog-master.xml
标记的情况下运行该应用程序,但是我想所有更改日志文件都需要在此处列出,因为如果我要运行此应用程序,则需要所有更改日志文件在另一台机器上从头开始创建整个数据库。
那么如何配置liquibase仅运行尚未执行的变更日志?
答案 0 :(得分:1)
这里的问题是存储在filename
表中的DATABASECHANGELOG
字段。
Liquibase不仅通过changeSet
或id
和id
来确定author
的身份,而是根据值来确定changeSet
的身份所有三个字段中的一个:id
,author
,filename
。
运行mvn liquibase:update
或mvn liquibase:changelogSync
时,变更日志的filename
为src/main/resources/db/changelog/changes/demodb-changelog.xml
。这是由liquibase存储在每个表行的DATABASECHANGELOG.FILENAME
字段中的值。
但是,当您启动spring-boot应用程序时,更改日志的文件名是classpath:db/changelog/db.changelog-master.xml
,包含在其中的更改日志的文件名是db/changelog/changes/demodb-changelog.xml
。
然后,Liquibase检查已经应用了哪些变更集。
它检查id
表中每个变更集的author
,filename
和DATABASECHANGELOG
以及db/changelog/changes/demodb-changelog.xml
文件中的每个变更集,但没有发现任何东西匹配,因为filename
字段不同。在数据库中,其值为src/main/resources/db/changelog/changes/demodb-changelog.xml
。
src/main/resources/db/changelog/changes/demodb-changelog.xml != db/changelog/changes/demodb-changelog.xml
因此,Liquibase认为这些是不同的变更集,并尝试应用它们。它失败,因为表已经存在。但是,如果将变更集更改为可以多次应用的内容,则会发现Liqibase为DATABASECHANGELOG
表中的行创建了一组新集,这些行具有相同的id
,相同的author
但不同filename
...
老实说,我不知道如何解决这个问题。看来,Spring Boot中的Liquibase如何工作是一个基本问题。在spring-boot应用程序启动时读取更改日志文件时,它们必须位于类路径中,尤其是在使用默认的fat-jar的情况下。但是,当您将Liquibase作为maven插件运行时,它们通常位于本地文件系统上(不是真的,请参见我的“ Update 2”)。
也许存在一些解决方法,请在注释中添加它,然后我将更新答案。
更新
我发现一种解决方法是使用<databaseChangeLog>
标签的logicalFilePath
属性。
用于在创建变更集的唯一标识符时覆盖文件名和路径。移动或重命名更改日志时必填。
如果您在所有变更日志/变更集中都进行了仔细设置,它应该可以正常工作。
您的db/changelog/changes/demodb-changelog.xml
文件的示例:
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd"
logicalFilePath="db/changelog/changes/demodb-changelog.xml">
</databaseChangeLog>
但是请注意<include>
标签。它支持包括原始*.sql
文件。而且您无法为其设置logicalFilePath
。并且logicalFilePath
标记上设置的<databaseChangeLog>
也不会被继承。
更新2
我找到了一个更强大的解决方案。
只需不在maven插件的src/main/resources
属性中指定前缀changeLogFile
。这样,maven将在类路径中而不是文件系统中搜索变更日志。因此,您必须先执行mvn process-resources
,然后再执行liquibase maven插件。
因此,您的liquibase maven插件(changeLogFile
文件)的liquibase.properties
属性应如下所示:
changeLogFile=db/changelog/changes/demodb-changelog.xml
然后,当您运行
mvn process-resources liquibase:changelogSync
Liquibase将在DATABASECHANGELOG
表中创建记录,其中FILENAME
字段等于db/changelog/changes/demodb-changelog.xml
,该字段等于filename,由spring-boot在启动时运行迁移时使用({{1 }}前缀在比较文件名时由Liquibase自动删除)。最后,这使Liquibase能够理解,这些是同一组变更集,并且已经应用。
答案 1 :(得分:0)
MySQL将在表中维护liquibase的日志,
Liquibase使用DATABASECHANGELOG表来跟踪已运行哪些changeSet。
无论何时运行变更日志,对于每个变更日志,表都会添加一行,根据ID,我们可以知道运行了哪些变更日志,
有些标记,例如runAlways
,runOnChange
,这些标记将帮助我们执行/不执行。
您可以参考:
For understanding flags:
https://www.liquibase.org/documentation/changeset.html
For understanding DATABASECHANGELOG Table:
https://www.liquibase.org/documentation/databasechangelog_table.html