Sometimes, when you use Hibernate in a project, you rely on hbm2ddl utility, it is fast and seems a very nice feature to manage database. However, when the project starts growing up it comes more difficult to maintain. I suffered it recently in a medium size project (150 KLoc) and thus I decided to search for a better option.
After some research, I found Liquibase and now I am convinced it is the right way. In this post I am going to tell you how I use Liquibase.
Of course, I don’t want to teach you all about Liquibase, www.liquibase.org is plenty of documentation. Instead, I tell you what I think Liquibase is good for, and how it saved my day.
Liquibase was designed to define and track DDL evolution independently of the RDBMS, so it is a database agnostic way to define and manage your database model. It has its own language to express ChangeSet. Liquibase groups ChangeSet into ChangeLog files. ChangeLog allows you to track database evolution. It is right to think about Liquibase as if it were a kind of CVS for databases.
Liquibase is also capable to obtain differences between two different databases, between a ChangeLog and a database, even between Hibernate Mappings and database!. Liquibase expresses differences as a set of ChangeSet that you can add to the ChangeLog.
Sample project
I create a very simple project, called Casiopea to show you how I am using Liquibase. You can find this project as a github repo Casiopea Project.
This project has no functional code, it only has minimal Maven, Spring, Hibernate and Liquibase configuration. It also has three domain entities, as depicted below, with its Hibernate mappings (xml mappings, not annotations).
As you can see in the class diagram, there are three entities: Person, Project and Role related through a MemberShip relationship entity. This model let me know the role every person plays in the projects they work. As said, it is only an example to show how to use Liquibase.
What I want from Liquibase?
I don’t want to waste my time, so I want to obtain SQL-DDL scripts from Hibernate mappings. Of course hbm2ddl is straight forward, but it doesn’t serve to compare databases nor create SQL migration scripts for production systems.
So I want Liquibase to create SQL-DDL scripts for several RDBMS, compare databases, update databases and create migration scripts. To be more precise, I want Liquibase in my project to work as following:
mvn compile liquibase:diff
: This Maven command compares Hibernate mappings against database and generates a ChangeSet that moves database to what Hibernate mappings expects.- The automatically generated ChangeSet must be revised by a developer so it can be included into the Liquibase ChangeLog file.
- After ChangeLog modification, it is possible to update database through
mvn copmile liquibase:update
.
It is worth to say that manual revision of the second step is very important. It is very useful to adjust some things as foreign key names, database dependent stuff, data types size, and so on. Of course, it is possible to obtain SQL script with liquibase:updateSQL
command.
Project configuration
If you want to leverage your project with Liquibase, you have to do following configuration.
Spring
Casiopea Project has an Spring application context descriptor (src/main/resources/app-context.xml
) with two beans:
DataSource
: which points to database.SessionFactory
: which holds Hibernate mappings.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
As you can see, lines 8-10 configure database url, username and password using Maven properties. Lines 18-20, point to mappings classpath location. It is important to define mappingLocations
property of LocalSessionFactoryBean
instead mappingResources
property. The second it is not recognized by liquibase-hiberante plugin.
Liquibase
Liquibase is configured by means of liquibase.properties
file in resources folder. Here you can find relevant lines:
1 2 3 4 5 6 7 |
|
Lines 3-6, define database connection as in Spring application context, that is, using Maven properties.
Line 2, defines diffChangeLogFile
property. This is used when Liquibase calculates differences between mappings and database. Liquibase will write diff in a unique file named from project version, developer name and timestamp maven compilation.
Maven POM
Here it is the main configuration. There are three main points:
- Maven profiles, to define database connection properties
- Liquibase plugin, to configure Liquibase execution and dependencies.
- Resource filtering, to replace maven variables in Spring, Liquibase and other configuration properties.
Maven profiles
Following is a fragment of the Maven POM file, where a profile called db-local
is defined:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
In this profile, Maven points to a local HsqlDB instance called casiopea
. You can easily change these values, or you can define new maven profiles to add more database connections.
If you want to start a local HsqlDB instance called casiopea
you can download HsqlDB jar and run the following command:
1
|
|
Liquibase plugin
Following is a fragment of the Maven POM file, where Liquibase plugin is configured:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Line 7, shows that liquibase.properties
is not read from resources path but from target. This is because of the Maven filtering process.
Resource filtering
Resource filtering is configured in maven as usual, including the files that contain Maven properties. You can check it at the end of the Maven pom, inside build section.
Running the project
Before running this project it is very important to say that it won’t work. There is a bug with the Liquibase Hibernate plugin that ignores Spring SessionFactory bean, when using org.springframework.orm.hibernate4.LocalSessionFactory
.
I documented this bug as #61. Until this bug won’t be resolved, Casiopea project does not work properly. Instead you could use a forked version of Liquibase Hibernate plugin where I solved this bug.
So, if you still want to use Casiopea project, you can clone my liquibase-hibernate fork and install it localy using mvn install
. This command will deploy in your local maven repository the 3.6-SNAPSHOT version that you need.
Once installed this dependecy, you can start a local instance of HsqlDB as explained above, and run liquibase:diff
. You will find a message similar to this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
Line 11, tells you Liquibase Hibernate plugin has found Spring application context and sessionFactory
bean.
Lines 15-20, list all mapped entities that are defined in sessionFactory
. Finally, line 24, shows where Liquibase has written automated ChangeSet. You can edit this file and found how good is Liquibase ;)
Now you can review the autolog and copy all ChangeSets to ChangeLog file. After that, you can run liquibase:update
or liquibase:updateSQL
and smile for a while.
I hope you enjoy reading this post, and obviously I would like you find it helpful.
Thank you for reading!