-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
spring boot v3 integration example #134
Comments
I've just finished setting the same stack up and have gotten it all working without exceptions, it also took me several hours due to a complete lack of documentation/info on how to setup the different plugins all together. I'd also be happy to help work on documentation for this stack as there is surprisingly little info on how to make it work. |
@ZooToby yes, please! What about starting with a quick YouTube video where you show it and the community can help thanks to that? I ended up with JPA Buddy to avoid this pain. |
I'd be happy to - unless it may be more helpful for be to post my config/learnings in a quick write up here? The final config is actually fairly simple, just took a heap of debugging/trawling through source code to get it working Even got it setup to automatically generate change logs following an alphabetic naming scheme so I'm pretty happy with the final config |
That sounds good. I'd like to take a look at the configuration and provide some feedback. |
Okay, here we go: OverviewThis is my stack
Quick note; I haven't setup liquibase kotlin dsl plugin as we already use yaml for a lot of our IaC/other config File structureI've only included what I thought is relevant, am happy to update with the rest of the projects structure if it makes things clearer
Build ScriptThe first part of the config is fairly standard plugins {
val kotlinVersion = "1.9.0"
val springBootVersion = "3.1.4"
val springDependencyManagementVersion = "1.1.3"
val liquiBaseVersion = "2.2.1"
id("org.springframework.boot") version springBootVersion
id("io.spring.dependency-management") version springDependencyManagementVersion
kotlin("jvm") version kotlinVersion
kotlin("plugin.spring") version kotlinVersion
kotlin("plugin.jpa") version kotlinVersion
id("org.liquibase.gradle") version liquiBaseVersion
}
// Make liquibase extend from main runtime, letting it be able to see/interact with hibernate
configurations {
liquibaseRuntime.extendsFrom(runtimeClasspath)
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-security")
runtimeOnly("org.postgresql:postgresql")
implementation("io.hypersistence:hypersistence-utils-hibernate-62:${hipersistenceUtilsVersion}")
testRuntimeOnly("com.h2database:h2")
liquibaseRuntime("org.liquibase:liquibase-core:${liquibaseVersion}")
liquibaseRuntime("org.postgresql:postgresql")
liquibaseRuntime("org.liquibase.ext:liquibase-hibernate6:${liquibaseVersion}")
liquibaseRuntime("info.picocli:picocli:${picocliVersion}")
// To connect with hibernate liquibase needs to be able to get the main souce sets output
liquibaseRuntime(sourceSets.getByName("main").output)
} This block will import all the dependencies and setup the config needed for liquibase to be able to access hibernate. Just to note; I've left a lot of other dependencies and plugins that aren't relevant. This next part is the main part of the config so I will go through it in smaller bits: // Loading in liquibase configuration properties
val liquibasePropertyFile = file("src/main/resources/db/liquibase.properties")
val loadLiquibaseProperties = Properties()
if (liquibasePropertyFile.exists()) {
loadLiquibaseProperties.load(liquibasePropertyFile.inputStream())
}
val liquibaseProperties = loadLiquibaseProperties.toMutableMap()
// Otherwise use env var if exists
// Otherwise default to liquibase.properties
System.getenv("LIQUIBASE_CHANGELOG")?.let { liquibaseProperties["changelogFile"] = it }
System.getenv("LIQUIBASE_REFERENCE_URL")?.let { liquibaseProperties["reference-url"] = it }
System.getenv("DB_USERNAME")?.let { liquibaseProperties["username"] = it }
System.getenv("DB_PASSWORD")?.let { liquibaseProperties["password"] = it }
System.getenv("DB_SCHEMA")?.let { liquibaseProperties["defaultSchemaName"] = it }
// Create database url
val liquibaseHost: String? = System.getenv("DB_HOST")
val liquibasePort: String? = System.getenv("DB_PORT")
// If DB_HOST and DB_PORT env vars are set, assume database name is 'exampledatabase'
val liquibaseDbName = System.getenv("DB_NAME") ?: "exampledatabase"
if (liquibaseHost != null && liquibasePort != null) {
liquibaseProperties["url"] = "jdbc:postgresql://$liquibaseHost:$liquibasePort/$liquibaseDbName"
} This block enables me to set the liquibase configuration in the liquibase.properties file and override all of the values with env vars for CI/CD and different environments. My Important Even though this is a changelogFile=src/main/resources/db/changelog-master.yaml
username=demo
password=123456
url=jdbc:postgresql://localhost:5432/example
defaultSchemaName=public
reference-url=hibernate:spring:au.com.example.package.entity?dialect=org.hibernate.dialect.PostgreSQLDialect
logLevel=info The hibernate reference url doco is here This will create all of the config needed to be able to register a basic liquibase task, however, if you want to be able to generate/diff changelogs then some config needs to be created to generate the changelogs into seperate sub files. Instead, I have a (very unoptimised) script for checking the default changelog folder for the last changelogs index tasks.withType(LiquibaseTask::class.java).forEach {
// it.doFirst is used to ensure that unique changelog names are only set when diffChangelog and generateChangelog are run
it.doFirst {
// If the task generates a changelog, change the changelog filepath to a new sub path
// and auto generate a unique name
if (it.name == "diffChangelog" || it.name == "generateChangelog") {
val changelogPath = "src/main/resources/db/changelogs/"
val existingChangelogs = Path(changelogPath).listDirectoryEntries("*.changelog.yaml")
var lastChangeLog = 0
existingChangelogs.forEach { path ->
// Get the auto generated preceding number, these define the run order for the changelog
val changelogNum = path.nameWithoutExtension.split(".")[0].toIntOrNull()
// If naming scheme has been broken and no integer exists, ignore the changelog.
// Otherwise, if it is the biggest so far then save it
if (changelogNum != null && changelogNum > lastChangeLog) {
lastChangeLog = changelogNum
}
}
// Enable a description for each file to be set when running this task, eg: `./gradlew generateChangelog -Pdescription=short-changelog-description`
val changelogDescription = when (val desc = properties["description"]) {
null -> ""
else -> ".$desc"
}
// Format the changelog file as 00.<provided description>.changelog.yaml
liquibaseProperties["changelogFile"] = changelogPath +
String.format("%02d", lastChangeLog + 1) +
changelogDescription +
".changelog.yaml"
}
// Finally, register the liquibase activity
liquibase.activities.register("main") { arguments = liquibaseProperties }
}
} That is all of my config I need for my build file. Root changelogThe final important bit of configuration is the databaseChangeLog:
- includeAll:
path: changelogs/
relativeToChangelogFile: true
endsWithFilter: .changelog.yaml
- includeAll:
path: dataseeds/
relativeToChangelogFile: true
endsWithFilter: .dataseed.yaml The dataseed files are just liquibase changelogs with This is the extent of my configuration, all of my hibernate entity classes are defined in the au.com.example.package.entity package. I'd love some feedback on where it could be improved/cleaned up - if there is anything else you were looking for config lmk |
I followed your example and other resources and finally got it to almost work... The arguments are passed in but are considered not supported by the command... What the hell is going on here? Using "org.liquibase.gradle" 3.0.1 and gradle wrapper version 8.8. |
I spent several hours researching how to make work this plugin on the stack:
I still face issues with making it work without exceptions.
Let's create an up-to-date example as documentation for this conventional dev stack, please.
I'm ready to help.
The text was updated successfully, but these errors were encountered: