この記事から得られる知識
- Liquibaseの基本的な概念と、なぜ現代の開発で不可欠なのかを理解できる。
- MavenやGradleを使ったJavaプロジェクトへのLiquibase導入手順を学べる。
- XML、YAML、SQL形式でのChangelog(変更セット)の具体的な書き方を習得できる。
update
やrollback
といった主要なLiquibaseコマンドの実行方法がわかる。- Spring BootアプリケーションとLiquibaseを連携させ、開発プロセスを自動化する方法を把握できる。
- Contexts、Labels、Preconditionsといった、より高度で実践的なテクニックを学び、環境に応じたスキーマ管理が可能になる。
第一章: Liquibaseとは何か?
Liquibaseは、データベーススキーマの変更を追跡、管理、適用するためのオープンソースのライブラリです。 アプリケーションのソースコードをバージョン管理システム(Gitなど)で管理するように、データベースの構造(テーブル、カラム、インデックスなど)の変更履歴をバージョン管理します。
開発チームが複数人になったり、開発環境、ステージング環境、本番環境など複数の環境でアプリケーションを管理したりする際に、データベースの状態を全員で、そして全環境で一貫性を持って維持することは非常に困難です。手動でのSQL実行は、適用漏れや順序の間違い、ヒューマンエラーの温床となりがちです。
Liquibaseは、これらの課題を解決するために開発されました。変更内容をChangelogと呼ばれるファイルに記述することで、誰がいつ、どのような変更を行ったのかを明確にし、任意のバージョンへの更新や、問題発生時のロールバックを安全かつ確実に行うことができます。
第二章: Liquibaseのセットアップ
JavaプロジェクトでLiquibaseを使い始めるのは非常に簡単です。ここでは、代表的なビルドツールであるMavenとGradleを使ったセットアップ方法を解説します。
Mavenでのセットアップ
Mavenプロジェクトの場合、pom.xml
にliquibase-core
の依存関係を追加します。Spring Bootを利用している場合は、spring-boot-starter-parent
が適切なバージョンを管理してくれるため、バージョンの指定は不要なことが多いです。
<dependencies> <dependency> <groupId>org.liquibase</groupId> <artifactId>liquibase-core</artifactId> </dependency>
</dependencies>
Gradleでのセットアップ
Gradleプロジェクトでは、build.gradle
のdependencies
ブロックに以下を追記します。
dependencies { implementation 'org.liquibase:liquibase-core'
}
データベース接続情報の設定
Liquibaseは、どのデータベースに対して変更を適用するかを知る必要があります。Spring Boot環境では、application.properties
やapplication.yml
に設定されたデータソース情報を自動的に利用するため、特別な設定は不要です。
スタンドアロンで実行する場合などは、liquibase.properties
というファイルを作成し、接続情報を記述します。
# liquibase.properties
changeLogFile: src/main/resources/db/changelog/db.changelog-master.xml
url: jdbc:mysql://localhost:3306/mydatabase
username: myuser
password: mypassword
driver: com.mysql.cj.jdbc.Driver
第三章: 基本的な使い方 – Changelogの作成
Liquibaseの心臓部となるのがChangelogファイルです。このファイルに変更したい内容をChangeSetという単位で追記していきます。ChangeSetはトランザクションの単位であり、一意の`id`と`author`(作成者)を持つ必要があります。
ChangelogはXML、YAML、JSON、SQLのいずれかの形式で記述できますが、ここでは最も一般的で機能が豊富なXML形式と、可読性の高いYAML、そして既存の資産を活用しやすいSQL形式を解説します。
XML形式でのChangelog
XML形式は、Liquibaseの全ての機能を活用できる最も表現力の高いフォーマットです。スキーマ定義(XSD)が提供されているため、IDEによる入力補完やバリデーションの恩恵を受けることができます。
一般的に、すべてのChangelogファイルをインクルードする「マスターChangelog」を用意し、機能やバージョンごとにファイルを分割して管理することが推奨されます。
例: db/changelog/db.changelog-master.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-latest.xsd"> <!-- 他のChangelogファイルをインクルード --> <include file="db/changelog/001-create-users-table.xml"/> <include file="db/changelog/002-add-email-to-users.xml"/>
</databaseChangeLog>
例: db/changelog/001-create-users-table.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-latest.xsd"> <changeSet id="1" author="your-name"> <createTable tableName="users"> <column name="id" type="bigint" autoIncrement="true"> <constraints primaryKey="true" nullable="false"/> </column> <column name="username" type="varchar(50)"> <constraints nullable="false" unique="true"/> </column> <column name="created_at" type="timestamp" defaultValueComputed="CURRENT_TIMESTAMP"/> </createTable> <!-- ロールバック処理を明示的に定義 --> <rollback> <dropTable tableName="users"/> </rollback> </changeSet>
</databaseChangeLog>
<createTable>
のような多くのChange Typeでは、Liquibaseが自動的にロールバック処理(この場合は<dropTable>
)を生成してくれます。しかし、自動生成できない複雑な変更の場合は、<rollback>
タグを使って手動でロールバック処理を定義することが重要です。
YAML形式でのChangelog
YAML形式は、XMLよりも簡潔で可読性が高いのが特徴です。インデントで構造を表現するため、括弧が少なくすっきりとした見た目になります。
例: db/changelog/001-create-users-table.yaml
databaseChangeLog: - changeSet: id: 1 author: your-name changes: - createTable: tableName: users columns: - column: name: id type: bigint autoIncrement: true constraints: primaryKey: true nullable: false - column: name: username type: varchar(50) constraints: nullable: false unique: true - column: name: created_at type: timestamp defaultValueComputed: CURRENT_TIMESTAMP rollback: - dropTable: tableName: users
SQL形式でのChangelog
SQLに慣れ親しんだ開発者や、既存のSQLスクリプトを再利用したい場合には、SQL形式が便利です。ただし、特定のデータベースに依存したSQLを記述してしまうと、Liquibaseの「データベース非依存」という利点が失われる可能性がある点には注意が必要です。
SQL形式では、特定のコメント形式を使ってメタデータをLiquibaseに伝えます。
例: db/changelog/001-create-users-table.sql
-- liquibase formatted sql
-- changeset your-name:1
CREATE TABLE users ( id BIGINT AUTO_INCREMENT NOT NULL, username VARCHAR(50) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT pk_users PRIMARY KEY (id), CONSTRAINT uc_users_username UNIQUE (username)
);
-- rollback DROP TABLE users;
-- changeset your-name:2
-- comment: Add email column to users table
ALTER TABLE users ADD email VARCHAR(100);
-- rollback ALTER TABLE users DROP COLUMN email;
各ChangeSetは-- changeset author:id
というコメントで始まり、SQL文の終わりはセミコロンで示します。ロールバック処理は-- rollback
コメントに続けて記述します。
第四章: Liquibaseコマンドの実行
Changelogファイルを作成したら、次はその変更をデータベースに適用します。Liquibaseには様々なコマンドが用意されており、CLI(コマンドラインインターフェース)やMaven/Gradleプラグインから実行できます。
主要なLiquibaseコマンド
コマンド | 説明 |
---|---|
update | Changelogファイルを読み込み、まだ適用されていない全てのChangeSetをデータベースに適用します。 |
updateSQL | update コマンドで実行されるであろうSQL文を、実際には実行せずに出力します。本番環境への適用前に、実行内容を確認するのに非常に役立ちます。 |
rollback | 指定したタグ、日付、または件数までデータベースの状態を巻き戻します。例えば、rollback a_tag と実行すると、’a_tag’というタグが付けられた時点まで変更がロールバックされます。 |
rollbackSQL | rollback コマンドで実行されるSQL文を出力します。 |
tag | データベースの現在の状態に一意の「タグ」を付けます。リリースバージョン(例:v1.0.0)などをタグ付けしておくことで、将来そのバージョンへのロールバックが容易になります。 |
diff | 2つのデータベースの状態を比較し、その差分をChangelog形式で出力します。開発中のデータベースと、クリーンなデータベースを比較して変更点を洗い出す、といった使い方ができます。 |
generateChangeLog | 既存のデータベーススキーマを解析し、その状態を再現するためのChangelogファイルを生成します。Liquibaseを途中から導入するプロジェクトで非常に役立ちます。 |
status | まだデータベースに適用されていないChangeSetの一覧を表示します。 |
validate | Changelogファイルの構文が正しいか、循環参照がないかなどを検証します。 |
コマンドの実行例 (Maven)
Mavenプラグインを使えば、コマンドラインから簡単にLiquibaseコマンドを実行できます。
# データベースの更新
mvn liquibase:update
# 実行されるSQLの確認
mvn liquibase:updateSQL
# 'v1.0' タグまでロールバック
mvn liquibase:rollback -Dliquibase.rollbackTag=v1.0
# 現在の状態にタグ付け
mvn liquibase:tag -Dliquibase.tag=v1.1
# 差分の確認
mvn liquibase:diff
DATABASECHANGELOGテーブル
update
コマンドを初めて実行すると、データベース内にDATABASECHANGELOG
とDATABASECHANGELOGLOCK
という2つのテーブルが自動的に作成されます。DATABASECHANGELOG
テーブルには、適用されたChangeSetのID、作成者、ファイル名、実行日時、MD5ハッシュなどが記録されます。Liquibaseは、このテーブルとChangelogファイルの内容を比較することで、次にどのChangeSetを実行すべきかを判断します。
第五章: Spring Bootとの連携
Spring BootとLiquibaseの連携は非常にスムーズです。依存関係にliquibase-core
を追加するだけで、Spring Bootはアプリケーションの起動時にLiquibaseを自動的に実行しようとします。
基本的な設定
application.properties
またはapplication.yml
で、Liquibaseの動作を細かく制御できます。
例: application.properties
# Liquibaseを有効にする (デフォルトはtrue)
spring.liquibase.enabled=true
# マスターChangelogファイルのパスを指定
# デフォルトは classpath:/db/changelog/db.changelog-master.xml
spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.yaml
# アプリケーション起動時にDBをクリーンインストールしてからLiquibaseを実行するかどうか
# spring.liquibase.drop-first=false
# Hibernateのスキーマ自動生成機能(ddl-auto)は無効にするのが一般的
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.ddl-auto
)はnone
またはvalidate
に設定することが強く推奨されます。create
やcreate-drop
、update
に設定していると、Liquibaseによる管理と競合し、予期せぬ動作を引き起こす可能性があります。 プロファイルごとの実行制御
Springのプロファイル機能を使えば、環境ごとにLiquibaseの有効/無効を切り替えることができます。例えば、テスト実行時にはLiquibaseを無効にしたい場合などです。
例: application-test.properties
# testプロファイルではLiquibaseを無効にする
spring.liquibase.enabled=false
このように設定しておけば、テスト実行時に@ActiveProfiles("test")
を指定することで、Liquibaseの自動実行をスキップできます。
第六章: 実践的なテクニック
基本的な使い方をマスターしたら、次はより高度な機能を活用して、複雑な要件に対応してみましょう。
Contexts: 環境に応じた実行制御
Contextsは、特定のChangeSetをどの環境で実行するかを制御するための仕組みです。例えば、「テスト環境でのみテストデータを投入したい」「本番環境では実行したくない変更がある」といったシナリオで非常に有効です。
ChangeSetにcontext
属性を追加し、実行時に適用したいContextを指定します。
Changelogでの定義 (XML):
<changeSet id="insert-test-data" author="your-name" context="test"> <insert tableName="users"> <column name="username" value="testuser"/> </insert>
</changeSet>
<changeSet id="add-performance-index" author="your-name" context="!dev"> <!-- dev環境以外で実行される --> <createIndex indexName="idx_username" tableName="users"> <column name="username"/> </createIndex>
</changeSet>
Spring Boot環境では、application.properties
で実行Contextを指定します。カンマ区切りで複数指定も可能です。
spring.liquibase.contexts=test, another_context
何もContextを指定しない場合、Contextが指定されていない全てのChangeSetが実行されます。
Labels: より柔軟なグルーピング
LabelsはContextsと似ていますが、より複雑な論理式(AND, OR, !)を使って実行するChangeSetを選択できる点が異なります。機能単位やリリース単位でラベルを付け、柔軟にマイグレーションを組み合わせたい場合に便利です。
Changelogでの定義 (XML):
<changeSet id="feature-A-change" author="your-name" labels="feature-A"> <!-- ... -->
</changeSet>
<changeSet id="hotfix-change" author="your-name" labels="hotfix, release-1.2"> <!-- ... -->
</changeSet>
実行時にはlabel-filter
プロパティで実行したいラベルの式を指定します。
# 'feature-A' または 'hotfix' ラベルを持つChangeSetを実行
spring.liquibase.label-filter=feature-A or hotfix
Preconditions: 実行前の前提条件チェック
Preconditionsを使うと、ChangeSetを適用する前にデータベースの状態をチェックし、条件を満たさない場合は実行をスキップしたり、エラーで停止させたりすることができます。これにより、マイグレーションの安全性が大幅に向上します。
例えば、「特定のテーブルが存在する場合にのみカラムを追加する」「特定のDB(例: PostgreSQL)でのみ実行する」といった制御が可能です。
Changelogでの定義 (XML):
<changeSet id="add-column-if-table-exists" author="your-name"> <preConditions onFail="MARK_RAN"> <tableExists tableName="products"/> </preConditions> <addColumn tableName="products"> <column name="description" type="text"/> </addColumn>
</changeSet>
<changeSet id="postgresql-specific-change" author="your-name"> <preConditions onFail="SKIP"> <dbms type="postgresql"/> </preConditions> <-- PostgreSQL固有の変更処理 --> <sql> CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; </sql>
</changeSet>
onFail
属性で、前提条件が満たされなかった場合の動作を指定します。
HALT:
実行を停止し、エラーを発生させます(デフォルト)。SKIP:
このChangeSetの実行をスキップして、次のChangeSetに進みます。MARK_RAN:
実行はしませんが、実行済みとしてマークします。これにより、次回以降このChangeSetは実行対象外となります。WARN:
警告メッセージを出力して、ChangeSetの実行は続行します。
まとめ
Liquibaseは、Java開発におけるデータベーススキーマ管理の複雑さとリスクを劇的に軽減してくれる強力なツールです。Changelogによる変更履歴のバージョン管理は、チーム開発の効率を向上させ、デプロイプロセスの信頼性を高めます。
最初は覚えることが多いように感じるかもしれませんが、基本的なupdate
コマンドとChangelogの作成から始め、Spring Bootとの連携を活用すれば、その恩恵をすぐに実感できるはずです。さらに、ContextsやPreconditionsといった高度な機能を使いこなすことで、あらゆる環境、あらゆる要件に柔軟に対応できる、堅牢なデータベース管理体制を構築できます。
手動でのSQL管理から脱却し、安全で再現性の高いデータベースマイグレーションを実現するために、ぜひ今日のプロジェクトからLiquibaseの導入を検討してみてください。