How to Write XML Changesets in Liquibase

How to Write XML Changesets in Liquibase

Examples for writing changesets in Liquibase

2020-09-21

If you want to have a basic understanding of Liquibase, I would suggest you look into our previous article on database version control with liquibase https://www.turtle-techies.com/database-version-controler-with-liquibase/.

We are assuming that you have already installed liquibase in your system. If you haven’t, please refer to this official documentation from liquibase https://www.liquibase.org/get-started/first-steps.

In this article, we will be discussing changelogs, changesets and some examples of how to write changesets in XML format. The database I am referring to while writing changesets is Oracle. You might need to change some of the values from code I provided according to the database you are using for example the driver detail, the connection parameter, data types etc.

Some background information

What is a changelog

A changelog is a collection of the changesets. Changelogs can be written in SQL, XML, JSON and YAML format.
Changelogs can include nested elements like preConditions, property, changeSet, include, context etc.

What is a changeset

Changeset in liquibase represents a single change to your database. Each changeset is executed in a transaction and will be either committed or rolled back depending on whether there is an error or not.
Each changeset is uniquely identified by “ID”, “AUTHOR” and the directory and filename of the changelog file.

Sample changelog file

Let’s start with organizing your changelog files

Changelog files can be managed in two ways - either to create a single changelog file and add all of your changesets to that file, or create multiple files and split the changesets across them.

In my experience, the latter is more manageable and has a lot of advantages.
Segregating changesets based on database objects and release versions help to manage the changes in a more organized way.

If you already have a base database and you want to implement liquibase to that project, you can add changesets for initial tables, data and procedures in different folders and manage database updates based on release versions. Managing changelog files based on release will help you to understand the changes that were part of the release and will be easier for other developers.

Working in an agile environment, here is an example of organizing changelogs that I use most and suggest others to follow. I also prefer organizing changesets in multiple files and using the include tag in the database changelog of master changelog file to define the files and order of execution.

Organizing your changelog files in a project

We can also define a master changelog file containing nested include elements to define the execution order of the files. An example of a master changelog file including other changeset files to run in order is given below.



<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">

    <include file="Initial-Changesets/Initial-Tables/Initial-Tables.xml" relativeToChangelogFile="true" />
    <include file="Initial-Changesets/Initial-Procedures/Initial-Procedures.xml" relativeToChangelogFile="true" />
    <include file="Initial-Changesets/Initial-Data/Initial-Data.xml" relativeToChangelogFile="true" />
    <include file="Updates/Release-1.1-Updates.xml" relativeToChangelogFile="true" />
    <include file="Updates/Release-1.2-Updates.xml" relativeToChangelogFile="true" />

</databaseChangeLog>


Here, I will explain to you different methods of writing changesets in liquibase with an example of two tables EMPLOYEE and DEPARTMENT.

1. How to create tables with liquibase XML

We can create one or more than one table in a single changeset. We can define NOT NULL constraint, Primary Key and Foreign Key etc inside the createTable tag. Constraints can also be defined outside the create table tag. Here is an example of creating tables DEPARTMENT and EMPLOYEES in a single changeset.



<changeSet author="Author1" id="1">
 <createTable tableName="DEPARTMENT">
   <column name="ID" type="NUMBER">
     <constraints nullable="false" primaryKey="true"/>
   </column>
   <column name="DEPT_NAME" type="VARCHAR2(100 BYTE)">
   <constraints nullable="false"/>
   </column>
   <column name="DEPT_DESCRIPTION" type="VARCHAR2(1000 BYTE)">
   </column>
 </createTable>

 <createTable tableName="EMPLOYEES">
   <column name="ID" type="NUMBER">
     <constraints nullable="false" primaryKey="true"/>
   </column>
   <column name="NAME" type="VARCHAR2(100 BYTE)">
   </column>
   <column name="DEPARTMENT_ID" type="NUMBER">
     <constraints nullable="false" foreignKeyName="DEPARTMENT_ID_FK" references="DEPARTMENT(ID)"/>
   </column>
   <column name="DESIGNATION" type="VARCHAR2(250 BYTE)"/>
   <column name="SALARY" type="NUMBER"/>
 </createTable>

 <addNotNullConstraint columnName="NAME" tableName="EMPLOYEES"/>
</changeSet>

While creating changesets, I would suggest using a single database change in changeset so that if there is an error in one database change in a changeset, we don’t need to roll back the whole changesets. For example, in the changeset given, if there is an error in EMPLOYEES table, Liquibase will fail after creating the DEPARTMENT table, so in the next execution after resolving the error it will throw an exception saying table DEPARTMENT has already been created. So, we need to manually rollback the changes that come before the one with the error.

2. Let’s insert some data using SQL file

Liquibase allows us to run the SQL stored in an external file. This is valuable in the event that when the changesets are not supported by Liquibase, if there should be an occurrence of bulk insertions and furthermore for the situation where users incline toward utilizing SQL contents over the other Liquibase changelog alternatives, as XML, JSON, and YAML. Here is how you can insert records in the table EMPLOYEES using an external SQL file.


<changeSet author="Author1" id="2">
 <sqlFile dbms="oracle"
          encoding="utf8"
          endDelimiter=";"
          path="Release1_SQL/insert_into_employees_file.sql"
          relativeToChangelogFile="true"
          splitStatements="true"
          stripComments="true"/>
</changeSet>

SQL File

---- DEPARTMENT
Insert into DEPARTMENT (ID,DEPT_NAME,DEPT_DESCRIPTION) values (1,'D1','D1 Department');

---- EMPLOYEES
Insert into EMPLOYEES (ID,NAME,DEPARTMENT_ID,DESIGNATION,SALARY) values (1,'John Doe',1,'Developer',50000);

3. How to write SQL statements using SQL tag

Liquibase also allows us to write SQL inside XML changeset to run whatever SQL you want. It can be useful in the cases when complex changesets aren’t supported by liquibase automated changesets. Below is an example of writing complex changes using the SQL tag.


<changeSet author="Author1" id="2">
 <sql dbms="oracle"
      endDelimiter=";"
      splitStatements="true"
      stripComments="true">
   MERGE INTO EMPLOYEES  dest
   USING  (SELECT 2   AS id, 'John Doe2'  AS Name,  1 AS DEPARTMENT_ID, 'Clerk'  AS DESIGNATION, 40000 AS SALARY  FROM   dual) src
   ON (dest.id = src.id )
   WHEN NOT matched THEN
   INSERT (dest.ID,dest.NAME,dest.DEPARTMENT_ID,dest.DESIGNATION,dest.SALARY) VALUES  (src.ID,src.NAME,src.DEPARTMENT_ID,src.DESIGNATION,src.SALARY) ;
 </sql>
</changeSet>

4. How to create Procedures and Triggers

Procedures and triggers can be created/run using the createProcedure tag in Liquibase or using custom SQL changesets. If you want to use the CREATE OR REPLACE feature in Liquibase, runOnChange=‘true’ can be set which will either create or update stored procedures while running.


<changeSet author="Author1" id="4" runOnChange="true">
   <createProcedure
           encoding="utf8"
           procedureName="LIST_EMPLOYEES_BY_DEPT">
<![CDATA[CREATE OR REPLACE PROCEDURE "LIST_EMPLOYEES_BY_DEPT" (
 P_PARAM1    IN EMPLOYEES.DEPARTMENT_ID%TYPE,
 RETURNCURSOR    OUT SYS_REFCURSOR) AS
BEGIN
 OPEN RETURNCURSOR FOR
 SELECT * 
 FROM EMPLOYEES
 WHERE DEPARTMENT_ID=P_PARAM1;
END LIST_EMPLOYEES_BY_DEPT;
]]>    
   </createProcedure>
</changeSet>

5. How to work with changelog parameters

Liquibase allows us to define global changelog parameters which will allow dynamic substitution of the parameter in the changelogs described using the ${parameter-name} syntax. There are many ways to configure parameter values including command line parameter, properties file, parameter block of the database changelog file etc.

Here is an example of using the changelog parameter in the liquibase.properties file:


changeLogFile: target/classes/changelog/master.xml
driver: oracle.jdbc.OracleDriver

url: ${schema.connection.url}
username: ${schema.user}
password: ${schema.password}

# ChangeLog Parameters
client.name: ${dept.name}

Using changelog parameter in changeSet:


<changeSet author="Author1" id="5">
 <sql dbms="oracle"
      endDelimiter=";"
      splitStatements="true"
      stripComments="true">
  UPDATE DEPARTMENT SET DEPT_DESCRIPTION='D1 Department New' WHERE DEPT_NAME= '${dept.name}' ;
 </sql>
</changeSet>

Changelog parameter in changelog file


<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">

    <property  name="table.name"  value="TEST_TABLE"/>  

    <changeSet  author="Author1"  id="6" >  
         <createTable  tableName="${table.name}">  
             <column  name="id"  type="int"/>  
             <column  name="column1"  type="varchar2(10)"/>  
             <column  name="column2"  type="int"/>  
         </createTable>  
    </changeSet>  
</databaseChangeLog>

That’s all folks!

As the application development process goes on, increasingly more database changes are done by the development team which should be kept up and overseen.
With these examples, you will be able to complete the majority of migration tasks for your database including object creation, data insertion and migration, composing DDL and DML utilizing SQL tag, defining the changelog parameters and so on.

Turtle Techies T shirt
Get your Turtle Techies swag