Liquibaseとは?データベース変更を柔軟に管理できるマイグレーションツール

プログラミング・IT

アプリケーション開発を進めていくと、データベースの構造を何度も変更することになります。

「新しいテーブルを追加したい」「カラムのデータ型を変更したい」「本番環境に反映した変更を元に戻したい」…こんなニーズに応えてくれるのがLiquibase(リキベース)です。

Liquibaseは、データベースの変更履歴を管理し、安全にマイグレーションを実行できる強力なツール。XML、YAML、JSON、SQLなど複数の形式で変更内容を記述できる柔軟性が魅力です。

この記事では、Liquibaseの基本から実践的な使い方まで、初心者の方にも分かりやすく解説していきます。

スポンサーリンク
  1. Liquibaseとは?基本を理解しよう
    1. Liquibaseの概要
    2. Liquibaseの特徴
    3. データベースマイグレーションの必要性
  2. FlywayとLiquibaseの違い
    1. 記述形式の違い
    2. ロールバック機能
    3. 学習コスト
    4. どちらを選ぶべきか
  3. Liquibaseの仕組み
    1. changelogファイル
    2. データベーストラッキングテーブル
    3. 実行フロー
  4. Liquibaseのセットアップ方法
    1. Mavenプロジェクトでの導入
    2. Gradleプロジェクトでの導入
    3. Spring Bootでの設定
    4. ディレクトリ構成
  5. XMLでchangelogを書く方法
    1. マスターchangelogファイル
    2. テーブル作成のchangeset
    3. カラム追加のchangeset
  6. YAMLでchangelogを書く方法
    1. YAMLのマスターファイル
    2. テーブル作成(YAML版)
    3. カラム追加(YAML版)
  7. SQLでchangelogを書く方法
    1. SQLファイルの作成
    2. SQLをマスターファイルから読み込む
  8. Liquibaseの実行方法
    1. Spring Bootでの自動実行
    2. コマンドラインでの実行
    3. プログラムから実行
  9. ロールバック機能の使い方
    1. ロールバックの基本
    2. 日付指定でのロールバック
    3. タグを使ったロールバック
    4. ロールバック用SQLの自動生成
  10. 便利な機能とコマンド
    1. status:状態確認
    2. validate:検証
    3. diff:差分確認
    4. generateChangeLog:既存DBからchangelog生成
    5. preconditions:前提条件の設定
  11. ベストプラクティス
    1. changesetは小さく保つ
    2. IDの命名規則を統一
    3. コンテキストの活用
    4. ロールバックテストを行う
    5. バージョン管理
  12. トラブルシューティング
    1. changelogが見つからない
    2. checksum検証エラー
    3. ロックエラー
    4. 循環参照エラー
  13. まとめ

Liquibaseとは?基本を理解しよう

まずは、Liquibaseがどんなツールなのか見ていきましょう。

Liquibaseの概要

Liquibaseは、データベーススキーマの変更を管理するためのオープンソースツールです。

スキーマというのは、データベースの構造(テーブルやカラムの定義)のことですね。

Liquibaseを使うと、データベースの変更を履歴として記録しながら、段階的に構造を更新できます。さらに、変更を元に戻す「ロールバック」も簡単に行えるんです。

Liquibaseの特徴

Liquibaseには、こんな特徴があります。

特徴説明
多様な記述形式XML、YAML、JSON、SQLで記述可能
ロールバック対応変更を簡単に元に戻せる
環境別設定開発・テスト・本番で異なる設定が可能
データベース独立MySQL、PostgreSQL、Oracleなど主要DBに対応
自動ドキュメント生成変更履歴を自動でドキュメント化

データベースマイグレーションの必要性

Liquibaseのようなマイグレーションツールがなぜ必要なのでしょうか。

手動での変更は危険
本番環境で手作業でSQL文を実行すると、ミスが起きやすくなります。

環境間の一貫性
開発、テスト、本番環境で同じ状態を保つのは、手動では困難です。

チーム開発での協調
複数人で開発していると、誰がどんな変更をしたか分からなくなります。

Liquibaseは、これらの課題を解決してくれる心強い味方なんです。

FlywayとLiquibaseの違い

データベースマイグレーションツールには、FlywayとLiquibaseという2大ツールがあります。

違いを理解して、自分のプロジェクトに合った方を選びましょう。

記述形式の違い

Flyway
基本的にSQLファイルのみで記述します。シンプルで分かりやすいのが特徴です。

Liquibase
XML、YAML、JSON、SQLの4つの形式から選べます。柔軟性が高いですね。

ロールバック機能

Flyway
無料版ではロールバック機能がありません。変更を戻すには、手動で新しいマイグレーションを作成する必要があります。

Liquibase
無料版でもロールバック機能が使えます。コマンド一つで変更を元に戻せるんです。

学習コスト

Flyway
SQLさえ書ければ使えるので、学習コストが低めです。

Liquibase
XMLやYAMLの記法を覚える必要があり、少し学習コストが高くなります。

どちらを選ぶべきか

Flywayがおすすめの場合

  • シンプルに使いたい
  • SQLに慣れている
  • 小規模なプロジェクト

Liquibaseがおすすめの場合

  • ロールバック機能が必要
  • 複雑な変更管理が必要
  • データベースに依存しないコードを書きたい

Liquibaseの仕組み

Liquibaseがどのように動作するのか、基本的な仕組みを理解しましょう。

changelogファイル

Liquibaseでは、データベースの変更内容をchangelog(チェンジログ)ファイルに記述します。

changelogファイルには、複数のchangeset(チェンジセット)が含まれます。

changesetは、1つの変更単位のこと。「テーブルを1つ作成する」「カラムを1つ追加する」といった変更が、それぞれ1つのchangesetになります。

データベーストラッキングテーブル

Liquibaseは、データベース内に自動的に2つの管理テーブルを作成します。

DATABASECHANGELOG
実行済みのchangesetを記録するテーブルです。

DATABASECHANGELOGLOCK
マイグレーション実行中に、他の実行を防ぐためのロックテーブルです。

これらのテーブルのおかげで、どの変更が実行済みか、Liquibaseが自動的に把握できるんです。

実行フロー

Liquibaseは、以下の流れでマイグレーションを実行します。

  1. changelogファイルを読み込む
  2. DATABASECHANGELOGテーブルで実行済みのchangesetを確認
  3. 未実行のchangesetを順番に実行
  4. 実行結果をDATABASECHANGELOGに記録

すでに実行されたchangesetは、再実行されません。

Liquibaseのセットアップ方法

それでは、実際にJavaプロジェクトでLiquibaseを使う方法を見ていきましょう。

Mavenプロジェクトでの導入

Mavenを使っている場合、pom.xmlに依存関係を追加します。

<dependency>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-core</artifactId>
    <version>4.24.0</version>
</dependency>

Gradleプロジェクトでの導入

Gradleを使っている場合は、build.gradleに以下を追加します。

dependencies {
    implementation 'org.liquibase:liquibase-core:4.24.0'
}

Spring Bootでの設定

Spring Bootを使っている場合は、application.propertiesで設定を行います。

application.propertiesの例

spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=password
spring.liquibase.enabled=true
spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.xml

設定項目の説明

  • spring.liquibase.enabled:Liquibaseを有効化
  • spring.liquibase.change-log:メインのchangelogファイルのパス

ディレクトリ構成

changelogファイルは、以下のような場所に配置します。

src/main/resources/
  └── db/
      └── changelog/
          ├── db.changelog-master.xml
          ├── changes/
          │   ├── v1.0.0-create-users-table.xml
          │   └── v1.0.1-add-email-column.xml
          └── data/
              └── initial-data.sql

マスターファイル(db.changelog-master.xml)で、個別のchangelogファイルを読み込む構成にするのが一般的です。

XMLでchangelogを書く方法

Liquibaseで最もよく使われるXML形式のchangelogを見ていきましょう。

マスター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-4.24.xsd">

    <include file="db/changelog/changes/v1.0.0-create-users-table.xml"/>
    <include file="db/changelog/changes/v1.0.1-add-email-column.xml"/>

</databaseChangeLog>

<include>タグで、個別のchangelogファイルを読み込んでいます。

テーブル作成のchangeset

ユーザーテーブルを作成するchangesetの例です。

v1.0.0-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-4.24.xsd">

    <changeSet id="1" author="yamada">
        <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"/>
            </column>
            <column name="created_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP"/>
        </createTable>
    </changeSet>

</databaseChangeLog>

要素の説明

  • changeSet id:changesetを識別する一意のID
  • author:作成者の名前
  • createTable:テーブル作成を指示
  • column:カラムの定義
  • constraints:制約の設定

カラム追加のchangeset

既存のテーブルにカラムを追加する例です。

v1.0.1-add-email-column.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-4.24.xsd">

    <changeSet id="2" author="yamada">
        <addColumn tableName="users">
            <column name="email" type="VARCHAR(100)">
                <constraints nullable="false"/>
            </column>
        </addColumn>
    </changeSet>

</databaseChangeLog>

<addColumn>タグで、既存のテーブルに新しいカラムを追加しています。

YAMLでchangelogを書く方法

XMLより簡潔に書けるYAML形式も人気があります。

YAMLのマスターファイル

db.changelog-master.yaml

databaseChangeLog:
  - include:
      file: db/changelog/changes/v1.0.0-create-users-table.yaml
  - include:
      file: db/changelog/changes/v1.0.1-add-email-column.yaml

XMLと比べて、とてもシンプルですね。

テーブル作成(YAML版)

v1.0.0-create-users-table.yaml

databaseChangeLog:
  - changeSet:
      id: 1
      author: yamada
      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
              - column:
                  name: created_at
                  type: TIMESTAMP
                  defaultValueComputed: CURRENT_TIMESTAMP

インデント(字下げ)で階層構造を表現します。

カラム追加(YAML版)

v1.0.1-add-email-column.yaml

databaseChangeLog:
  - changeSet:
      id: 2
      author: yamada
      changes:
        - addColumn:
            tableName: users
            columns:
              - column:
                  name: email
                  type: VARCHAR(100)
                  constraints:
                    nullable: false

YAMLは読みやすく、書きやすいのが特徴です。

SQLでchangelogを書く方法

生のSQLを直接使うこともできます。

SQLファイルの作成

v1.0.0-create-users-table.sql

--liquibase formatted sql

--changeset yamada:1
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

--rollback DROP TABLE users;

ポイント

  • ファイルの先頭に--liquibase formatted sqlを記述
  • --changeset author:idでchangesetを定義
  • --rollbackでロールバック時の処理を記述

SQLをマスターファイルから読み込む

db.changelog-master.xml

<databaseChangeLog>
    <include file="db/changelog/changes/v1.0.0-create-users-table.sql"/>
</databaseChangeLog>

XMLやYAMLのマスターファイルから、SQLファイルを読み込めます。

Liquibaseの実行方法

changelogファイルを作成したら、実際にマイグレーションを実行してみましょう。

Spring Bootでの自動実行

Spring Bootを使っている場合、アプリケーション起動時に自動的にLiquibaseが実行されます。

mvn spring-boot:run

未実行のchangesetが自動的に適用されます。

コマンドラインでの実行

Liquibaseは、コマンドラインツールとしても使えます。

Mavenプラグインを使う場合

mvn liquibase:update

Gradleプラグインを使う場合

gradle liquibaseUpdate

プログラムから実行

Javaコード内で、プログラム的に実行することもできます。

import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.resource.ClassLoaderResourceAccessor;

import java.sql.Connection;
import java.sql.DriverManager;

public class LiquibaseMigration {
    public static void main(String[] args) throws Exception {
        Connection connection = DriverManager.getConnection(
            "jdbc:mysql://localhost:3306/mydb", 
            "root", 
            "password"
        );

        Database database = DatabaseFactory.getInstance()
            .findCorrectDatabaseImplementation(new JdbcConnection(connection));

        Liquibase liquibase = new Liquibase(
            "db/changelog/db.changelog-master.xml",
            new ClassLoaderResourceAccessor(),
            database
        );

        liquibase.update("");

        connection.close();
    }
}

ロールバック機能の使い方

Liquibaseの大きな魅力の一つが、ロールバック機能です。

ロールバックの基本

changesetを元に戻すには、以下のコマンドを実行します。

mvn liquibase:rollback -Dliquibase.rollbackCount=1

これで、最後に実行した1つのchangesetがロールバックされます。

日付指定でのロールバック

特定の日時まで戻すこともできます。

mvn liquibase:rollback -Dliquibase.rollbackDate=2024-01-01

2024年1月1日の状態まで戻ります。

タグを使ったロールバック

あらかじめタグを設定しておくと、そのタグまで戻せます。

タグの設定

<changeSet id="3" author="yamada">
    <tagDatabase tag="version-1.0"/>
</changeSet>

タグまでロールバック

mvn liquibase:rollback -Dliquibase.rollbackTag=version-1.0

ロールバック用SQLの自動生成

Liquibaseは、XML/YAML形式で書いたchangesetから、ロールバック用のSQLを自動生成してくれます。

例えば、createTableに対してはDROP TABLEが自動的に用意されるんです。

カスタムのロールバック処理が必要な場合は、明示的に指定できます。

<changeSet id="4" author="yamada">
    <sql>
        UPDATE users SET status = 'active' WHERE status IS NULL;
    </sql>
    <rollback>
        UPDATE users SET status = NULL WHERE status = 'active';
    </rollback>
</changeSet>

便利な機能とコマンド

Liquibaseには、他にも便利な機能がたくさんあります。

status:状態確認

現在のマイグレーション状態を確認できます。

mvn liquibase:status

どのchangesetが実行済みで、どれが未実行かが表示されます。

validate:検証

changelogの内容が正しいか検証します。

mvn liquibase:validate

diff:差分確認

2つのデータベースの差分を確認できます。

mvn liquibase:diff

本番環境と開発環境の違いを調べるときに便利です。

generateChangeLog:既存DBからchangelog生成

すでに存在するデータベースから、changelogを自動生成できます。

mvn liquibase:generateChangeLog

既存プロジェクトにLiquibaseを導入するときに役立ちます。

preconditions:前提条件の設定

changesetを実行する前に、条件をチェックできます。

<changeSet id="5" author="yamada">
    <preConditions onFail="MARK_RAN">
        <not>
            <tableExists tableName="users"/>
        </not>
    </preConditions>
    <createTable tableName="users">
        <!-- カラム定義 -->
    </createTable>
</changeSet>

この例では、usersテーブルが存在しない場合のみ実行されます。

ベストプラクティス

Liquibaseを効果的に使うためのポイントをまとめました。

changesetは小さく保つ

1つのchangesetには、1つの論理的な変更だけを含めましょう。

良い例

<!-- 1つのテーブル作成のみ -->
<changeSet id="1" author="yamada">
    <createTable tableName="users"/>
</changeSet>

悪い例

<!-- 複数の無関係な変更を詰め込んでいる -->
<changeSet id="1" author="yamada">
    <createTable tableName="users"/>
    <createTable tableName="products"/>
    <addColumn tableName="orders"/>
</changeSet>

IDの命名規則を統一

changesetのIDは、チーム内で統一したルールで付けましょう。

  • 連番:1, 2, 3…
  • 日時:20240101-1, 20240101-2…
  • バージョン:v1.0.0-1, v1.0.0-2…

コンテキストの活用

環境ごとに実行するchangesetを分けられます。

<changeSet id="6" author="yamada" context="dev">
    <!-- 開発環境でのみ実行 -->
    <insert tableName="users">
        <column name="username" value="test_user"/>
    </insert>
</changeSet>

<changeSet id="7" author="yamada" context="prod">
    <!-- 本番環境でのみ実行 -->
</changeSet>

実行時にコンテキストを指定します。

mvn liquibase:update -Dliquibase.contexts=dev

ロールバックテストを行う

本番環境で実行する前に、ロールバックが正しく動作するか確認しましょう。

# マイグレーション実行
mvn liquibase:update

# ロールバックテスト
mvn liquibase:rollback -Dliquibase.rollbackCount=1

# 再度マイグレーション
mvn liquibase:update

バージョン管理

changelogファイルは、Gitなどのバージョン管理システムに必ずコミットしましょう。

データベースの変更履歴を、コードと一緒に管理できます。

トラブルシューティング

Liquibaseを使う際によく遭遇する問題と、その解決方法です。

changelogが見つからない

症状
FileNotFoundExceptionなどのエラーが出る。

原因と対処法

  1. パスが間違っている
    application.propertiesのパスを確認
  2. ファイルが配置されていない
    src/main/resources/db/changelog/に正しく配置されているか確認

checksum検証エラー

症状
「Checksum validation failed」というエラーが表示される。

原因
実行済みのchangesetを変更した。

対処法

changesetは変更せず、新しいchangesetを追加するのが原則です。

どうしても修正が必要な場合は、以下のコマンドでチェックサムをクリアします。

mvn liquibase:clearCheckSums

ロックエラー

症状
「Waiting for changelog lock」というメッセージが出て処理が進まない。

原因
前回のマイグレーション実行が異常終了し、ロックが残っている。

対処法

mvn liquibase:releaseLocks

このコマンドで、ロックを強制解除できます。

循環参照エラー

症状
「Circular reference detected」というエラーが出る。

原因
マスターchangelogファイルの<include>で、ファイルの読み込み順序に問題がある。

対処法
依存関係を整理して、正しい順序で読み込むようにします。

まとめ

Liquibaseについて解説してきました。

重要なポイント

  • LiquibaseはXML、YAML、JSON、SQLで記述できる柔軟なマイグレーションツール
  • 無料版でもロールバック機能が使える
  • changesetで変更を管理し、実行履歴はDATABASECHANGELOGに記録される
  • Spring Bootでは自動的にマイグレーションが実行される
  • マスターchangelogファイルで複数のファイルを統合管理できる
  • 前提条件やコンテキストで、柔軟な実行制御が可能
  • changesetは小さく保ち、ロールバックテストを忘れずに行う

Liquibaseは、最初は複雑に感じるかもしれません。しかし、ロールバック機能や豊富な機能を使いこなせば、データベース管理が格段に楽になります。

まずは簡単なテーブル作成から始めて、徐々に高度な機能にも挑戦してみてください。安全で確実なデータベース管理を実現しましょう!

コメント

タイトルとURLをコピーしました