Выбор между XML и SQL для накатывания скриптов LiquiBase на примере Java/Spring/H2

В процессе работы над очередным проектом в команде возникли споры по поводу использования формата XML или SQL в Liquibase. Естественно про Liquibase уже написано много статей, но как всегда хочется добавить свои наблюдения. В статье будет представлен маленький туториал по созданию простенького приложения с бд и рассмотрим разницу метаинформации по данным типам.

Liquibase — это независимая от базы данных библиотека для отслеживания, управления и применения изменений схем базы данных. Для того, чтобы внести изменения в БД, создается файл миграции (*changeset*), который подключается в главный файл (*changeLog*), который контролирует версии и управляет всеми изменениями. В качестве описания структуры и изменений базы данных используется XML, YAML, JSON и SQL форматы.

Основная концепция миграций БД, выглядит следующим образом:



Более подробную информацию по Liquibase можно узнать тут или тут. Надеюсь общая картинка ясна, так что перейдем к созданию проекта.

В тестовом проекте используется


  • Java 8
  • Spring-boot
  • Maven
  • H2
  • ну и сам liquibase

Создание проекта и зависимости


Использование Spring-boot здесь ничем не обусловлено, можно обойтись просто maven-plugin для накатывания скриптов. Итак, приступим.

1. Создаем в IDE maven-проект и в pom файл добавляем следующие зависимости:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.liquibase</groupId>
        <artifactId>liquibase-core</artifactId>
        <version>3.6.3</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
   <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
        </dependency>
</dependencies>

2. В папке resources создать файл application.yml и добавить следующие строчки:

spring:
  liquibase:
    change-log: classpath:/db/changelog/db.changelog-master.yaml
 datasource:
    url: jdbc:h2:mem:test;
    platform: h2
    username: sa
    password:
    driverClassName: org.h2.Driver
  h2:
    console:
      enabled: true

Строка liquibase:change-log: classpath:/db/changelog/db.changelog-master.yaml — сообщает нам, где находится файл со списком скриптов liquibase.

3. В папке resources по пути db.changelog-master создаем cследующие файлы:

  • xmlSchema.xml – скрипт изменений в формате xml
  • sqlSchema.sql — скрипт изменений в формате sql
  • data.xml – добавление данных в таблицу
  • db.changelog-master.yml – список changeSet –ов

4. Добавление данных в файлы:
Для теста необходимо создать две несвязанные т
аблицы и минимальный набор данных.
В файл sqlSchema.sql добавляем всеми известный sql синтаксис:

--liquibase formatted sql
--changeset TestUsers_sql:1

CREATE TABLE test_sql_table
(
    name VARCHAR NOT NULL,
    description VARCHAR
);

--changeset TestUsers_sql:2

CREATE TABLE test_sql_table_2
(
    name VARCHAR NOT NULL,
    description VARCHAR
);

Использование sql в качестве changeset-ов обуславливается легким написанием скриптов. В файлах всем понятный, обычный sql.

Для разделения changeset используется комментарий:
--changeset TestUsers_sql:1 с указанием номера изменений и фамилии
(параметры можно посмотреть тут.)

В файл xmlSchema.sql добавляем DSL, который предоставляет liquibase:


<?xml version="1.0" encoding="UTF-8"?>

<databaseChangeLog
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">

    <changeSet id="Create table test_xml_table" author="TestUsers_xml">

        <createTable tableName="test_xml_table">
            <column name="name" type="character varying">
                <constraints primaryKey="true" nullable="false"/>
            </column>
            <column name="description" type="character varying"/>
        </createTable>

    </changeSet>

    <changeSet id="Create table test_xml_table_2" author="TestUsers_xml">

        <createTable tableName="test_xml_table_2">
            <column name="name" type="character varying">
                <constraints primaryKey="true" nullable="false"/>
            </column>
            <column name="description" type="character varying"/>
        </createTable>

    </changeSet>
</databaseChangeLog>

Данный формат описания создания таблиц является универсальным для разных баз данных. Прям как лозунг джавы: «Написано однажды, работает везде». Liquibase использует xml-описание и компилирует его в определенных sql код, в зависимости от выбранной базы данных. Что очень удобно, для общих параметров.

Каждая операция выполняется в отдельном changeSet, с указанием id и имени автора. Думаю, язык, используемый в xml, очень прост к пониманию и даже не требует объяснения.

5. Загрузим в наши таблички данные, это делать не обязательно, но раз уж сделали таблички, надо в них чего-нить положить. Заполняем файл data.xml, следующими данными:

<?xml version="1.0" encoding="UTF-8"?>

<databaseChangeLog
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">

    <changeSet id="insert data to test_xml_table" author="TestUsers">
        <insert tableName="test_xml_table">
            <column name="name" value="model"/>
            <column name="description" value="какая-либо модель"/>
        </insert>
    </changeSet>

    <changeSet id="insert data to test_xml_table_2" author="TestUsers">
        <insert tableName="test_xml_table_2">
            <column name="name" value="model"/>
            <column name="description" value="какая-либо модель"/>
        </insert>
    </changeSet>

    <changeSet id="insert data to test_sql_table" author="TestUsers">
        <insert tableName="test_sql_table">
            <column name="name" value="model"/>
            <column name="description" value="какая-либо модель"/>
        </insert>
    </changeSet>

    <changeSet id="insert data to test_sql_table_2" author="TestUsers">
        <insert tableName="test_sql_table_2">
            <column name="name" value="model"/>
            <column name="description" value="какая-либо модель"/>
        </insert>
    </changeSet>

</databaseChangeLog>

Файлы для накатывания таблиц созданы, данные для таблиц созданы. Пришло время объединить все это в общий порядок накатывания и запустить наше приложение.

В файл db.changelog-master.yml добавляем наши sql и xml файлы:

databaseChangeLog:
  - include:
      # schema
      file: db/changelog/xmlSchema.xml
  - include:
      file: db/changelog/sqlSchema.sql
      # data
  - include:
      file: db/changelog/data.xml

И теперь, когда у нас все создано. Просто запустим наше приложение. Для запуска можно использовать командную строку или плагин, но мы создадим просто main метод и запустим наше SpringApplication.

Просмотр метаинформации


Теперь, когда мы запустили наши два скриптика на создание и заполнение таблиц, мы можем посмотреть таблицу databaseChangeLog и увидеть, что накатилось.



Результат накатывания xml:

  1. В поле id от файлов xml попадает заголовок, который разработчик указывает в changeSet, каждый отдельный changeSet является отдельной строчкой в базе с указанием заголовка и описания.
  2. Указывается автор каждого изменения.

Результат накатывания sql:

  1. В поле id от файлов sql нет подробной информации об changeSet.
  2. Не указывается автор каждого изменения.

Еще одно важное заключение в сторону использования xml – это rollback. Такие команды как create table, alter table, add column имеют автоматический откат, при использовании xml. Для sql файлов каждый rollback необходимо писать вручную.

Вывод


Каждый выбирает для себя сам, что ему использовать. Но наш выбор пал все таки на сторону xml. Подробная метаинформация и легкий переход на другие БД, перевесили чашу весов от всеми любимого sql-формата.
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 11

    +1
    Свойство nullable для primaryKey=«true» — это прекрасно. Вообще в xml варианте явно избыточность информации. За это его очень не любят
      +2
      Хм, в SQL указываются те же атрибуты, что и для XML: и автора можно, и что там будет ещё нужно. Так что не аргумент :)
        0
        Между XML и SQL в ликвибейзе выберу YAML
          0

          Активно используем liquibase в своем проекте (крупный enterprise). Могу сказать, что использование changeset-ов с XML возможно только для небольших приложений с довольно простой структурой бд. Как только появляется необходимость сложной миграции с кучей таблиц, вьюх, запросами к другим бд через dblink — без нативного SQL не обойтись.
          Самая главная проблема — отладка. Гораздо легче руками написать в каком нибудь GUI запрос, проверить и добавить в changeset, чем мучаться с XML, возможности которого сильно ограничены.
          Единственный минус — все rollback нужно писать руками, а это не всегда возможно. Но у нас редко возникает необходимость откатить состояние бд до прошлых версий

            0

            Активно используем liquibase в своем проекте (крупный enterprise). Могу сказать, что использование changeset-ов с XML возможно только для небольших приложений с довольно простой структурой бд. Как только появляется необходимость сложной миграции с кучей таблиц, вьюх, запросами к другим бд через dblink — без нативного SQL не обойтись.
            Самая главная проблема — отладка. Гораздо легче руками написать в каком нибудь GUI запрос, проверить и добавить в changeset, чем мучаться с XML, возможности которого сильно ограничены.
            Единственный минус — все rollback нужно писать руками, а это не всегда возможно. Но у нас редко возникает необходимость откатить состояние бд до прошлых версий

              0
              чем мучаться с XML, возможности которого сильно ограничены — нуу не совсем, у него огромная поддержка инструкций. На мой взгляд, даже намного читабельные, чем просто файл с SQL
              0
              Для инита достаточно data.sql Древний и проверенный способ. А в статье извините описан мегакостыль, к которому еще вопросы есть. Что мешает создать схему, и заполнять ее с ORM? Зачем создавать себе сложности, и героически их решать?
                0
                data.sql — ну такое. Это если проект у тебя домашний какой-нибудь)
                  0

                  тогда уж schema.sql+data.sql


                  но Liquibase не про инициирование БД, а про её миграции

                    0
                    Да нее, ну надо же было что-нибудь положить в бд) Вот dporollo и не понравился мой подход
                  +1
                  Спасибо за статью.
                  Но мне кажется лучше использовать гибрид (xml/yaml) + чистый sql в файлах (mtm_property_tickler.sql)
                  SCDB — супер-пользователь для запуска (знает только ответственный DevOps)
                  MOBILE — премонтированная папка со скриптами для каждой схемы и нужной структурой с которой работает разработчик DB.
                  Вот пример
                  databaseChangeLog:  
                    - changeSet:
                        id: tag-7.1
                        author: sergey_p
                        context : dev,preprod
                        changes:
                          - tagDatabase:
                                tag: 7.1
                    - changeSet:
                        id: sql_recomp_rollback
                        author: sergey_p
                        context : dev,preprod,prod
                        changes:
                        rollback:
                          - sqlFile:
                                dbms: oracle
                                encoding: utf8
                                path: MOBILE/recomp.sql
                                relativeToChangelogFile: true
                                splitStatements: false
                                stripComments: false
                    - changeSet:
                        id: sql_file_insert
                        author: sergey_p
                        context : dev,preprod,prod
                        changes:
                          - sql:
                                dbms: oracle
                                sql: ALTER SESSION SET CURRENT_SCHEMA = MOBILE
                          - sqlFile:
                                dbms: oracle
                                encoding: cp1251
                                path: MOBILE/data/mtm_property_tickler.sql
                                relativeToChangelogFile: true
                                splitStatements: true
                                stripComments: false
                        rollback:
                          - sql:
                                dbms: oracle
                                sql: ALTER SESSION SET CURRENT_SCHEMA = MOBILE
                          - sqlFile:
                                dbms: oracle
                                encoding: cp1251
                                path: MOBILE/data/rollback/mtm_property_tickler.sql
                                relativeToChangelogFile: true
                                splitStatements: true
                                stripComments: false
                          - sql:
                                dbms: oracle
                                sql: ALTER SESSION SET CURRENT_SCHEMA = SCDB
                    - changeSet:
                        id: sql_file_body
                        author: sergey_p
                        context : dev,preprod,prod
                        changes:
                          - sql:
                                dbms: oracle
                                sql: ALTER SESSION SET CURRENT_SCHEMA = MOBILE
                          - sqlFile:
                                dbms: oracle
                                encoding: cp1251
                                path: MOBILE/packages/body/sale_to_billing.sql
                                relativeToChangelogFile: true
                                splitStatements: false
                                stripComments: false
                        rollback:
                          - sql:
                                dbms: oracle
                                sql: ALTER SESSION SET CURRENT_SCHEMA = MOBILE
                          - sqlFile:
                                dbms: oracle
                                encoding: cp1251
                                path: MOBILE/packages/body/rollback/sale_to_billing.sql
                                relativeToChangelogFile: true
                                splitStatements: false
                                stripComments: false
                          - sql:
                                dbms: oracle
                                sql: ALTER SESSION SET CURRENT_SCHEMA = SCDB
                    - changeSet:
                        id: sql_recomp
                        author: sergey_p
                        context : dev,preprod,prod
                        changes:
                          - sqlFile:
                                dbms: oracle
                                encoding: utf8
                                path: MOBILE/recomp.sql
                                relativeToChangelogFile: true
                                splitStatements: false
                                stripComments: false
                        rollback:
                  

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое