使用Liquibase在Spring Boot应用程序中管理数据库结构。 第二部分

上一部分中,我们了解了Liquibase的基本功能,并编写了一个使用Liquibase初始化数据库的Spring引导应用程序的基本示例。 完整的基本应用程序代码可以在GitHub上找到 。 在本文中,我们将讨论liquibase-maven-plugin及其为数据库结构版本化提供的其他功能。 让我们从如何使用比较功能自动创建脚本开始。

假设我们需要对数据库的结构进行一些更改。 例如,我们希望email不为null 。 当然,对于这么小的更改,您可以手动更正代码和脚本,但是如果还有更多更改,该怎么办? 在这种情况下,比较Liquibase内置数据库的能力将对我们有所帮助。 它的一个有趣的功能是,您不仅可以比较两个数据库,还可以比较应用程序中具有一组JPA实体的数据库。 这就是我们现在要做的!

使用liquibase-diff创建具有更改的脚本


pom.xml文件的plugins部分中, 我们添加了这个相当复杂的设计。 这是liquibase-maven-plugin ,与该依赖项连接的依赖项用于分析休眠实体和使用YAML文件。 该插件将通过比较两个数据库中的结构,甚至通过比较数据库中的数据结构以及我们应用程序中的一组hiberante实体,来帮助我们自动生成liquibase脚本为此添加了liquibase-hibernate5 )。

<plugin> <groupId>org.liquibase</groupId> <artifactId>liquibase-maven-plugin</artifactId> <version>3.8.1</version> <configuration> <propertyFile>${project.build.outputDirectory}/liquibase-maven-plugin.properties</propertyFile> <systemProperties> <user.name>your_liquibase_username</user.name> </systemProperties> <logging>info</logging> </configuration> <dependencies> <dependency> <groupId>org.liquibase.ext</groupId> <artifactId>liquibase-hibernate5</artifactId> <version>3.6</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>2.2.0.RELEASE</version> </dependency> </dependencies> </plugin> 

注意user.name设置。 它是可选的,但没有它,Liquibase将在创建的脚本中指示启动插件的当前OS用户的名称。

可以在调用maven时将插件的设置直接写入pom.xml或作为命令行参数传递,但是我更喜欢带有单独liquibase-maven-plugin.properties文件的选项。 其内容将如下所示。

 changeLogFile= @project.basedir@/src/main/resources/db/changelog/db.changelog-master.yaml url= jdbc:mysql://localhost:3306/geek_db?createDatabaseIfNotExist=true&allowPublicKeyRetrieval=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC username= dbusername password= dbpassword driver= com.mysql.cj.jdbc.Driver referenceUrl= hibernate:spring:ru.usharik.liquibase.demo.persist.model?dialect=org.hibernate.dialect.MySQLDialect diffChangeLogFile= @project.basedir@/src/main/resources/db/changelog/db.changelog-@timestamp@.yaml ignoreClasspathPrefix= true 
值得注意urlreferenceUrl参数。 比较后将创建liquibase的脚本将是使用referenceUrl链接的基础和使用url链接的基础之间的区别。 如果以后使用url链接在基础上运行此脚本,则该脚本将与referenceUrl链接上的脚本相同。 应特别注意referenceUrl链接。 如您所见,它不是指数据库,而是指实体类所在的应用程序包。 因此,我们现在可以找到一个脚本,该脚本会将代码中所做的更改添加到数据库中。

现在我们需要配置maven-resource-plugin来替换设置文件中的占位符,例如@project.basedir@@timestamp@ 。 为此,请将以下表单的资源部分添加到构建部分

 <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>*.properties</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <filtering>false</filtering> <includes> <include>**/*.*</include> </includes> </resource> </resources> 

顺便说一句,Spring启动将使用maven-resource-plugin填充的占位符的标准格式从${smth}更改为@smth@ 。 事实是,Spring Boot中@smth@占位符用于替换环境变量和Spring Boot应用程序本身的参数。

另外,我们稍微更改pom.xml中properties部分,以所需的格式将值分配给timestamp变量。 las,标准格式可能包含某些操作系统的文件名中禁止的字符。

 <properties> <java.version>1.8</java.version> <timestamp>${maven.build.timestamp}</timestamp> <maven.build.timestamp.format>yyyyMMdd-HHmmssSSS</maven.build.timestamp.format> </properties> 

现在,让我们更改User类中的email字段

  @Column(name = "email", nullable = false) private String email; 

最后,使用liquibase-maven-plugin运行build命令进行比较。

 mvn clean install liquibase:diff -DskipTests=true 

在这种情况下,我们需要完全重建项目,因为该插件(liquibase:diff)将不使用编译源,而是使用目标文件夹中的编译实体类文件进行分析。

如果一切正确完成,那么在资源/ db / changelog文件夹中成功执行命令后,您将看到一个名为db.changelog-20190723-100748666.yaml的文件。 由于我们在文件名中使用了当前日期和时间,因此每次启动时我们都会有一个新文件,这非常方便。 如果您已经使用上一课所对应的表结构创建了数据库,则文件的内容应如下所示。

 databaseChangeLog: - changeSet: id: 1563876485764-1 author: your_liquibase_username (generated) changes: - addNotNullConstraint: columnDataType: varchar(255) columnName: email tableName: users 
如您所见,此脚本完全进行了代码中的更改。 作为练习,我建议您针对一个空的数据库运行此脚本并查看结果。

接下来,我们可以简单地将changeSet从该文件复制到db.changelog-master.yaml,或者可以通过一条指令将该文件连接到它

  - include: file: db.changelog-20190723-100748666.yaml relativeToChangelogFile: true 

同样在此文件中,您需要指定logicalFilePath: db/changelog/db.changelog-20190723-100748666.yaml ,类似于在db.changelog-master.yaml中的操作
这将有助于解决在应用程序中使用内置的liquibase bean和liquibase-maven-plugin时可能出现的一些问题。 之后,重新启动应用程序或运行命令:

 mvn liquibase:update 

让我们尝试对代码进行一些更复杂的更改。 例如,添加一个角色表,该角色表将与用户表具有多对多关系。

 @Entity @Table(name = "roles") public class Role implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; @Column(name = "name", unique = true, nullable = false) private String name; @ManyToMany(mappedBy = "roles") private Set<User> users; //  , ,  } 

然后在“用户”表中添加
  @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "users_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) private Set<Role> roles; 

开始比较之后,我们得到一个包含以下内容的文件
 databaseChangeLog: - changeSet: id: 1563877765929-1 author: your_liquibase_username (generated) changes: - createTable: columns: - column: autoIncrement: true constraints: primaryKey: true primaryKeyName: rolesPK name: id type: BIGINT - column: constraints: nullable: false name: name type: VARCHAR(255) tableName: roles - changeSet: id: 1563877765929-2 author: your_liquibase_username (generated) changes: - createTable: columns: - column: constraints: nullable: false name: user_id type: BIGINT - column: constraints: nullable: false name: role_id type: BIGINT tableName: users_roles - changeSet: id: 1563877765929-3 author: your_liquibase_username (generated) changes: - addPrimaryKey: columnNames: user_id, role_id tableName: users_roles - changeSet: id: 1563877765929-4 author: your_liquibase_username (generated) changes: - addUniqueConstraint: columnNames: name constraintName: UC_ROLESNAME_COL tableName: roles - changeSet: id: 1563877765929-5 author: your_liquibase_username (generated) changes: - addForeignKeyConstraint: baseColumnNames: user_id baseTableName: users_roles constraintName: FK2o0jvgh89lemvvo17cbqvdxaa deferrable: false initiallyDeferred: false referencedColumnNames: id referencedTableName: users - changeSet: id: 1563877765929-6 author: your_liquibase_username (generated) changes: - addForeignKeyConstraint: baseColumnNames: role_id baseTableName: users_roles constraintName: FKj6m8fwv7oqv74fcehir1a9ffy deferrable: false initiallyDeferred: false referencedColumnNames: id referencedTableName: roles 

我们还可以轻松地将此文件添加到工作脚本中。

回滚更改


现在,让我们看看如何回滚所做的更改。 由于某些原因,我们在changeSets中指定的那些标识符不能用于回滚到它们。 有三个选项,指定一个回滚点

  • 从当前计数的changeSet的数量
  • 更改日期之后
  • 通过标签(使用特殊类型的changeSet设置)

标签设置如下。

  - changeSet: id: some_uniqui_id author: liquibase_user_name changes: - tagDatabase: tag: db_tag 

好了,列出了三种回滚方式的命令

 mvn liquibase:rollback -Dliquibase.rollbackTag=db_tag mvn liquibase:rollback -Dliquibase.rollbackCount=1 mvn liquibase:rollback "-Dliquibase.rollbackDate=Jun 03, 2017" 

最后,一些其他材料

  1. 本文代码
  2. 关于Liquibase中的回滚
  3. 关于使用Liquibase进行迁移
  4. GitHub上的Liquibase
  5. 关于数据库版本控制的各种方法的非常好的文章

当然,对于任何评论,补充,说明等,我都会感到非常高兴。

Source: https://habr.com/ru/post/zh-CN460907/


All Articles