Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot: run liquibase migrations without starting app

In Spring Boot, the documentation seems to encourage running migrations on app startup.

This is fine, but sometimes app startup may have side effects / dependencies that I don't want to bother with - I just want to run the migrations on their own. Think just setting up a local dev database, to poke around in it, without even running the app.

In Dropwizard by comparison, running migrations alone is straightforward with built in arguments for the app, like so

java -jar hello-world.jar db migrate helloworld.yml

Is there anything equivalent for Spring Boot? Or do I just have to drop down and run liquibase directly?

I'm interested in a direct answer, but also kind of interested in seeing if I'm misunderstanding something at a higher level - like perhaps this approach of running on startup is generally 'better' for some reasons I haven't discovered yet, so you're encouraged solely to do it this way by Spring Boot as a design choice.

like image 334
davnicwil Avatar asked Mar 28 '18 10:03

davnicwil


3 Answers

I know this is an old question, but in case someone else stumbles upon it, this might be useful.

You can define a command line argument for your app, that you will use to only spin up a portion of the app context that will run migration.

Here's an example in Kotlin:

import org.springframework.boot.ApplicationContextFactory.ofContextClass
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.runApplication
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Import

@SpringBootApplication
class Application

@Import(DataSourceAutoConfiguration::class, LiquibaseAutoConfiguration::class)
class LiquibaseInit

fun main(args: Array<String>) {
    if (args.contains("dbinit")) {
        SpringApplicationBuilder(LiquibaseInit::class.java)
            .contextFactory(ofContextClass(AnnotationConfigApplicationContext::class.java))
            .run(*args)
        return
    }

    runApplication<Application>(*args)
}

Note: with Spring Boot 2.4.0 and earlier use SpringApplicationBuilder#contextClass instead of contextFactory.

We have 2 classes declared here: Application (the main app class having @SpringBootApplication annotation) and LiquiBaseInit (having @DataSourceAutoConfiguration and @LiquibaseAutoConfiguration), the first one will spin up the whole context, while the latter will only spin up the beans necessary for Liquibase to run migration.

Inside the main function we check if the arguments array has a string dbinit and if it is there we start an application context out of LiquiBaseInit class.

Now you can run migration with your jar file like this:

java -jar hello-world.jar dbinit

If you're going to run your app in Kubernetes, you might also want to check out this article in my blog: https://blog.monosoul.dev/2021/12/26/using-liquibase-with-kubernetes/ .

like image 184
Andrei Nevedomskii Avatar answered Oct 28 '22 11:10

Andrei Nevedomskii


You can use different Spring profiles: For instance, use a profile called 'init' which will activate the 'liquibase' profile.

application.yml: (disable Liquibase by default)

spring:
  liquibase:
    enabled: false

application-init.yml: (does not run a web container, so spring will close automatically after startup)

spring:
  profiles:
    include: liquibase
  main:
    web-application-type: none

spring-liquibase.yml: (enables liquibase)

spring:
  liquibase:
    enabled: true
    change-log: classpath:/db/changelog/changelog.xml

This setup allows you to run Liquibase as an init container (spring.profiles.active=init), but if you want to you can still run Liquibase as part of your web-app (spring.profiles.active=liquibase).

like image 3
blagerweij Avatar answered Oct 28 '22 09:10

blagerweij


This answer mentions a hook that runs after Liquibase. In that question, it was used to populate the database, probably with test or default values.

@Bean
@DependsOn("liquibase")
public YourBean yourBean() {
    return new YourBean();
}

static class YourBean {

    @PostConstruct
    public void shutdown() {
         // Exit your application here.
         );
    }

}

That could work. I don't know, you'd probably even have access to the liquibase mode and only shut down when it was "create."

like image 2
knallfrosch Avatar answered Oct 28 '22 11:10

knallfrosch