Liquibase
runs the update
command to execute the ChangeSet
and with each successful execution, it adds a row in DATABASECHANGELOG
table with the execution date
, deployment ID
, and MD5 checksum
to keep track of executed ChangeSet
. each ChangeSet
is distinguished by unique id
property.
<changeSet author="wesome (generated)" id="1692523877432-1">
let's see with an example, below is the ChangeLog
file for creating a table ChangeSet
with id=1692523877432-1
, and an insert record ChangeSet
with id=1692523877432-1
.
changelog.mysql.xml
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" 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/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet author="wesome (generated)" id="1692523877432-1">
<createTable catalogName="appledb" tableName="apple">
<column name="apple_id" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="apple_name" type="VARCHAR(255)"/>
<column defaultValueComputed="CURRENT_TIMESTAMP" name="current_date_time" type="datetime"/>
</createTable>
</changeSet>
<changeSet author="wesome (generated)" id="1692523877432-2">
<insert catalogName="appledb" tableName="apple">
<column name="apple_id" valueNumeric="1"/>
<column name="apple_name" value="Macintosh"/>
<column name="current_date_time" valueDate="now()"/>
</insert>
</changeSet>
</databaseChangeLog>
liquibase.properties (update changeLogFile file format with SQL, XML, YAML, JSON as per changelog.mysql.<format>)
changeLogFile=changelog.mysql.xml
liquibase.command.url:jdbc:mysql://localhost:3306/AppleDb?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
liquibase.command.username: root
liquibase.command.password: rootroot
run update command
liquibase update
Liquibase
has printed the number of ChangeSet
executed in the logs
Running Changeset: changelog.mysql.xml::1692523877432-1::wesome (generated)
Running Changeset: changelog.mysql.xml::1692523877432-2::wesome (generated)
UPDATE SUMMARY
Run: 2
Previously run: 0
Filtered out: 0
-------------------------------
Total change sets: 2
Liquibase: Update has been successful.
query DATABASECHANGELOG
table, the id
column has the same values of id
attributes of ChangeSet
.
select * from appledb.DATABASECHANGELOG;
How Liquibase checks for existing changeset id
in consecutive deployments, Liquibase
matches for id column of DATABASECHANGELOG
with the ChangeSet id
, and if the same then Liquibase
will ignore the ChangeSet
and execute the next one. let's see the same with an example
below is the ChangeLog
file contains 2 ChangeSet
, 1 for creating a table ChangeSet
with id=1692523877432-1
, and another for inserting a record ChangeSet
with id=1692523877432-1
.
changelog.mysql.xml
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" 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/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet author="wesome (generated)" id="1692523877432-1">
<createTable catalogName="appledb" tableName="apple">
<column name="apple_id" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="apple_name" type="VARCHAR(255)"/>
<column defaultValueComputed="CURRENT_TIMESTAMP" name="current_date_time" type="datetime"/>
</createTable>
</changeSet>
<changeSet author="wesome (generated)" id="1692523877432-2">
<insert catalogName="appledb" tableName="apple">
<column name="apple_id" valueNumeric="1"/>
<column name="apple_name" value="Macintosh"/>
<column name="current_date_time" valueDate="now()"/>
</insert>
</changeSet>
</databaseChangeLog>
liquibase.properties (update changeLogFile file format with SQL, XML, YAML, JSON as per changelog.mysql.<format>)
changeLogFile=changelog.mysql.xml
liquibase.command.url:jdbc:mysql://localhost:3306/AppleDb?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
liquibase.command.username: root
liquibase.command.password: rootroot
run the update command
liquibase update
Liquibase
has printed the number of ChangeSet
executed in the logs
Running Changeset: changelog.mysql.xml::1692523877432-1::wesome (generated)
Running Changeset: changelog.mysql.xml::1692523877432-2::wesome (generated)
UPDATE SUMMARY
Run: 2
Previously run: 0
Filtered out: 0
-------------------------------
Total change sets: 2
Liquibase: Update has been successful.
query DATABASECHANGELOG
table, the id
column has the same values of id
attributes of ChangeSet
.
select * from appledb.DATABASECHANGELOG;
Now let's see the Apple
table in the AppleDB
schema.
select * from appledb.apple;
It is showing an apple
table with 1 record inserted, run the update
command again and notice the logs
liquibase update
Liquibase
has printed the number of ChangeSet
executed in the logs
Running Changeset: changelog.mysql.xml::1692523877432-1::wesome (generated)
Running Changeset: changelog.mysql.xml::1692523877432-2::wesome (generated)
UPDATE SUMMARY
Run: 0
Previously run: 2
Filtered out: 0
-------------------------------
Total change sets: 0
Liquibase: Update has been successful.
Liquibase
matched the ChangeSet
id with the id of DATABASECHANGELOG
table and noticed both the changeset has been already executed hence skip both.
Liquibase skip already executed the ChangeSet but run a new changeset
We learned that Liquibase
ignores the expected changeset, but what will happen if a new ChangeSet
with a new id
appears in the changelog file? well Liquibase
will ignore previously executed ChangeSet
as per default behavior but will execute the new ChangeSet
.
Let's update the same ChangeLog
file and append another insert record ChangeSet
in the same ChangeLog
file keeping the existing ChangeSet
intact.
changelog.mysql.xml
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" 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/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet author="wesome (generated)" id="1692523877432-1">
<createTable catalogName="appledb" tableName="apple">
<column name="apple_id" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="apple_name" type="VARCHAR(255)"/>
<column defaultValueComputed="CURRENT_TIMESTAMP" name="current_date_time" type="datetime"/>
</createTable>
</changeSet>
<changeSet author="wesome (generated)" id="1692523877432-2">
<insert catalogName="appledb" tableName="apple">
<column name="apple_id" valueNumeric="1"/>
<column name="apple_name" value="Macintosh"/>
<column name="current_date_time" valueDate="now()"/>
</insert>
</changeSet>
<changeSet author="shriksha (generated)" id="1692523877432-3">
<insert catalogName="appledb" tableName="apple">
<column name="apple_id" valueNumeric="2"/>
<column name="apple_name" value="Fuji"/>
<column name="current_date_time" valueDate="now()"/>
</insert>
</changeSet>
</databaseChangeLog>
liquibase.properties (update changeLogFile file format with SQL, XML, YAML, JSON as per changelog.mysql.<format>)
changeLogFile=changelog.mysql.xml
liquibase.command.url:jdbc:mysql://localhost:3306/AppleDb?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
liquibase.command.username: root
liquibase.command.password: rootroot
run the update command
liquibase update
Liquibase
matched the existing ChangeSet
with DATABASECHANGELOG
table and noticed that the ChangeSet
id 1692523877432-1 and 1692523877432-2 has already been executed before hence skipped those and ran the latest one. printed the number of ChangeSet
executed in the logs
Running Changeset: changelog.mysql.xml::1692523877432-3::wesome (generated)
UPDATE SUMMARY
Run: 1
Previously run: 2
Filtered out: 0
-------------------------------
Total change sets: 3
Liquibase: Update has been successful.
query DATABASECHANGELOG
table, the id column has the same values of id attributes of ChangeSet
.
select * from appledb.DATABASECHANGELOG;
Now let's see the Apple table in the AppleDB schema.
select * from appledb.apple;
It is showing apple
table with 2 records inserted, the new ChanegSet
has been inserted without actually modifying the existing data.
Run changeset every time irrespective of previous executions with runAlways attribute
Some times requirement is to execute a ChangeSet
every time irrespective of its previous successful executions, such as updating the TimeStamp
, restoring default values after every time interval, etc. Liquibase
provides a runAlways
attribute to run ChangeSet
every time deployment happens.
Let's take the same example as above, below CahngeLog
has 3 ChangeSet
of table creation, record insertion, and record updation.
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" 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/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet author="wesome (generated)" id="1692523877432-1">
<createTable catalogName="appledb" tableName="apple">
<column name="apple_id" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="apple_name" type="VARCHAR(255)"/>
<column defaultValueComputed="CURRENT_TIMESTAMP" name="current_date_time" type="datetime"/>
</createTable>
</changeSet>
<changeSet author="wesome (generated)" id="1692523877432-2">
<insert catalogName="appledb" tableName="apple">
<column name="apple_id" valueNumeric="1"/>
<column name="apple_name" value="Macintosh"/>
<column name="current_date_time" valueDate="now()"/>
</insert>
</changeSet>
<changeSet author="wesome (generated)" id="1692523877432-3" runAlways="true">
<update catalogName="appledb" tableName="apple">
<column name="current_date_time" valueDate="now()"/>
</update>
</changeSet>
</databaseChangeLog>
liquibase.properties (update changeLogFile file format with SQL, XML, YAML, JSON as per changelog.mysql.<format>)
changeLogFile=changelog.mysql.xml
liquibase.command.url:jdbc:mysql://localhost:3306/AppleDb?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
liquibase.command.username: root
liquibase.command.password: rootroot
run the update command
liquibase update
Liquibase
has printed the number of ChangeSet
executed in the logs
Running Changeset: changelog.mysql.xml::1692523877432-1::wesome (generated)
Running Changeset: changelog.mysql.xml::1692523877432-2::wesome (generated)
UPDATE SUMMARY
Run: 1
Previously run: 2
Filtered out: 0
-------------------------------
Total change sets: 3
Liquibase: Update has been successful.
Query the Apple table in the AppleDB schema.
select * from appledb.apple;
Notice the date column of the apple table, the record has been updated.
runAlways only works with the update command, not create or insert.
as per the above examples, the attribute runAlways
is working fine with the updated changeset, because it has to update the existing record every time.
<changeSet author="wesome (generated)" id="1692523877432-3" runAlways="true">
<update catalogName="appledb" tableName="apple">
<column name="current_date_time" valueDate="now()"/>
</update>
</changeSet>
but if attribute runAlways
added with create or insert attribute then it runs perfectly for the first time but will throw an error from the second deployment onwards.
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" 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/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet author="shriksha (generated)" id="1692523877432-1" runAlways="true">
<createTable catalogName="appledb" tableName="apple">
<column name="apple_id" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="apple_name" type="VARCHAR(255)"/>
<column defaultValueComputed="CURRENT_TIMESTAMP" name="current_date_time" type="datetime"/>
</createTable>
</changeSet>
</databaseChangeLog>
run the update command
liquibase update
Liquibase
has printed the number of ChangeSet
executed in the logs
Running Changeset: changelog.mysql.xml::1692523877432-1::shriksha (generated)
UPDATE SUMMARY
Run: 1
Previously run: 0
Filtered out: 0
-------------------------------
Total change sets: 1
Liquibase: Update has been successful.
Liquibase
has successfully executed the ChangeSet
for the first time. now run the update command again.
liquibase update
Liquibase
has printed the number of ChangeSet
executed in the logs
Running Changeset: changelog.mysql.xml::1692523877432-1::shriksha (generated)
Unexpected error running Liquibase: Migration failed for changeset changelog.mysql.xml::1692523877432-1::shriksha (generated):
Reason: liquibase.exception.DatabaseException: Table 'apple' already exists [Failed SQL: (1050) CREATE TABLE appledb.apple (apple_id BIGINT NOT NULL, apple_name VARCHAR(255) NULL, current_date_time datetime DEFAULT NOW() NULL, CONSTRAINT PK_APPLE PRIMARY KEY (apple_id))]
For more information, please use the --log-level flag
The execution will fail because runAlways
trying to create a table that already exists in the database.