如何在 liquibase 开始工作之前在 Postgres DB 中创建模式?

How to create schema in Postgres DB, before liquibase start to work?

我有独立的应用程序。它在 java、spring-boot、postgres 上,它有 liquibase。

我需要部署我的应用程序,liquibase 应该创建所有 tables,等等。但它应该在自定义架构中而不是在 public 中执行。 liquibase 的所有服务 table(databasechangelog 和 databasechangeloglock)也应该在自定义模式中。在 liquibase 开始工作之前,如何在数据库中创建我的架构?我必须在我的应用程序部署时在配置中或类似的地方执行它。无需对数据库进行任何手动干预。

application.properties:

spring.datasource.jndi-name=java:/PostgresDS
spring.jpa.properties.hibernate.default_schema=my_schema
spring.jpa.show-sql = false
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.datasource.continue-on-error=true
spring.datasource.sql-script-encoding=UTF-8

liquibase.change-log = classpath:liquibase/changelog-master.yaml
liquibase.default-schema = my_schema

更新:

当 liquibase 启动时,它会创建两个 tables 数据库变更日志和一个 table。之后,liquibase 开始工作。但是我想要 liquibase.default-schema = my_schema 中的 liquibase,但是当 liquibase 开始工作时它不存在并且出现错误:异常是 liquibase.exception.LockException: liquibase.exception.DatabaseException: ERROR: schema "my_schema" does not exist

我希望 liquibase 在自定义架构中工作,而不是在 public:

liquibase.default-schema = my_schema

但在 liquibase 可以执行此操作之前,必须先创建架构。 Liquibase 无法执行此操作,因为它尚未启动并且启动时需要架构。 恶性循环。

我用 application.properties 找到了解决方案。

org.springframework.jdbc.datasource.init.ScriptUtils 在 liquibase 之前工作。

日志:

14:11:14,760 INFO  [org.springframework.jdbc.datasource.init.ScriptUtils] 
(ServerService Thread Pool -- 300) Executing SQL script from URL 
[vfs:/content/app.war/WEB-INF/classes/schema.sql]

14:11:14,761 INFO  [org.springframework.jdbc.datasource.init.ScriptUtils] 
(ServerService Thread Pool -- 300) Executed SQL script from URL 
[vfs:/content/app.war/WEB-INF/classes/schema.sql] in 1 ms.

14:11:14,912 ERROR [stderr] (ServerService Thread Pool -- 300) INFO 9/27/18 
2:11 PM: liquibase: Successfully acquired change log lock

14:11:15,292 ERROR [stderr] (ServerService Thread Pool -- 300) INFO 9/27/18 
2:11 PM: liquibase: Reading from my_schema.databasechangelog

14:11:15,320 ERROR [stderr] (ServerService Thread Pool -- 300) INFO 9/27/18 
2:11 PM: liquibase: Successfully released change log lock

我只是将 schema.sqlCREATE SCHEMA IF NOT EXISTS my_schema; 放入资源目录并且一切正常。

感谢大家的帮助。

更新:它适用于 Spring 启动 1.X。如果您使用 Spring Boot 2,您应该在属性文件中启用 schema.sql,并使用 spring.datasource.initialization-mode=always。 更多信息见 Spring Boot - Loading Initial Data

更新 2:在 Spring Boot 2.5.2 中(也许在早期版本中也是如此)这个解决方案现在不起作用,正如@peterh 在评论中所写的那样。悲伤但真实。我尝试这个解决方案的最后一个版本是 Spring Boot 2.0.9 在文档 Spring Boot 中说它是从 Spring Boot 2.5.x[=18 重新设计的=]

更新 3:关于为什么他们取消此功能的一些信息 -> https://github.com/spring-projects/spring-boot/issues/22741

您可以为此使用 Spring Boot Pre-Liquibase module。这正是它的意义所在。它在执行 Liquibase 本身之前执行一些 SQL。 Pre-Liquibase 在 Spring Boot AutoConfigure 链中设置自己,以便保证它始终在 before Liquibase.

执行

第 1 步

将以下 Starter 添加到您的项目中:

<dependency>
    <groupId>net.lbruun.springboot</groupId>
    <artifactId>preliquibase-spring-boot-starter</artifactId>
    <version>  ---latest-version---  </version>
</dependency>

第 2 步

将 SQL 文件添加到 src/main/resources/preliquibase,名称为 postgresql.sql,内容如下:

CREATE SCHEMA IF NOT EXISTS ${spring.liquibase.default-schema};

${} 语法表示一个占位符变量。 Pre-Liquibase 将从您的 Spring 环境中的属性解析它。

第 3 步

像这样设置应用程序属性:

spring.liquibase.default-schema=${my.db.schemaname}
spring.jpa.properties.hibernate.default_schema=${my.db.schemaname}

现在 - 在此示例中 - 唯一需要决定的是 my.db.schemaname 值的来源。那是你的选择。 example project 提倡它应该来自 OS 环境变量,特别是如果您要部署到云中。

最后的话

警告:Pre-Liquibase 可能过于灵活,因为它允许执行 any SQL 代码。不要试图将内容放入原属于 Liquibase ChangeSet 的 Pre-Liquibase 文件中。老实说,我能想到的 Pre-Liquibase 的唯一用途是设置一个数据库“主页”(即模式或目录),Liquibase 数据库对象可以在其中存在,以便同一应用程序的实例可以按模式或目录分开同时驻留在同一个数据库服务器上。

(披露:我是 Pre-Liquibase 模块的作者)