Liquibase und Maven

Einleitung


Liquibase ist ein Datenbank-Versionskontrollsystem, hauptsächlich für die Struktur und in geringerem Umfang für den Inhalt der Datenbank. Gleichzeitig ist die Beschreibung der Datenbank zum einen recht abstrakt und erlaubt die Verwendung verschiedener DBMS auf der unteren Ebene. Zum anderen können Sie immer zum SQL-Dialekt eines bestimmten DBMS wechseln, was ziemlich flexibel ist. Liquibase ist ein etabliertes Open-Source-Projekt, das außerhalb seiner nativen Java-Umgebung aktiv eingesetzt wird und für das Funktionieren kein tiefgreifendes Java-Wissen voraussetzt. Das XML-Format wurde in der Vergangenheit als Beschreibung der Basisstruktur und der Basisänderungen verwendet. Jetzt werden jedoch YAML und JSON parallel unterstützt.


In diesem Artikel werden wir die Erfahrungen früherer Generationen zusammenfassen und uns auf die Arbeit mit Liquibase mit Maven konzentrieren. Als Testbetriebssystem verwenden wir Ubuntu.


Andere Liquibase-Artikel



Umgebung einrichten


Sie können Liquibase auf verschiedene Arten ausführen, aber Maven oder Gradle ist am bequemsten zu verwenden.


sudo apt install maven
mvn -version

Die pom.xml ist hier das Makefile - es enthält bereits alle notwendigen Abhängigkeiten, Einstellungen und Profile.


pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.test.db</groupId>
    <artifactId>db</artifactId>
    <version>1.0.0</version>
    <name>db</name>
    <description>Test Database</description>
    <packaging>pom</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <slf4j.version>1.7.24</slf4j.version>
        <logback.version>1.2.3</logback.version>
        <liquibase.version>3.6.2</liquibase.version>
        <postgresql.version>42.2.5</postgresql.version>
        <snakeyaml.version>1.23</snakeyaml.version>
    </properties>
    <dependencies>
        <!--Logging-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>
        <!--JDBC drivers-->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>${postgresql.version}</version>
        </dependency>
        <dependency>
            <groupId>org.liquibase</groupId>
            <artifactId>liquibase-core</artifactId>
            <version>${liquibase.version}</version>
        </dependency>
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>${snakeyaml.version}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.liquibase</groupId>
                <artifactId>liquibase-maven-plugin</artifactId>
                <version>${liquibase.version}</version>
                <configuration>
                    <propertyFile>${profile.propertyFile}</propertyFile>
                    <changeLogFile>${profile.changeLogFile}</changeLogFile>
                    <dataDir>${profile.dataDir}</dataDir>
                    <!-- log  -->
                    <verbose>${profile.verbose}</verbose>
                    <logging>${profile.logging}</logging>
                    <promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <profiles>
      <!-- Development settings, -Denv=dev -->
      <profile>
        <id>dev</id>
        <activation>
          <property>
            <name>env</name>
            <value>dev</value>
          </property>
        </activation>
        <properties>
          <profile.propertyFile>dev/liquibase.properties</profile.propertyFile>
          <profile.changeLogFile>dev/master.xml</profile.changeLogFile>
          <profile.dataDir>dev/data</profile.dataDir>
          <profile.verbose>true</profile.verbose>
          <profile.logging>debug</profile.logging>
        </properties>
      </profile>
      <!-- Production settings, -Denv=prod -->
      <profile>
        <id>prod</id>
        <activation>
          <property>
            <name>env</name>
            <value>prod</value>
          </property>
        </activation>
        <properties>
          <profile.propertyFile>prod/liquibase.properties</profile.propertyFile>
          <profile.changeLogFile>prod/master.xml</profile.changeLogFile>
          <profile.dataDir>prod/data</profile.dataDir>
          <profile.verbose>false</profile.verbose>
          <profile.logging>info</profile.logging>
        </properties>
      </profile>
    </profiles>
</project>

Wir starten Update


Nachdem wir pom.xml ausgeführt haben, können wir ein Update der Datenbank ausführen - den Befehl liquibase: update.


Dafür brauchen wir:


  • Datei liquibase.properties mit Datenbankverbindungseinstellungen (Login / Passwort und möglicherweise andere Parameter)
  • XML-Datei mit Datenbankänderungen
  • sh-Basisstart-Update-Skript

Datei mit Datenbankverbindungseinstellungen


liquibase.properties


username=test
password=test
referenceUsername=test
#можно задавать и другие параметры
#url=jdbc:postgresql://dev/test
#referenceUrl=jdbc:postgresql://dev/test_reference

Datei mit Datenbankänderungen


Das Grundkonzept der Liquibase sind die sogenannten Basisänderungen (Changesets). Dazu können sowohl Strukturänderungen als auch Datenänderungen gehören. Um die vorgenommenen Änderungen zu steuern, verwendet liquibase die Tabellen databasechangelog und databasechangeloglock .


<?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: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/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
    <changeSet context="legacy" author="author (generated)" id="1">
        <createTable tableName="test">
            <column autoIncrement="true" name="id" type="SERIAL">
                <constraints nullable="false"/>
            </column>
            <column name="user_name" type="VARCHAR(255)"/>
            <column name="preferences" type="TEXT"/>
        </createTable>
    </changeSet>
</databaseChangeLog>

Update-Datenbank für Skriptlauf


Hier wird liquibase ausgeführt: Update für das dev-Profil und die Datenbank aus liquibase.url, die im Standard-JDBC-Format angegeben ist. Nach dem Update werden die in changeSet angegebene Tabelle und die zwei Service-Tabellen databasechangelog und databasechangeloglock in der Datenbank angezeigt .


#!/usr/bin/env bash
mvn liquibase:update\
    -Denv=dev\
    -Dliquibase.url="jdbc:postgresql://dev/test?prepareThreshold=0&stringtype=unspecified"

SQL-Generierung ohne Datenbank-Update


Manchmal ist es erforderlich, den Inhalt der erstellten Abfragen zu prüfen, bevor Sie die Änderungen starten. Dazu werden die Befehle liquibase: updateSQL und liquibase: rollbackSQL verwendet.


#!/usr/bin/env bash
mvn liquibase:updateSQL\
    -Denv=dev\
    -Dliquibase.url="jdbc:postgresql://dev/test?prepareThreshold=0&stringtype=unspecified" > /tmp/script.sql

Lesen Sie mehr über changeSet


Änderungen können in verschiedenen Formaten vorliegen, einschließlich normaler SQL-Dateien oder in einer separaten Datei.


Jede Änderung kann einen Rollback-Abschnitt enthalten, in dem Sie mit dem Befehl liquibase: rollback Änderungen rückgängig machen können . Zum Markieren von Änderungen, zum Beispiel für ein bequemeres Rollback dort, können Sie tagDatabase verwenden .


Einfaches Format


<changeSet context="legacy" author="author (generated)" id="1">
    <createTable tableName="test">
        <column autoIncrement="true" name="id" type="SERIAL">
            <constraints primaryKey="true" primaryKeyName="test_pkey"/>
        </column>
        <column name="c1" type="VARCHAR(255)"/>
        <column name="c2" type="INTEGER"/>
        <column name="c3" type="SMALLINT"/>
        <column name="c4" type="VARCHAR(255)"/>
        <column name="c5" type="TEXT"/>
        <column name="c6" type="VARCHAR(255)"/>
    </createTable>
</changeSet>

Embedded SQL


<changeSet context="legacy" author="author" id="1-domain-some-domain">
    <sql>
        CREATE DOMAIN public.some_domain AS bigint;
        ALTER DOMAIN public.some_domain OWNER TO test;
    </sql>
    <rollback>
        DROP DOMAIN public.some_domain;
    </rollback>
</changeSet>

SQL-Datei


<changeSet context="legacy" author="author" id="1-user">
     <sqlFile dbms="postgresql" path="sql/some.sql" relativeToChangelogFile="true" />
     <rollback> delete from "some"; </rollback>
</changeSet>

Tags


<changeSet context="legacy" author="author" id="1-initial-changeset">
    <tagDatabase tag="initial"/>
</changeSet>

Kontexte starten


Kontexte können verwendet werden, um verschiedene Konfigurationen, z. B. Entwicklung / Produktion, einfacher zu verwalten. Der Kontext wird im Kontextattribut changeSet angegeben und dann von Maven mit dem Parameter -Dcontexts gestartet.


Ändern Sie mit dem Kontext


<changeSet context="legacy" author="author" id="1-initial-changeset">
    <tagDatabase tag="initial"/>
</changeSet>

Änderungen nach Kontext ausführen


#!/usr/bin/env bash
mvn liquibase:update\
    -Denv=dev\
    -Dliquibase.url="jdbc:postgresql://dev/test?prepareThreshold=0&stringtype=unspecified"\
    -Dliquibase.contexts=non-legacy

Rollback-Änderungen


Der Vorgang ist ein Reverse-Upgrade, das in den meisten Fällen automatisch unterstützt wird. Für andere ist die Aufgabe über den Rollback-Abschnitt möglich. Wird vom Befehl liquibase: rollback ausgeführt.


Rollback-Änderung


<changeSet context="legacy" author="author" id="1-domain-some-domain">
    <sql>
        CREATE DOMAIN public.some_domain AS bigint;
        ALTER DOMAIN public.some_domain OWNER TO test;
    </sql>
    <rollback>
        DROP DOMAIN public.some_domain;
    </rollback>
</changeSet>

Rollback starten


#!/usr/bin/env bash
mvn liquibase:update\
    -Denv=dev\
    -Dliquibase.url="jdbc:postgresql://dev/test?prepareThreshold=0&stringtype=unspecified"\
    -Dliquibase.contexts=non-legacy

Vergleich


In der Entwicklung ist es zweckmäßig, zwei vorhandene Grundlagen für Änderungen zu vergleichen. In den Einstellungen (oder Startparametern) müssen Sie der Referenzdatenbank einen Link und Daten hinzufügen, um darauf zuzugreifen.


liquibase.properties


referenceUsername=test
referenceUrl=jdbc:postgresql://dev/test_reference

Vergleichsschemata


Vergleichen der Schemas url und referenceUrl.


#!/usr/bin/env bash
mvn liquibase:diff\
    -Denv=dev\
    -Dliquibase.referenceUrl="jdbc:postgresql://dev/test?prepareThreshold=0"\
    -Dliquibase.url="jdbc:postgresql://dev/test_reference?prepareThreshold=0"\
    -Dliquibase.diffChangeLogFile=dev/diff.xml

Schema speichern


Es ist auch hilfreich, das aktuelle Datenbankschema mit oder ohne Daten beizubehalten. Es muss beachtet werden, dass Liquibase das Schema beibehält, das nicht vollständig mit dem Original übereinstimmt, z. B. verwendete Domänen oder Vererbung müssen separat hinzugefügt werden (siehe Einschränkungen).


Schema ohne Daten speichern


Erhaltung des bestehenden Basisschemas.


#!/usr/bin/env bash
mvn liquibase:generateChangeLog\
    -Denv=dev\
    -Dliquibase.url="jdbc:postgresql://dev/test_reference?prepareThreshold=0"\
    -Dliquibase.outputChangeLogFile=dev/changelog.xml

Speichern eines Schemas mit Daten


Speichern des Schemas der vorhandenen Datenbank mit Daten.


#!/usr/bin/env bash
mvn liquibase:generateChangeLog\
    -Denv=dev\
    -Dliquibase.url="jdbc:postgresql://dev/test_reference?prepareThreshold=0"\
    -Dliquibase.outputChangeLogFile=dev/changelog.xml

Einschränkungen und Probleme


Arbeiten Sie mit binären Daten in der Datenbank


Beim Entladen, Vergleichen und Verwenden von Binärdaten treten bestimmte Probleme auf, insbesondere das Problem bei der Generierung von Änderungen.



Vererbung und gemeinsame Spalten



Quellcode



Alternative Lösungen


Flyway


Zusammen mit Liquibase ist es in der Java-Community beliebt - http://flywaydb.org/documentation


Sqitch


Perl Analog - http://sqitch.org


FluentMigrator


Аналог для .Net — https://github.com/schambers/fluentmigrator


DBGeni


Аналог для Ruby — http://dbgeni.appsintheopen.com/manual.html


Приложения


Структура проекта


pom.xml - maven makefile
  dev
    liquibase.properties - login/password etc
    master.xml - changesets

Как добавить liquibase в существующий проект



Как работают изменения базы



Больше о формате изменений



Больше про update



Weitere Informationen zum Generieren von Änderungen.



Mehr zu benutzerdefiniertem SQL



Umgang mit datenbankspezifischen Datentypen


<createTable tableName="t_name">
...
<column name="doubleArray" type="DOUBLE_ARRAY"/>
...
</createTable>
<modifySql dbms="postgresql">
<replace replace="DOUBLE_ARRAY" with="double precision[][]"/>
</modifySql>

Andere



Jetzt auch beliebt: