Механізм перетворення моделі структури БД у вигляді Liquibase ChangeSet

Функціональні сценарії

  • Створення Diff Document на основі DataModelSnapshot бази даних (поточна версія та версія змін) в форматі .json відповідно до вимог.

  • Створення Xml liquibase Changesets на основі згенерованого Diff Document.

Out of scope

  • Операція видалення для AccessRule

Технологічний стек

Технологія / Бібліотека Версія Ліцензія Документація Опис

Guava

31.1-jre

Apache License 2.0

Документація

Guava — Google бібліотека для роботи з колекціями в Java.

Компонент Diff calculator

Компонент Diff calculator на основі DataModelSnapshot створює Diff Document. Так як DataModelSnapshot має структуру на основі Map, то для підрахунку різниці використовується бібліотека Guava.

Підрахунок різниці між двома станами структури БД на тестових даних займає 30 мс.

Приклад згенерованого Diff Document

{
   "tablesToAdd":{
      "application_type":{
         "name":"application_type",
         "remarks":"Довідник типів заяв",
         "columns":{
            "name":{
               "name":"name",
               "remarks":"Тип заяви",
               "type":"text",
               "notNullFlag":true,
               "tableName":"application_type"
            },
            "application_type_id":{
               "name":"application_type_id",
               "remarks":"Ідентифікатор типів заяв",
               "type":"uuid",
               "defaultValue":"uuid_generate_v4()",
               "notNullFlag":true,
               "tableName":"application_type"
            },
            "constant_code":{
               "name":"constant_code",
               "remarks":"Символьна константа",
               "type":"text",
               "notNullFlag":true,
               "tableName":"application_type"
            }
         },
         "primaryKey":{
            "name":"pk_application_type_id",
            "columns":[
               {
                  "name":"application_type_id",
                  "sorting":"ASC"
               }
            ],
            "tableName":"application_type"
         },
         "uniqueConstraints":{
            "application_type_name_key":{
               "name":"application_type_name_key",
               "columns":[
                  {
                     "name":"name",
                     "sorting":"ASC"
                  }
               ],
               "tableName":"application_type"
            }
         }
      }
   },
   "tablesToEdit":{
      "staff":{
         "attributesToEdit":{
            "remarks":"Кадровий склад"
         },
         "columnsToAdd":{
            "researches":{
               "name":"researches",
               "remarks":"Масив ідентифікаторів досліджень",
               "type":"_uuid",
               "notNullFlag":false,
               "tableName":"staff"
            }
         },
         "columnsToEdit":{
            "education":{
               "remarks":"Освіта, фах",
               "defaultValue":"Юридичний факультет"
            }
         },
         "foreignKeysToAdd":{
            "fk_staff_status":{
               "name":"fk_staff_status",
               "targetTable":"staff_status",
               "columnPairs":[
                  {
                     "sourceColumnName":"staff_status_id",
                     "targetColumnName":"staff_status_id"
                  }
               ],
               "sourceTable":"staff"
            },
            "fk_staff_laboratory_new":{
               "name":"fk_staff_laboratory_new",
               "targetTable":"laboratory",
               "columnPairs":[
                  {
                     "sourceColumnName":"laboratory_id",
                     "targetColumnName":"laboratory_id"
                  }
               ],
               "sourceTable":"staff"
            }
         },
         "foreignKeysToDelete":{
            "fk_staff_laboratory":{
               "name":"fk_staff_laboratory",
               "targetTable":"laboratory",
               "columnPairs":[
                  {
                     "sourceColumnName":"laboratory_id",
                     "targetColumnName":"laboratory_id"
                  }
               ],
               "sourceTable":"staff"
            }
         },
         "indicesToAdd":{
            "ix_staff_staff_status__staff_status_id":{
               "name":"ix_staff_staff_status__staff_status_id",
               "columns":[
                  {
                     "name":"staff_status_id",
                     "sorting":"ASC"
                  }
               ],
               "tableName":"staff"
            },
            "ix_staff_laboratory__laboratory_id_new":{
               "name":"ix_staff_laboratory__laboratory_id_new",
               "columns":[
                  {
                     "name":"laboratory_id",
                     "sorting":"ASC"
                  }
               ],
               "tableName":"staff"
            }
         },
         "indicesToDelete":{
            "ix_staff_laboratory__laboratory_id":{
               "name":"ix_staff_laboratory__laboratory_id",
               "columns":[
                  {
                     "name":"laboratory_id",
                     "sorting":"ASC"
                  }
               ],
               "tableName":"staff"
            }
         }
      }
   },
   "accessRulesToAdd":{
      "1":{
         "permissionId":"1",
         "roleName":"isAuthenticated",
         "objectName":"laboratory",
         "columnName":"edrpou",
         "operation":"SELECT"
      }
   }
}

TODO: необхідно реалізувати Json схему опису Diff Document.

Компонент LiquibaseDataModelSerializer

Компонент LiquibaseDataModelSerializer на основі Diff Document генерує Liquibase XML-Changelog за наступним алгоритмом:

  • Отримуємо Liquibase ChangeLog

  • На основі Diff Document формуємо Liquibase Changes

  • Додаємо Liquibase Changes до потрібного Liquibase ChangeSet або створюємо новий

  • Liquibase на основі оновленого Liquibase ChangeLog генерує Liquibase XML-Changelog

Приклад згенерованого Liquibase XML-Changelog

<?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:pro="http://www.liquibase.org/xml/ns/pro"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext
                   https://nexus-public-mdtu-ddm-edp-cicd.apps.cicd2.mdtu-ddm.projects.epam.com/repository/extensions/com/epam/digital/data/platform/liquibase-ext-schema/latest/liquibase-ext-schema-latest.xsd
                   http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.6.xsd
                   http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.6.xsd">

    <changeSet author="system" id="id" objectQuotingStrategy="LEGACY">
        <createTable ext:isObject="false" remarks="Довідник типів заяв" tableName="application_type">
            <column name="name" remarks="Тип заяви" type="text">
                <constraints nullable="false"/>
            </column>
            <column defaultValue="uuid_generate_v4()" name="application_type_id" remarks="Ідентифікатор типів заяв" type="uuid">
                <constraints nullable="false" primaryKey="true" primaryKeyName="pk_application_type_id"/>
            </column>
            <column name="constant_code" remarks="Символьна константа" type="text">
                <constraints nullable="false"/>
            </column>
        </createTable>
        <addUniqueConstraint columnNames="name" constraintName="application_type_name_key" tableName="application_type"/>
        <setTableRemarks remarks="Кадровий склад" tableName="staff"/>
        <addColumn tableName="staff">
            <column name="researches" remarks="Масив ідентифікаторів досліджень" type="_uuid"/>
        </addColumn>
        <setColumnRemarks columnName="education" remarks="Освіта, фах" tableName="staff"/>
        <dropDefaultValue columnName="education" tableName="staff"/>
        <addDefaultValue columnName="education" defaultValue="Юридичний факультет" tableName="staff"/>
        <dropForeignKeyConstraint baseTableName="staff" constraintName="fk_staff_laboratory"/>
        <addForeignKeyConstraint baseColumnNames="staff_status_id" baseTableName="staff" constraintName="fk_staff_status" referencedColumnNames="staff_status_id" referencedTableName="staff_status"/>
        <addForeignKeyConstraint baseColumnNames="laboratory_id" baseTableName="staff" constraintName="fk_staff_laboratory_new" referencedColumnNames="laboratory_id" referencedTableName="laboratory"/>
        <dropIndex indexName="ix_staff_laboratory__laboratory_id" tableName="staff"/>
        <createIndex indexName="ix_staff_staff_status__staff_status_id" tableName="staff">
            <column descending="false" name="staff_status_id"/>
        </createIndex>
        <createIndex indexName="ix_staff_laboratory__laboratory_id_new" tableName="staff">
            <column descending="false" name="laboratory_id"/>
        </createIndex>
        <ext:rbac>
            <ext:role name="isAuthenticated">
                <ext:table name="laboratory">
                    <ext:column name="edrpou" read="true"/>
                </ext:table>
            </ext:role>
        </ext:rbac>
    </changeSet>
</databaseChangeLog>