'Using Liquibase to populate data with ORM
I am currently using Liquibase to create the database schema. It's a spring boot application that uses hibernate ORM.
Liquibase is changing the database schema at the beginning of application startup, before ORM is initialized (so that ORM looks at correct schema when being initialized).
However after the application is started I want to run Liquibase for the second time to populate data in the database with the help of the ORM and java code ( using java changeset: Java code changeset in liquibase ).
The reason to use Liquibase to populate the data with ORM and java is because Liquibase works correctly in a cluster (ex. if we have 3 nodes started at the same time only one node will populate the data).
Is is possible to run Liquibase in "2 phases"?
First time, during the application startup to update the database schema, and second time, after application was started, to populate some data using ORM. We need to do that from java code.
Previously when using Flyway we did that by having 2 Flyway instances, each with individual changes, and running the first instance at the beginning of the application startup before ORM is initialized and the second instance after the startup is complete and ORM is initialized.
Solution 1:[1]
Following solution seems to be working:
@Component
class LiquibaseOrmMigrationRunner(val dataSource: DataSource, val resourceLoader: ResourceLoader, val environment: Environment) :
ApplicationListener<ContextRefreshedEvent> {
override fun onApplicationEvent(event: ContextRefreshedEvent) {
val isLiquibaseEnabled = environment.getProperty("spring.liquibase.enabled", Boolean::class.java, true)
if (!isLiquibaseEnabled) {
return
}
var liquibase: Liquibase? = null
try {
val changeLogFile = "classpath:/db/changelog/orm/db.changelog-master-orm.yaml"
val connection = dataSource.connection
val liquibaseConnection = JdbcConnection(connection)
val database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(liquibaseConnection)
database.databaseChangeLogTableName = "ORMDATABASECHANGELOG"
database.databaseChangeLogLockTableName = "ORMDATABASECHANGELOGLOCK"
val resourceAccessor = SpringResourceAccessor(resourceLoader)
liquibase = Liquibase(changeLogFile, resourceAccessor, database)
liquibase.update(Contexts(), LabelExpression())
} catch (e: SQLException) {
throw DatabaseException(e)
} finally {
if (liquibase != null) {
liquibase.close()
}
}
}
}
This creates an additional liquibase instance that runs after the default one configured by spring boot.
To populate the database using orm the additional liquibase instance executes the following changeset
databaseChangeLog:
- changeSet:
id: initial_data
author: author
changes:
- customChange: { "class": "com.example.LiquibaseInitialDataMigrationOrm" }
The custom change looks like this:
class LiquibaseInitialDataMigrationOrm : CustomTaskChange {
override fun execute(database: Database?) {
val initialDataService = SpringApplicationContextSingleton.getBean("initialDataService") as InitialDataService
initialDataService.populateInitialData()
}
override fun getConfirmationMessage(): String {
return "Initial Data Liquibase ORM Migration finished"
}
@Suppress("EmptyFunctionBlock")
override fun setUp() {
}
@Suppress("EmptyFunctionBlock")
override fun setFileOpener(resourceAccessor: ResourceAccessor?) {
}
override fun validate(database: Database?): ValidationErrors {
return ValidationErrors()
}
}
The custom change uses a singleton that contains the spring application context
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Antonio |