{"id":679,"date":"2026-06-01T00:32:47","date_gmt":"2026-05-31T23:32:47","guid":{"rendered":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/?p=679"},"modified":"2026-06-17T00:33:39","modified_gmt":"2026-06-16T23:33:39","slug":"liquibase-datenbank-migrationen-versionieren-mit-yaml-xml-und-json","status":"publish","type":"post","link":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/?p=679","title":{"rendered":"Liquibase \u2014 Datenbank-Migrationen versionieren mit YAML, XML und JSON"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Datenbank-Migrationen sind ein kritischer Bestandteil moderner Softwareentwicklung. Jede \u00c4nderung am Schema muss nachvollziehbar, reproduzierbar und r\u00fcckg\u00e4ngig machbar sein. W\u00e4hrend&nbsp;<strong>Flyway<\/strong>&nbsp;auf nummerierte SQL-Dateien setzt, verfolgt&nbsp;<strong>Liquibase<\/strong>&nbsp;einen deklarativen Ansatz: \u00c4nderungen werden in Changeset-Dateien in den Formaten YAML, XML, JSON oder SQL beschrieben.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Warum Liquibase?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Liquibase hat gegen\u00fcber Flyway einige entscheidende Vorteile:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Datenbankunabh\u00e4ngigkeit<\/strong>\u00a0\u2014 ein Changeset kann (mit Einschr\u00e4nkungen) gegen PostgreSQL, MySQL, Oracle und MSSQL ausgef\u00fchrt werden<\/li>\n\n\n\n<li><strong>Rollback-Unterst\u00fctzung<\/strong>\u00a0\u2014 Liquibase kann \u00c4nderungen automatisch oder manuell zur\u00fccksetzen<\/li>\n\n\n\n<li><strong>Mehrere Formate<\/strong>\u00a0\u2014 YAML, XML, JSON oder SQL als Changeset-Format<\/li>\n\n\n\n<li><strong>Preconditions<\/strong>\u00a0\u2014 Bedingungen, die vor der Ausf\u00fchrung gepr\u00fcft werden<\/li>\n\n\n\n<li><strong>Contexts und Labels<\/strong>\u00a0\u2014 Migrationen lassen sich zielgerichtet auf bestimmte Umgebungen anwenden<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Integration mit Spring Boot 4.1<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In Spring Boot 4.1 ist Liquibase per Auto-Configuration integriert. Sobald&nbsp;<code>liquibase-core<\/code>&nbsp;auf dem Classpath liegt, f\u00fchrt Spring Boot beim Start automatisch&nbsp;<code>liquibase update<\/code>&nbsp;aus:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.liquibase<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>liquibase-core<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span># application.yml<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nspring:\n  liquibase:\n    change-log: classpath:db\/changelog\/db.changelog-master.yaml\n    enabled: true\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Die Master-Changelog-Datei referenziert alle einzelnen Changesets:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">&lt;em&gt;<span class=\"hljs-comment\"># db.changelog-master.yaml&lt;\/em&gt;<\/span>\ndatabaseChangeLog:\n  - <span class=\"hljs-keyword\">include<\/span>:\n      file: db\/changelog\/<span class=\"hljs-number\">001<\/span>-create-users.yaml\n  - <span class=\"hljs-keyword\">include<\/span>:\n      file: db\/changelog\/<span class=\"hljs-number\">002<\/span>-add-email-column.yaml\n  - <span class=\"hljs-keyword\">include<\/span>:\n      file: db\/changelog\/<span class=\"hljs-number\">003<\/span>-create-orders.sql\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Changesets in YAML<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">YAML ist das am einfachsten lesbare Format f\u00fcr Liquibase-Changesets:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">&lt;em&gt;<span class=\"hljs-comment\"># 001-create-users.yaml&lt;\/em&gt;<\/span>\ndatabaseChangeLog:\n  - changeSet:\n      id: <span class=\"hljs-number\">1<\/span>\n      author: rene\n      changes:\n        - createTable:\n            tableName: users\n            columns:\n              - column:\n                  name: id\n                  type: BIGINT\n                  autoIncrement: <span class=\"hljs-keyword\">true<\/span>\n                  constraints:\n                    primaryKey: <span class=\"hljs-keyword\">true<\/span>\n              - column:\n                  name: username\n                  type: VARCHAR(<span class=\"hljs-number\">255<\/span>)\n                  constraints:\n                    nullable: <span class=\"hljs-keyword\">false<\/span>\n                    unique: <span class=\"hljs-keyword\">true<\/span>\n              - column:\n                  name: created_at\n                  type: TIMESTAMP\n                  defaultValueComputed: CURRENT_TIMESTAMP\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Rollbacks<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Rollback beschreibt, wie eine Migration r\u00fcckg\u00e4ngig gemacht wird:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">  - changeSet:\n      id: 2\n      author: rene\n      changes:\n        - addColumn:\n            tableName: users\n            columns:\n              - column:\n                  name: email\n                  type: VARCHAR(320)\n      rollback:\n        - dropColumn:\n            tableName: users\n            columnName: email\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Bei einfachen \u00c4nderungen wie&nbsp;<code>createTable<\/code>&nbsp;generiert Liquibase das Rollback automatisch. Bei komplexeren Operationen muss es explizit definiert werden.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">SQL-basierte Changesets<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Auch purer SQL-Code l\u00e4sst sich verwenden \u2014 mit allen Vorteilen des Liquibase-Trackings:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span>-- 002-add-index.sql<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span>-- liquibase formatted sql<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span>-- changeset rene:3<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nCREATE INDEX idx_users_email ON users(email);\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span>-- rollback DROP INDEX idx_users_email;<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Wichtig: Der Kommentar&nbsp;<code>-- liquibase formatted sql<\/code>&nbsp;teilt Liquibase mit, dass die SQL-Datei Changeset-Metadaten enth\u00e4lt.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Preconditions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Preconditions verhindern, dass Migrationen unter falschen Voraussetzungen ausgef\u00fchrt werden:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">  - changeSet:\n      id: 4\n      author: rene\n      preConditions:\n        - onFail: HALT\n        - tableExists:\n            tableName: users\n        - not:\n            columnExists:\n              tableName: users\n              columnName: phone\n      changes:\n        - addColumn:\n            tableName: users\n            columns:\n              - column:\n                  name: phone\n                  type: VARCHAR(20)\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Mit&nbsp;<code>onFail: HALT<\/code>,&nbsp;<code>MARK_RAN<\/code>&nbsp;oder&nbsp;<code>CONTINUE<\/code>&nbsp;steuert man das Verhalten, falls die Vorbedingung nicht erf\u00fcllt ist.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Kontextabh\u00e4ngige Migrationen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Contexts und Labels erlauben migrationsspezifische Ausf\u00fchrungen je nach Umgebung:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">  - changeSet:\n      id: <span class=\"hljs-number\">5<\/span>\n      <span class=\"hljs-attr\">author<\/span>: rene\n      <span class=\"hljs-attr\">context<\/span>: dev\n      <span class=\"hljs-attr\">labels<\/span>: seed\n      <span class=\"hljs-attr\">changes<\/span>:\n        - insert:\n            tableName: users\n            <span class=\"hljs-attr\">columns<\/span>:\n              - column:\n                  name: username\n                  <span class=\"hljs-attr\">value<\/span>: <span class=\"hljs-string\">\"testuser\"<\/span>\n              - column:\n                  name: email\n                  <span class=\"hljs-attr\">value<\/span>: <span class=\"hljs-string\">\"test@example.com\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span># Nur Changesets mit dem Kontext \"dev\" ausf\u00fchren<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nmvn liquibase:update -Dliquibase.contextFilter=dev\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span># Changesets mit Label \"seed\" ausschlie\u00dfen<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nmvn liquibase:update -Dliquibase.labelFilter=!seed\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Fazit<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Liquibase ist die ideale Wahl, wenn du datenbankunabh\u00e4ngige, deklarativ beschriebene Migrationen mit vollst\u00e4ndiger Rollback-F\u00e4higkeit ben\u00f6tigst. Die Unterst\u00fctzung f\u00fcr YAML-Changelogs macht Changesets besonders lesbar und diff-bar, w\u00e4hrend Contexts und Preconditions die Steuerung in komplexen Deployment-Pipelines erlauben. In Spring Boot 4.1 ist Liquibase per Autokonfiguration sofort einsatzbereit \u2014 sobald&nbsp;<code>liquibase-core<\/code>&nbsp;auf dem Classpath liegt, werden Migrationen beim Anwendungsstart automatisch ausgef\u00fchrt.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Datenbank-Migrationen sind ein kritischer Bestandteil moderner Softwareentwicklung. Jede \u00c4nderung am Schema muss nachvollziehbar, reproduzierbar und r\u00fcckg\u00e4ngig machbar sein. W\u00e4hrend&nbsp;Flyway&nbsp;auf nummerierte SQL-Dateien setzt, verfolgt&nbsp;Liquibase&nbsp;einen deklarativen Ansatz: \u00c4nderungen werden in Changeset-Dateien in den Formaten YAML, XML, JSON oder SQL beschrieben. Warum Liquibase? Liquibase hat gegen\u00fcber Flyway einige entscheidende Vorteile: Integration mit Spring Boot 4.1 In Spring Boot [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-679","post","type-post","status-publish","format-standard","hentry","category-plain_java"],"_links":{"self":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/679","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=679"}],"version-history":[{"count":1,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/679\/revisions"}],"predecessor-version":[{"id":680,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/679\/revisions\/680"}],"wp:attachment":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=679"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=679"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=679"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}