Friday, 2 April 2021

Liquibase Demostration with Spring Boot Application

The following demonstration is a Spring Boot application which only exposes a GET endpoint /users/:id. To integrate Liquibase, add a dependency for it in pom.xml. ``` <dependency> <groupId>org.liquibase</groupId> <artifactId>liquibase-core</artifactId> </dependency> ``` The changelog files are located in resources/db/changelog/. ![image](https://user-images.githubusercontent.com/35857179/107869366-52681700-6ec8-11eb-8d44-f2eb27a28ee2.png) Liquibase supports SQL, XML, JSON and YAML format for storing changelog files. The 00-db.changelog-master.yaml includes the changelog files for the releases in the correct order. 00-db.changelog-master.yaml ```yml databaseChangeLog: - include: file: 01-db.changelog-yaml-example.yaml relativeToChangelogFile: true - include: file: 02-db.changelog-sql-example.sql relativeToChangelogFile: true - include: file: 03-db.changelog-json-example.json relativeToChangelogFile: true - include: file: 04-db.changelog-xml-example.xml relativeToChangelogFile: true ``` 01-db.changelog-yaml-example.yaml Here's an example of changelog in YAML format. It creates a table called user_details with four fields – id, username, first_name, last_name. A ChangeSet is identified by id and author. For best practice, keep only one change per ChangeSet. Pre-conditions can also be added here. ```yml databaseChangeLog: - changeSet: id: create-table-user author: liquibase-demo-service preConditions: - onFail: MARK_RAN not: tableExists: tableName: user_details changes: - createTable: columns: - column: autoIncrement: true constraints: nullable: false primaryKey: true primaryKeyName: user_pkey name: id type: BIGINT - column: constraints: nullable: false name: username type: TEXT - column: constraints: nullable: false name: first_name type: TEXT - column: name: last_name type: TEXT tableName: user_details ``` 02-db.changelog-sql-example.sql Here's an example of changelog in SQL format. It must start with the line –liquibase formatted sql to provide liquibase with metadata, followed by arbitrary SQL statements with a comment of the form : ``--changeset author:id attribute1:value1 attribute2:value2 [...]``. ```sql --liquibase formatted sql --changeset liquibase-demo-service:add-user-name-constraint ALTER TABLE user_details ADD CONSTRAINT user_details_username_key UNIQUE (username); ``` 03-db.changelog-json-example.json Here's an example of changelog in JSON format. ```json { "databaseChangeLog": [ { "changeSet": { "id": "add-last-name-constraint", "author": "liquibase-demo-service", "changes": [ { "addNotNullConstraint": { "columnName": "last_name", "constraintName": "user_last_name_key", "tableName": "user_details" } }] } } ] } ``` 04-db.changelog-xml-example.xml Here's an example of changelog in XML format. It utilizes loadUpdateData to loads and update from a CSV file into an existing table. ``` <?xml version="1.1" encoding="UTF-8" standalone="no"?> <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.8.xsd"> <changeSet author="liquibase-docs" id="loadUpdateData-example" context="!prod"> <loadUpdateData encoding="UTF-8" relativeToChangelogFile="true" file="../data/users.csv" onlyUpdate="false" primaryKey="id" quotchar="'" separator="," tableName="user_details"> </loadUpdateData> </changeSet> </databaseChangeLog> ``` By default, Liquibase is enabled. Those changelog files will be executed at Spring Boot startup. To disable it, add the following property in src\main\resources\application.yaml. ``` spring: liquibase: enabled: false ``` To deploy any changes in the changelog files that have not been deployed to database yet, run the update goal. ``` ./mvnw org.liquibase:liquibase-maven-plugin:update \ -Dliquibase.url=jdbc:postgresql://localhost:5432/liquibasedemo?current_schema=public \ -Dliquibase.changeLogFile=src/main/resources/db/changelog/00-db.changelog-master.yaml \ -Dliquibase.username=demouser \ -Dliquibase.password=demopassword ``` ![image](https://user-images.githubusercontent.com/35857179/107869368-572ccb00-6ec8-11eb-9394-7eae484da444.png) After running update goal, four tables have been created. ![image](https://user-images.githubusercontent.com/35857179/107869369-598f2500-6ec8-11eb-8106-9cfa12dcf198.png) databasechangelog tracks which changeset have been run. ![image](https://user-images.githubusercontent.com/35857179/107869370-5b58e880-6ec8-11eb-890c-54b93cc9338a.png) databasechangeloglock ensures there is only one Liquibase is running at one time as multiple instances being executed against the same database concurrently will lead to conflicts. The table user_details is defined in 01-db.changelog-yaml-example.yaml, with constraints defined in 02-db.changelog-sql-example.sql and 03-db.changelog-json-example.json. The data is loaded in 04-db.changelog-xml-example.xml. ![image](https://user-images.githubusercontent.com/35857179/107869372-5dbb4280-6ec8-11eb-8cf0-77217ca33ce9.png) user_details_id_seq is a sequence table for user_details. ![image](https://user-images.githubusercontent.com/35857179/107869373-601d9c80-6ec8-11eb-96aa-52885e47b98f.png) To verify the result, start the Sprint Boot Application. ![image](https://user-images.githubusercontent.com/35857179/107869374-627ff680-6ec8-11eb-84ef-c8c89d2cb304.png) Get user with id 100000000 ![image](https://user-images.githubusercontent.com/35857179/107869375-64e25080-6ec8-11eb-8023-b9b12b2ae05d.png) Tag can be used to mark the current database state for rollback in the future. ![image](https://user-images.githubusercontent.com/35857179/107869380-6e6bb880-6ec8-11eb-96d0-97abe0c884ff.png) To create a tag, run the following goal ``` ./mvnw org.liquibase:liquibase-maven-plugin:tag \ -Dliquibase.tag=version1 \ -Dliquibase.url=jdbc:postgresql://localhost:5432/liquibasedemo?current_schema=public \ -Dliquibase.username=demouser \ -Dliquibase.password=demopassword ``` ![image](https://user-images.githubusercontent.com/35857179/107869381-70ce1280-6ec8-11eb-9060-ac59485cf0b2.png) A tag version has been added. ![image](https://user-images.githubusercontent.com/35857179/107869383-73306c80-6ec8-11eb-8997-d4df594ae9a3.png) There are three rollback modes – rollbackByTag, rollbackToDate and rollbackCount. · RollbackByTag reverts all changes made to the database after the specific tag. · RollbackToDate reverts all changes made to the database from the current date to the date and time you specify. · RollbackCount reverts the changes sequentially starting with the most recent changes. There are another modes such as rollbackOneChangeSet and rollbackOneUpdate but it requires Liquibase Pro. Take the first one as an example, supposing there is another changelog – adding middle_name in user_details. 05-add-middle-name.json Here we add the rollback statement to this changeset. ``` { "databaseChangeLog": [ { "changeSet": { "id": "add-middle-name", "author": "liquibase-demo-service", "changes":[{ "addColumn":{ "catalogName":"cat", "columns":[{ "column":{ "name":"middle_name", "type":"TEXT" } } ], "schemaName":"public", "tableName":"user_details" } } ], "rollback": [ { "sql": { "sql": "ALTER TABLE USER_DETAILS DROP COLUMN middle_name;" } } ] } } ]} ``` Deploy this changelog ``` ./mvnw org.liquibase:liquibase-maven-plugin:update \ -Dliquibase.url=jdbc:postgresql://localhost:5432/liquibasedemo?current_schema=public \ -Dliquibase.changeLogFile=src/main/resources/db/changelog/05-add-middle-name.json \ -Dliquibase.username=demouser \ -Dliquibase.password=demopassword ``` The column "middle_name" has been added. ![image](https://user-images.githubusercontent.com/35857179/107869386-7592c680-6ec8-11eb-8236-7de18624480c.png) The latest changelog is after the tag version1. ![image](https://user-images.githubusercontent.com/35857179/107869388-77f52080-6ec8-11eb-9171-257909b42f41.png) If we need to revert the changes after it, update 00-db.changelog-master.yaml to include the latest changelog. ``` - include: file: 05-add-middle-name.json relativeToChangelogFile: true ``` then run the following goal. ``` ./mvnw org.liquibase:liquibase-maven-plugin:tag \ -Dliquibase.tag=version1 \ -Dliquibase.url=jdbc:postgresql://localhost:5432/liquibasedemo?current_schema=public \ -Dliquibase.username=demouser \ -Dliquibase.password=demopassword ``` ![image](https://user-images.githubusercontent.com/35857179/107869389-79bee400-6ec8-11eb-94fd-4e32ff05b1ae.png) The record for 05-add-middle-name.json has been dropped. ![image](https://user-images.githubusercontent.com/35857179/107869390-7b88a780-6ec8-11eb-9868-23997a906655.png) The column middle_name has been dropped. ![image](https://user-images.githubusercontent.com/35857179/107869393-7d526b00-6ec8-11eb-9e5b-88a55ddc383f.png) Similarly, we can use RollbackToDate or RollbackCount to revert the changes. RollbackToDate liquibase:rollback -Dliquibase.rollbackCount=1 RollbackCount liquibase:rollback "-Dliquibase.rollbackDate=Feb 03, 2021" To conclude, we can define changelogs in 4 possible formats and run the Maven Goals to deploy them to database in the pipeline. We can also create a tag to mark the current state and rollback if necessary.

No comments:

Post a Comment

A Fun Problem - Math

# Problem Statement JATC's math teacher always gives the class some interesting math problems so that they don't get bored. Today t...