I usually post about Persistence on Twitter - you can follow me there:

1. Overview

In this quick tutorial, we’ll make use of Liquibase to evolve the database schema of a Java web application.

We’re going to focus on a general Java app first, and we’re also going to take a focused look at some interesting options available for Spring and Hibernate.

Very briefly, the core of using Liquibase is the changeLog file – an XML file that keeps track of all changes that need to run to update the DB.

Let’s start with the Maven dependency we need to add into our pom.xml:

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

You can also check if there’s a newer version of liquibase-core here.

2. The Database Change Log

Now, let’s take a look at a simple changeLog file – this one only adds a column “address” to the table “users“:

<databaseChangeLog 
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog" 
  xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext
   http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd 
   http://www.liquibase.org/xml/ns/dbchangelog 
   http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd">
    
    <changeSet author="John" id="someUniqueId">
        <addColumn tableName="users">
            <column name="address" type="varchar(255)" />
        </addColumn>
    </changeSet>
    
</databaseChangeLog>

Note how the change set is identified by an id and an author – to make sure it can be uniquely identified and only applied once.

Let’s not see how to wire this into our application and make sure that it runs when the application starts up.

3. Run Liquibase with a Spring Bean

Our first option to run the changes on application startup is via a Spring bean. There are of course many other ways, but if we’re dealing with a Spring application – this is a good, simple way to go:

@Bean
public SpringLiquibase liquibase() {
    SpringLiquibase liquibase = new SpringLiquibase();
    liquibase.setChangeLog("classpath:liquibase-changeLog.xml");
    liquibase.setDataSource(dataSource());
    return liquibase;
}

Note how we’re pointing it to a valid changeLog file that needs to exist on the classpath.

4. Use Liquibase with Spring Boot

If you are using Spring Boot, there is no need to define a Bean for Liquibase.

All you need is to put your change log in “db/changelog/db.changelog-master.yaml” and Liquibase migrations will run automatically on startup.

Note that:

  • You need to add “liquibase-core” dependency.
  • You can change default change log file using “liquibase.change-log” property – for example:
liquibase.change-log=classpath:liquibase-changeLog.xml

5. Generate the changeLog with a Maven Plugin

Instead of writing the changeLog file manually – we can use the Liquibase Maven plugin to generate one and save ourselves a lot of work.

5.1. Plugin Configuration

Here is the changes to our pom.xml:

<dependency>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-maven-plugin</artifactId>
    <version>3.4.1</version>
</dependency> 
...
<plugins>
    <plugin>
        <groupId>org.liquibase</groupId>
        <artifactId>liquibase-maven-plugin</artifactId>
        <version>3.4.1</version>
        <configuration>                  
            <propertyFile>src/main/resources/liquibase.properties</propertyFile>
        </configuration>                
    </plugin> 
</plugins>

5.2. Generate a changeLog from an existing Database

We can use the plugin to generate a changeLog from an existing database:

mvn liquibase:generateChangeLog

Here are the liquibase properties:

url=jdbc:mysql://localhost:3306/oauth_reddit
username=tutorialuser
password=tutorialmy5ql
driver=com.mysql.jdbc.Driver
outputChangeLogFile=src/main/resources/liquibase-outputChangeLog.xml

The end result is an changeLog file that we can use either to create an initial DB schema or to populate data. Here’s how that would look like for our example app:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog ...>
    
    <changeSet author="John (generated)" id="1439225004329-1">
        <createTable tableName="APP_USER">
            <column autoIncrement="true" name="id" type="BIGINT">
                <constraints primaryKey="true"/>
            </column>
            <column name="accessToken" type="VARCHAR(255)"/>
            <column name="needCaptcha" type="BIT(1)">
                <constraints nullable="false"/>
            </column>
            <column name="password" type="VARCHAR(255)"/>
            <column name="refreshToken" type="VARCHAR(255)"/>
            <column name="tokenExpiration" type="datetime"/>
            <column name="username" type="VARCHAR(255)">
                <constraints nullable="false"/>
            </column>
            <column name="preference_id" type="BIGINT"/>
            <column name="address" type="VARCHAR(255)"/>
        </createTable>
    </changeSet>
    ...
</databaseChangeLog>

5.3. Generate a changeLog from diff between two databases

We can use the plugin to generate a changeLog file from the differences between two existing databases (for example: development and production):

mvn liquibase:diff

Here are the properties:

changeLogFile=src/main/resources/liquibase-changeLog.xml
url=jdbc:mysql://localhost:3306/oauth_reddit
username=tutorialuser
password=tutorialmy5ql
driver=com.mysql.jdbc.Driver
referenceUrl=jdbc:h2:mem:oauth_reddit
diffChangeLogFile=src/main/resources/liquibase-diff-changeLog.xml
referenceDriver=org.h2.Driver
referenceUsername=sa
referencePassword=

And here’s a snippet of the generated changeLog:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog ...>
    <changeSet author="John" id="1439227853089-1">
        <dropColumn columnName="address" tableName="APP_USER"/>
    </changeSet>
</databaseChangeLog>

This is a super powerful way to evolve your DB by – for example – allowing Hibernate to auto-generate a new schema for development, and then using that as a reference point against the old schema.

6. Use the Liquibase Hibernate Plugin

If the application uses Hibernate – we’re going to take a look at a very useful way of generating the changeLog.

First – here’s how the liquibase-hibernate plugin should be configured in Maven:

6.1. Plugin Configuration

First, let’s get the new plugin configured and using the right dependencies:

<plugins>
    <plugin>
        <groupId>org.liquibase</groupId>
        <artifactId>liquibase-maven-plugin</artifactId>
        <version>3.4.1</version>
        <configuration>                  
            <propertyFile>src/main/resources/liquibase.properties</propertyFile>
        </configuration> 
        <dependencies>
            <dependency>
                <groupId>org.liquibase.ext</groupId>
                <artifactId>liquibase-hibernate4</artifactId>
                <version>3.5</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>4.1.7.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-jpa</artifactId>
                <version>1.7.3.RELEASE</version>
            </dependency>
        </dependencies>               
    </plugin> 
</plugins>

6.2. Generate a changeLog from diffs between a Database and Persistence Entities

Now, for the fun part. We can use this plugin to generate a changeLog file from the differences between an existing database (for example production) and our new persistence entities.

So – to make things simple – once an entity is modified, you can simply generate the changes against the old DB schema, getting a clean, powerful way to evolve your schema in production.

Here are the liquibase properties:

changeLogFile=classpath:liquibase-changeLog.xml
url=jdbc:mysql://localhost:3306/oauth_reddit
username=tutorialuser
password=tutorialmy5ql
driver=com.mysql.jdbc.Driver
referenceUrl=hibernate:spring:org.baeldung.persistence.model
  ?dialect=org.hibernate.dialect.MySQLDialect
diffChangeLogFile=src/main/resources/liquibase-diff-changeLog.xml

Note: The referenceUrl is using package scan, so the dialect parameter is required.

7. Conclusion

In this tutorial we illustrated several ways to use Liquibase and get to a safe and mature way of evolving and refactoring the DB schema of a Java app.

The implementation of all these examples and code snippets can be found in my github project – this is an Eclipse based project, so it should be easy to import and run as it is.

I usually post about Persistence on Twitter - you can follow me there:


Sort by:   newest | oldest | most voted
David Morley
Guest

Just a minor correction: it’s Liquibase, not Liquidbase.

Love the blog!

Eugen Paraschiv
Guest

Hey David – nice catch, I really do need to double check spelling 🙂
Thanks,
Eugen.

Bogdan Marian
Guest

Great article, Eugen!

Micha Kops has written also a Liquibase article, but using JEE7, not Spring: http://www.hascode.com/2014/07/java-ee-7-database-migrations-with-liquibase-and-wildfly/.
A rather good alternative to Liquibase is Flyway (http://flywaydb.org/), for which Micha has written an article too: http://www.hascode.com/2013/04/easy-database-migrations-using-flyway-java-ee-6-and-glassfish/.

Eugen Paraschiv
Guest

Hey Bogdan – yeah, I remember that article – thanks for the refresh – he’s definitely an author I follow.
As for Flyway – I’ve used it in the past and liked it, so I’m going to add it to my content calendar. Cheers,
Eugen.

Jimo
Guest

I tried the described in 6 – diff between entities and DB, but it doesn’t work. I used the plugin configuration described in 6.1, but it throws NullPointerException. Same for plugin version 3.4.0. When I changed it to 3.3.5, the NPE disappeared, but it started working incorrectly. E.g. – it doesn’t recognize CatalogItem entity as ORM for the CATALOG_ITEM DB table and produces dropTable for CATALOG_ITEM and createTable for CATALOGITEM. Can anybody provide a plugin pom configuration that works?

Eugen Paraschiv
Guest

Hey Jimo – that’s interesting – this particular configuration is one I’ve been using on an on-going basis. So – if you can have a look at the project over on github and reproduce the issue – I’d be happy to have a look. Cheers,
Eugen.

Amit Ghorpade
Guest

Hey Eugen,
I was looking for an option where I want to run migration on database but when I want it, In Spring boot/Spring configuration liquibase migration runs when application starts.
Is there any way we can run liquibase migration on specific event?

Eugen Paraschiv
Guest
Hey Amit – sure, you can definitely do that. Spring Boot is just one way of running these migrations – but there are definitely other ways. For example, the article also shows how you can define your config as a bean – at which point you can use that bean programmatically – wherever you need. You can for example use @Scheduled and run that bean according to some cron expression. Or you can even have that in a separate executable app that simply bootstraps the context, runs the migration and finishes. That way you can drive the flow from a… Read more »
Amit Ghorpade
Guest

Hey Eugen,
Thanks for those options and help. I came to know that we have disable auto configuration of liquibase with spring boot and then run migration on demand.
For reference :- https://macbloglive.wordpress.com/ way we can run liquibase migration on demand.

Eugen Paraschiv
Guest

Nice new site – looks cool and the info is of course useful.

Calvin P
Guest

Hello, thanks for your post it was very helpful.

Regarding spring boot and liquibase integration, you mention that all you need to do is add it as a dependency and the migration will happen on start up. But how do you change the default liquibase databasechangelogtable and databsaechangeloglocktable names?

Thanks,
Calvin

Grzegorz Piwowarek
Guest

As far as I remember there was a possibility of achieving this by changing some properties. Quick googling gave me properties:

liquibase.databaseChangeLogTableName
liquibase.databaseChangeLogLockTableName

so:
-Dliquibase.databaseChangeLogTableName=newname
-Dliquibase.databaseChangeLogLockTableName=newname

wpDiscuz