Im
vorherigen Teil haben wir die grundlegenden Funktionen von
Liquibase herausgefunden und ein grundlegendes Beispiel für eine
Spring-Boot- Anwendung geschrieben, die Liquibase zum Initialisieren der Datenbank verwendet. Den vollständigen Basisanwendungscode finden Sie
hier auf GitHub . In diesem Artikel werden wir über das
Liquibase-Maven-Plugin und die zusätzlichen Funktionen sprechen, die es uns für die Versionierung der Datenbankstruktur bietet. Beginnen wir damit, wie Sie mithilfe
der Vergleichsfunktion automatisch Skripte erstellen.
Angenommen, wir müssten einige Änderungen an der Struktur unserer Datenbank vornehmen. Zum Beispiel möchten wir, dass
E-Mails nicht
null sind . Natürlich könnten Sie für eine so kleine Änderung den Code und die Skripte manuell korrigieren, aber was ist, wenn es weitere Änderungen gibt? In diesem Fall hilft uns die Möglichkeit, die in Liquibase integrierte Datenbank zu vergleichen. Ein interessantes Merkmal davon ist, dass Sie nicht nur zwei Datenbanken, sondern auch eine Datenbank mit einer Reihe von JPA-Entitäten in unserer Anwendung vergleichen können. Genau das werden wir jetzt tun!
Erstellen Sie mit liquibase-diff ein Skript mit Änderungen
Im Abschnitt
Plugins der Datei
pom.xml fügen
wir dieses ziemlich komplizierte Design hinzu. Dies ist das
Liquibase-Maven-Plugin , mit dem eine Abhängigkeit verbunden ist, um
Entitäten im Ruhezustand zu analysieren und mit YAML-Dateien zu arbeiten. Das Plugin hilft uns dabei, automatisch
Liquibase-Skripte zu generieren, indem Strukturen in zwei Datenbanken oder sogar Datenstrukturen in einer Datenbank und einer Reihe von Hiberante-Entitäten in unserer Anwendung verglichen werden (genau dafür wurde
Liquibase-Hibernate5 hinzugefügt).
<plugin> <groupId>org.liquibase</groupId> <artifactId>liquibase-maven-plugin</artifactId> <version>3.8.1</version> <configuration> <propertyFile>${project.build.outputDirectory}/liquibase-maven-plugin.properties</propertyFile> <systemProperties> <user.name>your_liquibase_username</user.name> </systemProperties> <logging>info</logging> </configuration> <dependencies> <dependency> <groupId>org.liquibase.ext</groupId> <artifactId>liquibase-hibernate5</artifactId> <version>3.6</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>2.2.0.RELEASE</version> </dependency> </dependencies> </plugin>
Achten Sie auf die Einstellung
user.name . Es ist optional, aber ohne es gibt Liquibase in den erstellten Skripten den Namen des aktuellen Betriebssystembenutzers an, unter dem das Plugin gestartet wird.
Die Einstellungen für das Plugin können direkt in pom.xml geschrieben oder beim Aufrufen von maven als Befehlszeilenparameter übergeben werden. Ich bevorzuge jedoch die Option mit einer separaten Datei
liquibase-maven-plugin.properties . Der Inhalt sieht ungefähr so aus.
changeLogFile= @project.basedir@/src/main/resources/db/changelog/db.changelog-master.yaml url= jdbc:mysql://localhost:3306/geek_db?createDatabaseIfNotExist=true&allowPublicKeyRetrieval=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC username= dbusername password= dbpassword driver= com.mysql.cj.jdbc.Driver referenceUrl= hibernate:spring:ru.usharik.liquibase.demo.persist.model?dialect=org.hibernate.dialect.MySQLDialect diffChangeLogFile= @project.basedir@/src/main/resources/db/changelog/db.changelog-@timestamp@.yaml ignoreClasspathPrefix= true
Es lohnt sich, auf die Parameter
url und
referenceUrl zu achten. Das Skript, das nach dem Vergleich eine Liquibase erstellt, ist der Unterschied zwischen der Basis über den Link referenceUrl und der Basis über den
URL- Link. Wenn Sie dieses Skript später über den
URL- Link auf der Basis ausführen, entspricht es dem auf dem referenceUrl-Link. Besonderes Augenmerk sollte auf den Link referenceUrl gelegt werden. Wie Sie sehen, bezieht es sich nicht auf die Datenbank, sondern auf das Paket unserer Anwendung, in dem sich die Entitätsklassen befinden. Dank dessen können wir jetzt ein Skript finden, das der Datenbank die Änderungen hinzufügt, die im Code vorgenommen wurden.
Jetzt müssen wir das Maven-Resource-Plugin so konfigurieren, dass Platzhalter in der Einstellungsdatei ersetzt werden, z. B.
@project.basedir@
und
@timestamp@
. Fügen Sie dazu den
Build- Abschnitt des folgenden Formulars zum
Build- Abschnitt hinzu
<resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>*.properties</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <filtering>false</filtering> <includes> <include>**/*.*</include> </includes> </resource> </resources>
Übrigens ändert Spring Boot das Standardformat für Platzhalter, die mit dem
Maven-Resource-Plugin ${smth}
von
${smth}
in
@smth@
. Tatsache ist, dass Platzhalter vom
@smth@
in Spring Boot verwendet werden, um Umgebungsvariablen und Parameter der Spring Boot-Anwendung selbst zu ersetzen.
Außerdem ändern wir den
Eigenschaftenabschnitt in
pom.xml geringfügig, um der
Zeitstempelvariablen den Wert in dem von uns benötigten Format zuzuweisen. Leider kann das Standardformat Zeichen enthalten, die in Dateinamen für einige Betriebssysteme verboten sind.
<properties> <java.version>1.8</java.version> <timestamp>${maven.build.timestamp}</timestamp> <maven.build.timestamp.format>yyyyMMdd-HHmmssSSS</maven.build.timestamp.format> </properties>
Ändern wir nun das E-Mail-Feld in der Benutzerklasse
@Column(name = "email", nullable = false) private String email;
Führen
Sie abschließend den Befehl build mit dem
Liquibase-Maven-Plugin zum Vergleich aus.
mvn clean install liquibase:diff -DskipTests=true
In diesem Fall müssen wir das Projekt vollständig neu erstellen, da das Plugin (liquibase: diff) keine kompilierten Quellen verwendet, sondern kompilierte Entitätsklassendateien aus dem Zielordner für die Analyse.
Wenn alles korrekt ausgeführt wurde, wird nach erfolgreicher Ausführung des Befehls im Ordner
resources / db / changelog eine Datei mit dem Namen
db.changelog-20190723-100748666.yaml angezeigt . Aufgrund der Tatsache, dass wir das aktuelle Datum und die aktuelle Uhrzeit im Dateinamen verwenden, erhalten wir bei jedem Start eine neue Datei, was sehr praktisch ist. Wenn Sie bereits eine Datenbank mit der Tabellenstruktur erstellt haben, die der vorherigen Lektion entspricht, sollte der Inhalt der Datei folgendermaßen aussehen.
databaseChangeLog: - changeSet: id: 1563876485764-1 author: your_liquibase_username (generated) changes: - addNotNullConstraint: columnDataType: varchar(255) columnName: email tableName: users
Wie Sie sehen können, nimmt dieses Skript genau die Änderung vor, die im Code vorgenommen wurde. Als Übung würde ich empfehlen, dass Sie dieses Skript für eine leere Datenbank ausführen und sich das Ergebnis ansehen.
Als nächstes können wir das changeSet einfach aus dieser Datei in
db.changelog-master.yaml kopieren oder diese Datei mit einer Anweisung damit verbinden
- include: file: db.changelog-20190723-100748666.yaml relativeToChangelogFile: true
Außerdem müssen Sie in dieser Datei den
logicalFilePath: db/changelog/db.changelog-20190723-100748666.yaml
angeben
logicalFilePath: db/changelog/db.changelog-20190723-100748666.yaml
, ähnlich wie in
db.changelog-master.yaml .
Dies hilft bei der Bewältigung einiger Probleme, die bei Verwendung der in der Anwendung integrierten Liquibase-Bean und des
Liquibase-Maven-Plugins möglich sind. Starten Sie danach die Anwendung neu oder führen Sie den folgenden Befehl aus:
mvn liquibase:update
Versuchen wir, den Code komplexer zu ändern. Fügen Sie beispielsweise eine Rollentabelle hinzu, die eine Viele-zu-Viele-Beziehung zur Benutzertabelle hat.
@Entity @Table(name = "roles") public class Role implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; @Column(name = "name", unique = true, nullable = false) private String name; @ManyToMany(mappedBy = "roles") private Set<User> users;
Und in der Benutzertabelle fügen wir hinzu
@ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "users_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) private Set<Role> roles;
Nach dem Start des Vergleichs erhalten wir eine Datei mit dem folgenden Inhalt
databaseChangeLog: - changeSet: id: 1563877765929-1 author: your_liquibase_username (generated) changes: - createTable: columns: - column: autoIncrement: true constraints: primaryKey: true primaryKeyName: rolesPK name: id type: BIGINT - column: constraints: nullable: false name: name type: VARCHAR(255) tableName: roles - changeSet: id: 1563877765929-2 author: your_liquibase_username (generated) changes: - createTable: columns: - column: constraints: nullable: false name: user_id type: BIGINT - column: constraints: nullable: false name: role_id type: BIGINT tableName: users_roles - changeSet: id: 1563877765929-3 author: your_liquibase_username (generated) changes: - addPrimaryKey: columnNames: user_id, role_id tableName: users_roles - changeSet: id: 1563877765929-4 author: your_liquibase_username (generated) changes: - addUniqueConstraint: columnNames: name constraintName: UC_ROLESNAME_COL tableName: roles - changeSet: id: 1563877765929-5 author: your_liquibase_username (generated) changes: - addForeignKeyConstraint: baseColumnNames: user_id baseTableName: users_roles constraintName: FK2o0jvgh89lemvvo17cbqvdxaa deferrable: false initiallyDeferred: false referencedColumnNames: id referencedTableName: users - changeSet: id: 1563877765929-6 author: your_liquibase_username (generated) changes: - addForeignKeyConstraint: baseColumnNames: role_id baseTableName: users_roles constraintName: FKj6m8fwv7oqv74fcehir1a9ffy deferrable: false initiallyDeferred: false referencedColumnNames: id referencedTableName: roles
Wir können diese Datei auch problemlos zu Arbeitsskripten hinzufügen.
Änderungen rückgängig machen
Nun wollen wir sehen, wie die vorgenommenen Änderungen rückgängig gemacht werden. Aus irgendeinem Grund können diese in changeSets angegebenen Bezeichner nicht zum Zurücksetzen auf sie verwendet werden. Es gibt drei Optionen: Geben Sie einen Rollback-Punkt an
- durch die Anzahl der changeSets, die vom aktuellen zählen
- nach dem Datum der Änderung
- via tag (set mit changeSet einer besonderen Art)
Das Tag wird wie folgt gesetzt.
- changeSet: id: some_uniqui_id author: liquibase_user_name changes: - tagDatabase: tag: db_tag
Nun, die Befehle für die drei aufgeführten Möglichkeiten zum Zurücksetzen
mvn liquibase:rollback -Dliquibase.rollbackTag=db_tag mvn liquibase:rollback -Dliquibase.rollbackCount=1 mvn liquibase:rollback "-Dliquibase.rollbackDate=Jun 03, 2017"
Und zum Schluss noch ein paar zusätzliche Materialien
- Code für diesen Artikel
- Informationen zum Rollback in Liquibase
- Informationen zur Migration mit Liquibase
- Liquibase auf Github
- Sehr guter Artikel über verschiedene Ansätze zur Versionierung einer Datenbank
Natürlich freue ich mich über Kommentare, Ergänzungen, Klarstellungen usw.