How To Implement Automatic Database Updates By Integrating Liquibase

By Thorben Janssen


After I talked about how to add Liquibase to your project and how to update your database together with your application code, it’s time to integrate Liquibase into your application so that you can execute the update automatically. Special thanks to Carlos Feria who asked for this post in a comment.

The automatic execution of the update scripts is especially interesting when you build smaller applications that don’t run in a highly regulated enterprise environment. In these situations, it’s often not possible to run the update process yourself and there might be no operations team which executes the SQL scripts. So, you need to run the database update automatically when you start your application.

There are several ways to do that. If you’re building a plain Java application, you can use the Liquibase API to trigger the update. And it gets even easier, if you’re using a CDI container, e.g. in a Java EE application server, or Spring. I will show you all 3 approaches in this post.

Let’s start with the plain Java environment.

Run Liquibase as part of a Java SE application

Before you can use the Liquibase API, you need to add the required dependencies to your application. The following maven coordinates add the Liquibase core component in version 3.5.3 to your project.

<dependency>
	<groupId>org.liquibase</groupId>
	<artifactId>liquibase-core</artifactId>
	<version>3.5.3</version>
</dependency>

After you’ve done that, you can implement the database migration in 3 steps:

  1. Get a database connection
  2. Initialize Liquibase
  3. Run the update

Integrate Liquibase into Hibernate’s bootstrapping process

Getting the database connection obviously depends on your environment and technology stack. In this post, I will show you how to do that with Hibernate 5.

Bootstrapping Hibernate

If you’re using Hibernate, you already configured the database connection in your Hibernate configuration. You probably also want to use Hibernate’s database validation to make sure that your mapping fits the database. You can do that by adding Liquibase to your startup process and execute the update before you create a SessionFactory.

// Prepare the Hibernate configuration
StandardServiceRegistry reg = new StandardServiceRegistryBuilder().configure().build();
MetadataSources metaDataSrc = new MetadataSources(reg);

// Get database connection
Connection con = metaDataSrc.getServiceRegistry().getService(ConnectionProvider.class).getConnection();
JdbcConnection jdbcCon = new JdbcConnection(con);

// Initialize Liquibase and run the update
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(jdbcCon);
Liquibase liquibase = new Liquibase("db.changelog.xml", new ClassLoaderResourceAccessor(), database);
liquibase.update("test");

// Create Hibernate SessionFactory
sf = metaDataSrc.addAnnotatedClass(Author.class).addAnnotatedClass(Book.class).buildMetadata().buildSessionFactory();

You first need to build a StandardServiceRegistry and use it to instantiate a MetadataSources object. I don’t provide any configuration when calling these methods and Hibernate reads the hibernate.cfg.xml file from the classpath.

In the next step, you can use the MetadataSources object to get an instance of a ConnectionProvider service and retrieve a java.sql.Connection. Hibernate created this connection based on the configuration data in the hibernate.cfg.xml file. You can then use the Connection object to create a Liquibase-specific JdbcConnection.

Now you’ve everything you need to initialize Liquibase. You first need to create a Database object and provide the name of the changelog file, a ClassLoaderResourceAccessor instance and the database object. Then, you can call the update method with a reference to the context you want to use for your database update.

After you’ve updated your database, you can follow Hibernate’s standard bootstrapping process. You therefore use the MetadataSources object to build your MetaData and build a SessionFactory.

That’s all you need to do to integrate Liquibase into your Java SE application. Every time you start the application, Liquibase will check the database and perform the required updates.

Run Liquibase with Spring Boot

Integrating Liquibase into your Spring Boot application is extremely easy. You just have to add the Liquibase Core to your classpath.

<dependency>
	<groupId>org.liquibase</groupId>
	<artifactId>liquibase-core</artifactId>
	<version>3.5.3</version>
</dependency>

That’s all you need to do. The Liquibase integration will automatically load the master changelog file from db/changelog/db.changelog-master.yaml.

Run Liquibase with a CDI container

The CDI integration is also a lot easier than integrating Liquibase into a plain Java SE application. But it requires a little more work than the Spring Boot integration.

You need to add the following 2 dependencies to your project and include them in your deployment.

<dependency>
	<groupId>org.liquibase</groupId>
	<artifactId>liquibase-core</artifactId>
	<version>3.5.3</version>
</dependency>
<dependency>
	<groupId>org.liquibase</groupId>
	<artifactId>liquibase-cdi</artifactId>
	<version>3.5.3</version>
</dependency>

After you’ve done that, you need to implement a CDI bean with 3 producer methods. These methods need to provide a CDILiquibaseConfig, a DataSource and a ResourceAccessor.

As you can see in the following code snippet, the produced CDILiquibaseConfig object contains a reference to the master changelog file. So, please make sure that you reference a file within your deployment unit or that you use an external path which you can access from within the CDI container on all machines. I explained the content of this file in more details in the first and second post of this series.

@Dependent
public class LiquibaseCdiIntegration {

    @Resource
    private DataSource myDataSource;

    @Produces 
    @LiquibaseType
    public CDILiquibaseConfig createConfig() {
        CDILiquibaseConfig config = new CDILiquibaseConfig();
        config.setChangeLog("//c:/tmp/db.changelog.xml");
        return config;
    }

    @Produces 
    @LiquibaseType
    public DataSource createDataSource() throws SQLException {
        return myDataSource;
    }

    @Produces 
    @LiquibaseType
    public ResourceAccessor create() {
        return new ClassLoaderResourceAccessor(getClass().getClassLoader());
    }
}

That’s all you need to do. When you deploy this application into a CDI container, Liquibase will run during the deployment and update the database.

Summary

As you’ve seen in this post, you can integrate Liquibase into your application. Doing that allows you to automatically check and update the database every time you start your application.


Tags


About the author

Thorben is an independent consultant, international speaker, and trainer specialized in solving Java persistence problems with JPA and Hibernate.
He is also the author of Amazon’s bestselling book Hibernate Tips - More than 70 solutions to common Hibernate problems.

Books and Courses

Coaching and Consulting

Leave a Reply

Your email address will not be published. Required fields are marked

This site uses Akismet to reduce spam. Learn how your comment data is processed.

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}