Kontinuierliche Datenbankmigration mit Liquibase und Flyway

Seite 2: Tools

Inhaltsverzeichnis

Im Java-Umfeld gibt es mittlerweile zahlreiche Open-Source-Bibliotheken und -Frameworks, die bei der Versionierung der Datenbank unterstützen können. An erster Stelle sind die Bibliotheken Liquibase und Flyway zu nennen. Beide stehen unter der Apache-2-Lizenz und unterstützen von Haus aus relationale Datenbanksysteme wie Oracle, DB2, SQL Server, MySQL, PostgreSQL, H2, Hsql und Apache Derby.

Die Erweiterung auf neue Systeme ist problemlos möglich, da beide Bibliotheken den JDBC-Standard (Java Database Connectivity) nutzen. Jede relationale Datenbank, die über einen JDBC-Treiber verfügt, lässt sich somit von Liquibase und Flyway ansprechen. Auch wenn der Trend mehr hin zu NoSQL-Datenbanken geht, werden derzeit ausschließlich relationale Datenbanksysteme unterstützt, da diese nach wie vor in Enterprise-Umgebungen als zentrale Datenhaltung eingesetzt und durch NoSQL-Produkte höchstens unterstützt und erweitert werden.

Die Dokumentation beider Bibliotheken kann als gut und umfangreich bezeichnet werden, jede Information, die man beim Arbeiten mit der jeweiligen Bibliothek benötigt, lässt sich auf den jeweiligen Webseiten beziehungsweise in der herunterladbaren Dokumentation finden. Die Bibliotheken sind als Plug-in-Systeme ausgelegt. Für den Build-Zeitpunkt lassen sich Liquibase und Flyway zum Beispiel in Ant oder Maven einbinden und ausführen. Ebenso ist aber eine API-Integration mit Grails oder Spring direkt in die Anwendung möglich. Beispielsweise lässt sich beim Hochfahren der Anwendung direkt überprüfen, ob die Datenbank die passende Version besitzt, und bei Bedarf diese gleich aktualisieren, wenn das gewünscht ist. Andernfalls kann man auch einfach eine Fehlermeldung beim Anwendungsstart ausgeben und so die Anwendung vor unerwünschtem Fehlverhalten während der Laufzeit schützen.

Eine Option für Nicht-Java-Projekte stellt die Ausführung über die Konsole dar. So lassen sich Flyway und Liquibase beide als eigenständig laufende Programme über jede Konsole nutzen und über entsprechende Kommandos in beliebige Build-Skripte integrieren. Zum Ausführungszeitpunkt ist lediglich eine Java-Runtime-Umgebung notwendig.

Beide zielen darauf ab, dass die Informationen über das Datenbank-Schema und die Daten in einzelnen Dateien abgelegt werden. Flyway nutzt hier einfache Dateien, in denen natives SQL im jeweiligen Dialekt der verwendeten Datenbank abgelegt ist und einfach ausgeführt wird. Liquibase führt hier eine weitere Abstraktionsschicht ein und legt die Schema-Informationen in einer eigenen, datenbankneutralen XML-Schema-Beschreibung ab, die in den jeweiligen Datenbankdialekt übersetzt wird. Bei Flyway stellt jede Datei ein Changeset (eine Menge an Änderungen an der Datenbank) dar, das nach dem Einchecken in das VCS nicht mehr verändert wird. Neue Änderungen resultieren in neuen Dateien. Liquibase unterstützt mit dem XML-Schema mehrere Changesets innerhalb einer Datei. Die folgenden Beispiele zeigen, wie sich mit Liquibase und Flyway eine Tabelle mit drei Spalten in einer MySQL-Datenbank anlegen lässt.

Hier die Konfiguration einer neuen Tabelle in Liquibase (XML wird zur Laufzeit in SQL ĂĽbersetzt):

<?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-2.0.xsd">

<changeSet id="1" author="nko">
<createTable tableName="my_table">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="name" type="varchar(255)">
<constraints nullable="false"/>
</column>
<column name="active" type="boolean" defaultValueBoolean="true"/>
</createTable>
</changeSet>

</databaseChangeLog>

Hier die Konfiguration einer neuen Tabelle in Flyway (natives SQL wird direkt ausgefĂĽhrt).

CREATE TABLE 'my_table' (
'id' INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
'name' VARCHAR(255) NOT NULL,
'active' TINYINT(1) NULL DEFAULT 1,
PRIMARY KEY ('id')
)

Die vermeintliche Unabhängigkeit zur Datenbank von Liquibase und der damit mögliche Vorteil in der Flexibilität hinsichtlich der theoretischen Austauschbarkeit der verwendeten Datenbank ist insofern zu relativieren, dass die Unabhängigkeit dann verloren geht, wenn die Beschreibung des Schemas für die Zieldatenbank spezifische Parameter verwendet werden. Eine MySQL-Datenbank kann zum Beispiel mit dem Oracle-spezifischen Datentyp VARCHAR2 für eine Textspalte nicht viel anfangen, und Oracle kommt mit dem Attribut autoincrement auch nicht sonderlich weit. Falls sich die strukturellen Änderungen nicht über das XML Schema abbilden lassen, muss beziehungsweise kann in Liquibase zu nativem SQL gegriffen und innerhalb eines spezifischen Tags das SQL-Statement angegeben werden. Die Datenbankunabhängigkeit ist allerdings dann auch in diesem Fall hinfällig. Hierbei unterstützt Liquibase allerdings jeweils nur ein einzelnes SQL-Statement je Tag, komplexe Skripte sind somit aufwendig oder nur schwer bis nicht möglich. Mit Flyway ist es durch die Plain-Text-/SQL-Dateien möglich, komplette Skripte in datenbankproprietären Sprachen (wie PL/SQL für Oracle, PL für DB2, T-SQL für MS SQL, Stored Procedures für MySQL oder PostgreSQL) auszuführen.

Zusätzlich sollte noch erwähnt sein, dass die XML-Beschreibung erst zur Laufzeit in den Datenbankdialekt übersetzt wird und somit, wenn auch nur geringfügig, langsamer in der Ausführung ist als eine Datei mit nativem SQL. Über die Lesbarkeit von Datenbankstrukturen in einem XML-Schema gegenüber einem SQL-Ausdruck darf sich jeder Leser selbst ein Urteil bilden.

Liquibase nutzt zur Steuerung, welche Datenbankänderungen in welcher Reihenfolge ausgeführt werden sollen, ein Master-XML, das alle auszuführenden Dateien deklarativ aufführt. Wird ein neues Datenbank-Skript (oder besser gesagt XML) bereitgestellt, ist es auch noch im Master-XML anzugeben, sonst wird es nicht ausgeführt. An der Stelle setzt Flyway auf den Ansatz "Convention over Configuration" und erlegt den auszuführenden Dateien eine Namenskonvention auf. Im Dateinamen muss der Versionsbezeichner in einem vorgegebenen Muster enthalten sein, dann steuert Flyway die Ausführung automatisch. Man darf bloß nicht vergessen, die Datei im richtigen Verzeichnis zu speichern.