Liquibase and PostgreSQL: create database and schema before executing changesets (Spring Boot)

Create the following class:

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.*;

@Slf4j
@Component
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "spring.liquibase", name = "enabled", matchIfMissing = true)
public class PostgreSQLSchemaInitBean implements InitializingBean {

    public static final String CONNECTION_URL_TEMPLATE_STRING = "%s/?user=%s&password=%s";
    public static final String CREATE_DATABASE_TEMPLATE_STRING = "CREATE DATABASE %s";
    public static final String COUNT_DATABASE_TEMPLATE_STRING = "SELECT count(1) FROM pg_catalog.pg_database WHERE lower(datname) = lower('%s')";
    public static final String CREATE_SCHEMA_TEMPLATE_STRING = "create schema if not exists %s";

    private final DataSource dataSource;

    @Value("${application.db.schema-name}")
    private String schemaName;

    @Value("${application.db.liquibase.schema-name}")
    private String liquibaseSchemaName;

    @Value("${application.db.name}")
    private String dbName;

    @Value("${application.db.username}")
    private String username;

    @Value("${application.db.password}")
    private String password;

    @Value("${application.db.base-url}")
    private String baseUrl;

    @Override
    public void afterPropertiesSet() throws SQLException {
        try (Connection connection = DriverManager.getConnection(String.format(CONNECTION_URL_TEMPLATE_STRING, baseUrl, username, password));
             Statement cnnectionStatement = connection.createStatement()) {
            createDatabase(cnnectionStatement);
        } catch (SQLException e) {
            throw new RuntimeException("Failed to create database '" + dbName + "'", e);
        }
        tryToCreateSchema();
    }

    private void createDatabase(Statement cnnectionStatement) throws SQLException {
        ResultSet rs = cnnectionStatement.executeQuery(String.format(COUNT_DATABASE_TEMPLATE_STRING, dbName));
        rs.next();
        int count = rs.getInt(1);
        log.debug("n. of databases: {}", count);
        if (count == 0) {
            cnnectionStatement.execute(String.format(CREATE_DATABASE_TEMPLATE_STRING, dbName));
            log.debug("### database created: {}", dbName);
        }
    }

    private void tryToCreateSchema() {
        try (Connection conn = dataSource.getConnection();
             Statement statement = conn.createStatement()) {
            statement.execute(String.format(CREATE_SCHEMA_TEMPLATE_STRING, liquibaseSchemaName));
            statement.execute(String.format(CREATE_SCHEMA_TEMPLATE_STRING, schemaName));
            log.debug("### schema created: {}", schemaName);
        } catch (SQLException e) {
            throw new RuntimeException("Failed to create schema '" + schemaName + "'", e);
        }
    }

}

Then create AbstractDependsOnBeanFactoryPostProcessor:

import liquibase.change.DatabaseChange;
import liquibase.integration.spring.SpringLiquibase;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AbstractDependsOnBeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Slf4j
@Configuration
@ConditionalOnClass({SpringLiquibase.class, DatabaseChange.class})
@ConditionalOnBean(PostgreSQLSchemaInitBean.class)
@ConditionalOnProperty(prefix = "spring.liquibase", name = "enabled", matchIfMissing = true)
@AutoConfigureAfter({DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
@Import({PostgreSQLSchemaInitBean.class})
public class PostgreSQLLiquibaseDependsOnPostProcessor extends AbstractDependsOnBeanFactoryPostProcessor {

    PostgreSQLLiquibaseDependsOnPostProcessor() {
        super(SpringLiquibase.class, PostgreSQLSchemaInitBean.class);
    }

}

Update application.yml properly:

# application.yml file
application:
  db:
    name: db_name
    schema-name: schma_name
    username: username
    password: password
    base-url: jdbc:postgresql://localhost:5432
    liquibase:
      schema-name: liquibase_schema_name
spring:
  datasource:
    hikari:
      schema: ${application.db.schema-name}
    driverClassName: org.postgresql.Driver
    url: ${application.db.base-url}/${application.db.name}
    username: ${application.db.username}
    password: ${application.db.password}
    testOnBorrow: true
    timeBetweenEvictionRunsMillis: 60000
    validationQuery: SELECT 1
  liquibase:
    change-log: classpath:/database/db.changelog.xml
    enabled: true
    liquibase-schema: ${application.db.liquibase.schema-name}