Schema migration with Liquibase and Micronaut

Developing programmer Development Website design and coding technologies working
Reading Time: 3 minutes

In today’s world of microservices, each microservice has its own set of information stored in some kind of data store. This data store can be either a database or some other system that can retain the data for a period of time. We moved to microservices because we didn’t want one set of functionality not to be impacted by the changes in other functionality.

After this migration to microservices then comes the question of how can we track the changes in microservices like code changes and database schema changes. There are a couple of solutions available in the market to track these kinds of changes. Here, in this article, we will look at one of the solutions to track the database schema changes using Liquibase in a micronaut service.

Create a new micronaut application

Create an application using the Micronaut Command Line Interface or with Micronaut Launch. The previous command creates a Micronaut application with the default package com.knoldus.micronaut in a directory named micronaut-with-liquibase.

mn create-app

   com.knoldus.micronaut.micronaut-with-liquibase \

   --features=data-jdbc,postgres,liquibase \

   --build=maven \

   --lang=java \

Create new Entity

Create a new database entity to save the employee

package com.knoldus.micronaut.entity;


import io.micronaut.core.annotation.NonNull;

import io.micronaut.core.annotation.Nullable;

import io.micronaut.data.annotation.GeneratedValue;

import io.micronaut.data.annotation.Id;

import io.micronaut.data.annotation.MappedEntity;

import io.micronaut.data.annotation.Version;


import javax.validation.constraints.NotBlank;


@MappedEntity //Map the class to the table defined in the schema

public class Employee {

    @Id //Specifies the ID of an entity

    @GeneratedValue(value = GeneratedValue.Type.AUTO)

    private Long id;

    @Version //To enable optimistic locking for your entity

    private Long version;

    @NonNull

    @NotBlank

    private String name;

    @Nullable

    private int age;


    public Employee(@NonNull String name, @Nullable int age) {

        this.name = name;

        this.age = age;
    }

    public Long getId() {

        return id;

    }

    public Long getVersion() {

        return version;

    }

    @NonNull

    public String getName() {


        return name;

    }

    @Nullable

    public int getAge() {

        return age;

    }

    public void setVersion(Long version) {

        this.version = version;

    }

}

Configure Liquibase

Configure the database migrations directory for Liquibase in application.yml.

src/main/resources/application.yml

liquibase:

  enabled: true

  datasources:

    default:

      change-log: 'classpath:db/liquibase-changelog.xml'

Create the following files with the database schema creation:

src/main/resources/db/liquibase-changelog.xml

<?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="changelog/create-employee.xml"

             relativeToChangelogFile="true"/>

</databaseChangeLog>

src/main/resources/db/changelog/create-employee.xml

<?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">



    <changeSet id="01" author="vimalk">



        <createTable tableName="employee"

                     remarks="A table to contain employee information">



            <column name="id" type="BIGINT">

                <constraints nullable="false"

                             unique="true"

                             primaryKey="true"

                             primaryKeyName="employeePK"/>

            </column>



            <column name="version" type="BIGINT">

                <constraints nullable="false"/>

            </column>



            <column name="age" type="INT">

                <constraints nullable="false"/>

            </column>



        </createTable>



    </changeSet>



</databaseChangeLog>

Run the application, the SQL file is run by Liquibase, which also produces the application’s necessary schema.

The database schema has below three tables:

  • databasechangelog
  • databasechangeloglock
  • employee

The tables databasechangelog and databasechangeloglock are used by Liquibase to keep track of database migrations. The employee table will look like this:

After the changeset, the employee table looks like:

ColumnNullable
idNO
versionNO
nameNO
ageNO

Make changes in the schema

We will make the age attribute nullable.

src/main/java/com/knoldus/micronaut/entity/Employee.java

    @Nullable

	private final Integer age;


	public Employee(@NonNull String name,

				  @Nullable Integer age) {

		this.name = name;

		this.age = age;

	}


	@Nullable

	public Integer getAge() {

		return age;

}

Add a new changeset to drop the null constraint. Update the liquibase-changelog.xml as per below snippet

src/main/resources/db/liquibase-changelog.xml

<?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="changelog/create-employee.xml"

             relativeToChangelogFile="true"/>



    <include file="changelog/nullable-age.xml"

             relativeToChangelogFile="true"/>



</databaseChangeLog>

src/main/resources/db/changelog/nullable-age.xml

<?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">



    <changeSet id="02" author="vimalk">

        <dropNotNullConstraint tableName="employee"

                               columnName="age"/>

    </changeSet>



</databaseChangeLog>

After the changeset, the employee table looks like:

ColumnNullable
idNO
versionNO
nameNO
ageYES

Enable Liquibase endpoint

To enable the Liquibase endpoint, add the management dependency on your classpath.

pom.xml

<dependency>

    <groupId>io.micronaut</groupId>

    <artifactId>micronaut-management</artifactId>

    <scope>compile</scope>

</dependency>

src/main/resources/application.yml

endpoints:

  liquibase:

    enabled: true

    sensitive: false

Go to endpoint http://localhost:8080/liquibase, below response can be expected

[

	{

		"name": "default",

		"changeSets": [

			{

				"id": "01",

				"tag": null,

				"author": "vimal",

				"comments": "",

				"dateExecuted": "2022-12-18T08:44:55.503Z",

				"deploymentId": "1353095487",

				"execType": "EXECUTED",

				"storedChangeLog": "db/changelog/create-employee.xml",

				"checksum": "8:6c2a1870c799ec202c52ee47dbd2d59d",

				"orderExecuted": 1,

				"contexts": [],

				"labels": [],

				"changeLog": "db/changelog/create-employee.xml",

				"description": "createTable tableName=employee"

			},

			{

				"id": "02",

				"tag": null,

				"author": "vimal",

				"comments": "",

				"dateExecuted": "2022-12-18T08:45:30.761Z",

				"deploymentId": "1353130749",

				"execType": "EXECUTED",

				"storedChangeLog": "db/changelog/nullable-age.xml",

				"checksum": "8:264ea35118fea6e138edbee863c37dd2",

				"orderExecuted": 2,

				"contexts": [],

				"labels": [],

				"changeLog": "db/changelog/nullable-age.xml",

				"description": "dropNotNullConstraint columnName=age, tableName=employee"

			}

		]

	}

]

Conclusion

We’ve seen that with a few configuration files we can easily track schema changes. With micronaut in dev mode, we do not need to restart the server again and again to detect the changes in the liquibase-changelog.xml. You can find the source code of this Github location.

References

https://micronaut-projects.github.io/micronaut-liquibase/latest/guide/

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading