Tags: liquibase, database, migrations, ci-cd, sql, xml, yaml
Level: intermediate
TL;DR: Liquibase is an open-source database schema change management tool that enables trackable, version-controlled, and repeatable database migrations across multiple environments.
Uses a changelog file (supported in XML, YAML, JSON, or SQL formats) to define database changes sequentially.
Each change is defined as a changeset, uniquely identified by an id, an author, and the file path.
Tracks executed changesets in the DATABASECHANGELOG table to prevent applying the same change twice.
Uses the DATABASECHANGELOGLOCK table to ensure only one Liquibase instance runs migrations at any given time, preventing race conditions.
Supports database-agnostic changes when using XML/YAML/JSON, automatically translating them into platform-specific SQL.
Enables dry runs and SQL previews using commands like updateSql to review changes before applying them.
Allows defining rollback statements to revert changes if a deployment fails or needs to be undone.
Integrates seamlessly with CI/CD pipelines, Spring Boot, Maven, Gradle, and Docker.
Q: How does Liquibase keep track of what has already been executed?R: It reads the DATABASECHANGELOG table. Every time a changeset runs successfully, it inserts a record with the changeset's ID, author, and MD5 checksum. Before running migrations, Liquibase checks this table and skips anything already applied.
Q: What happens if I modify an already executed changeset?R: Liquibase will calculate a new MD5 checksum, compare it to the one stored in DATABASECHANGELOG, and throw a validation failure, blocking the migration. Once executed, changesets should be considered immutable.
Q: Can we use plain SQL with Liquibase instead of XML or YAML?R: Yes, formatted SQL changelogs are fully supported. We just need to add specific comments like --changeset author:id to demarcate the changesets.
Q: What happens if a database migration fails halfway through?R: Liquibase stops execution. If the database supports transactional DDL (like PostgreSQL), it rolls back the current changeset. If not (like MySQL), partial changes might remain, and the DATABASECHANGELOGLOCK might stay locked, requiring manual intervention.
Modifying a previously executed changeset instead of creating a new one to apply corrections (triggers checksum errors).
Putting multiple DDL statements in a single changeset without verifying if the target database supports transactional DDL, risking partial updates.
Forgetting to provide explicit rollback instructions for destructive operations (like dropTable) or raw SQL, making rollback operations impossible.
Leaving the DATABASECHANGELOGLOCK table locked after a crashed process, requiring manual release via CLI (releaseLocks) or direct SQL update.
<!-- Example XML Changelog -->
<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-4.0.xsd">
<changeSet id="1" author="umberto">
<createTable tableName="users">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="email" type="varchar(255)">
<constraints nullable="false" unique="true"/>
</column>
</createTable>
</changeSet>
</databaseChangeLog>
SQL
```-- Example Formatted SQL Changelog
-- liquibase formatted sql
-- changeset umberto:2
ALTER TABLE users ADD COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
-- rollback ALTER TABLE users DROP COLUMN created_at;
liquibase update # Applies all pending changesets
liquibase updateSql # Outputs the SQL that would be executed (dry run)
liquibase rollbackCount 1 # Rolls back the last applied changeset
liquibase status # Lists pending changesets
liquibase releaseLocks # Releases the lock in DATABASECHANGELOGLOCK
liquibase clearCheckSums # Clears all checksums, forcing recalculation on next run
```