Java for Beginner
675 subscribers
559 photos
156 videos
12 files
858 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Введение в Liquibase

1. Что такое Liquibase и зачем он нужен

Liquibase — это инструмент для управления изменениями в структуре базы данных (миграциями).

Он позволяет:
Контролировать эволюцию схемы БД через версионированные скрипты.
Автоматизировать применение изменений (создание таблиц, изменение столбцов и т. д.).
Обеспечивать консистентность между разными окружениями (dev, test, prod).
Поддерживать откат изменений (rollback) в случае ошибок.


Проблемы, которые решает Liquibase:
Ручное выполнение SQL-скриптов на разных серверах.
Отсутствие истории изменений БД.
Несовместимость версий схемы БД между разработчиками.


2. Поддерживаемые СУБД

Liquibase работает с большинством популярных баз данных:
Реляционные: PostgreSQL, MySQL, Oracle, SQL Server, H2, SQLite.
NoSQL: MongoDB (с ограниченной поддержкой).
Облачные: Amazon RDS, Google Cloud SQL.

3. Принципы работы


Changelog (журнал изменений)

Это главный файл, который ссылается на все изменения (changeSet’ы). Хранится в формате XML, YAML, JSON или SQL.

ChangeSet (набор изменений)

Минимальная единица изменения в Liquibase.

Каждый changeSet:
Имеет уникальный идентификатор (id + author).
Описывает одно или несколько изменений (например, создание таблицы).
Может содержать атрибуты (runOnChange, failOnError).


Контроль версий

Liquibase ведет таблицы в БД:
DATABASECHANGELOG — журнал примененных changeSet’ов.
DATABASECHANGELOGLOCK — блокировка для предотвращения конфликтов.


4. Форматы changelog-файлов

XML — строгая структура, но многословный.
<?xml version="1.0" encoding="UTF-8"?>  
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.6.xsd">

<changeSet id="1" author="alex">
<createTable tableName="users">
<column name="id" type="INT" autoIncrement="true">
<constraints primaryKey="true"/>
</column>
<column name="username" type="VARCHAR(50)"/>
</createTable>
</changeSet>
</databaseChangeLog>


YAML — лаконичный, но чувствителен к отступам.
databaseChangeLog:  
- changeSet:
id: 1
author: alex
changes:
- createTable:
tableName: users
columns:
- column:
name: id
type: INT
autoIncrement: true
constraints:
primaryKey: true
- column:
name: username
type: VARCHAR(50)


SQL — прост для DBA, но менее гибкий.
--liquibase formatted sql  

--changeset alex:1
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50)
);



5. Интеграция Liquibase в Java-проект (Maven/Gradle)

Maven

Добавить в pom.xml:
<dependency>  
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>4.20.0</version>
</dependency>


Конфигурация в
application.properties (Spring Boot):
spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.yaml  
spring.liquibase.url=jdbc:postgresql://localhost:5432/mydb
spring.liquibase.user=user
spring.liquibase.password=pass


Gradle
Добавить в build.gradle:
implementation 'org.liquibase:liquibase-core:4.20.0'


#Java #middle #Liquibase
Структура и основные команды Liquibase

1. Файл конфигурации (liquibase.properties)

Файл liquibase.properties содержит настройки для подключения к БД и управления Liquibase.

Основные параметры:
# Подключение к БД  
url=jdbc:postgresql://localhost:5432/mydb
username=user
password=pass
driver=org.postgresql.Driver

# Настройки changelog
changeLogFile=db/changelog/db.changelog-master.xml

# Дополнительные параметры
liquibase.hub.mode=off # Отключение Liquibase Hub (если не используется)


Разбор параметров:
url – JDBC-URL базы данных (зависит от СУБД).
username и password – учетные данные для подключения.
driver – класс JDBC-драйвера (например, org.postgresql.Driver для PostgreSQL).
changeLogFile – путь к главному файлу changelog.


Где размещается?

В корне проекта (рядом с pom.xml/build.gradle).

Или указывается явно при запуске:
liquibase --defaults-file=config/liquibase.properties update


2. Основные команды CLI


Liquibase предоставляет консольные команды для управления миграциями.

update – применение изменений

Применяет все невыполненные changeSet’ы из changelog.
liquibase update


Что происходит?
Liquibase проверяет таблицу DATABASECHANGELOG.
Находит changeSet’ы, которых нет в этой таблице.
Применяет их в порядке указания в changelog.

rollback – откат изменений

Возвращает БД к предыдущему состоянию.
# Откат до тега v1.0  
liquibase rollback v1.0

# Откат последнего changeSet’а
liquibase rollbackCount 1

# Откат до определенной даты
liquibase rollbackToDate 2024-01-01


status – проверка состояния БД

Показывает, какие changeSet’ы не применены.
liquibase status


Вывод:
2 changesets have not been applied to mydb@jdbc:postgresql://localhost:5432/mydb


validate – проверка корректности changelog

Проверяет синтаксис changelog без применения изменений.
liquibase validate


Если есть ошибки, выведет сообщение, например:
ERROR: ChangeSet db/changelog/changes/001-create-table.xml::1::alex failed. Reason: Table 'users' already exists


3. Жизненный цикл изменений


Как Liquibase применяет changeSet’ы?

Парсинг changelog:
Liquibase читает главный файл (например, db.changelog-master.xml).
Загружает все вложенные changeSet’ы.


Проверка DATABASECHANGELOG:
Сравнивает список changeSet’ов с теми, что уже выполнены (хранятся в таблице DATABASECHANGELOG).

Применение изменений:
Невыполненные changeSet’ы применяются в порядке их объявления.
После успешного выполнения информация о changeSet’е записывается в DATABASECHANGELOG.


Контроль версий и порядок выполнения

Уникальность changeSet’а определяется по:
id (например, "1").
author (например, "alex").
Путь к файлу changelog.


Порядок выполнения:
<databaseChangeLog>
<include file="db/changelog/changes/001-create-users.xml"/>
<include file="db/changelog/changes/002-add-email.xml"/>
</databaseChangeLog>


Liquibase выполнит 001-create-users.xml раньше, чем 002-add-email.xml.

Атрибуты, влияющие на выполнение:

runOnChange="true" – повторно выполнит changeSet, если его содержимое изменилось.
failOnError="false" – пропустит ошибку (например, если таблица уже существует).


#Java #middle #Liquibase
Типы changesets и стратегии развертывания в Liquibase

1. Основные изменения (changesets)

1.1. Создание таблиц (createTable)
Позволяет создавать новые таблицы с указанием столбцов и ограничений.

Пример (YAML):

databaseChangeLog:  
- changeSet:
id: 1
author: dev
changes:
- createTable:
tableName: users
columns:
- column:
name: id
type: BIGINT
autoIncrement: true
constraints:
primaryKey: true
nullable: false
- column:
name: username
type: VARCHAR(50)
constraints:
unique: true


Разбор:
tableName – имя таблицы.
columns – список столбцов.
constraints – ограничения (primaryKey, nullable, unique).


1.2. Добавление столбцов (addColumn)

Добавляет новый столбец в существующую таблицу.

Пример (YAML):
- changeSet:  
id: 2
author: dev
changes:
- addColumn:
tableName: users
columns:
- column:
name: email
type: VARCHAR(100)
constraints:
nullable: false


Параметры:
tableName – имя таблицы.
defaultValue – значение по умолчанию (опционально).


1.3. Изменение структуры (alterTable, modifyDataType)
Используется для изменения типа данных столбца или переименования таблицы.

Пример (YAML):
- changeSet:  
id: 3
author: dev
changes:
- alterTable:
tableName: users
columns:
- column:
name: username
type: VARCHAR(100) # Увеличение длины
- renameColumn:
tableName: users
oldColumnName: email
newColumnName: email_address


2. Управление зависимостями

2.1. Атрибуты runAlways и runOnChange
runAlways="true" – changeSet выполняется каждый раз, даже если уже был применен.
- changeSet:  
id: 4
author: dev
runAlways: true
changes:
- sql:
sql: "INSERT INTO logs (message) VALUES ('Migration executed')"
runOnChange="true" – выполняется только если содержимое changeSet’а изменилось.


2.2. Условия (preConditions)
Проверяют условия перед выполнением changeSet’а.

Пример (YAML):
- changeSet:  
id: 5
author: dev
preConditions:
- tableExists:
tableName: users
changes:
- addColumn:
tableName: users
columns:
- column:
name: status
type: VARCHAR(20)


Доступные проверки:
tableExists / tableNotExists
columnExists
sqlCheck (произвольный SQL-запрос).


3. Стратегии развертывания

3.1. Последовательное применение
ChangeSet’ы выполняются строго по порядку, указанному в databaseChangeLog.

Пример:
databaseChangeLog:  
- include:
file: db/changelog/v1.0/001-create-tables.yaml
- include:
file: db/changelog/v1.0/002-add-constraints.yaml


3.2. Параллельное выполнение
Liquibase не поддерживает параллельное выполнение changeSet’ов по умолчанию.

Однако можно:
Разделять changelog на независимые модули.
Использовать contexts для условного выполнения.


Пример:
- changeSet:  
id: 6
author: dev
context: "!prod" # Не выполнять в production
changes:
- insert:
tableName: test_data
columns:
- column:
name: value
value: "Test"


4. Поддержка разных форматов

Liquibase позволяет использовать:
YAML – лаконичный и удобный для разработчиков.
XML – строгий и структурированный.
JSON – альтернатива YAML.
SQL – для прямого написания SQL-запросов.


Пример SQL-формата:

--liquibase formatted sql  

--changeset dev:7
CREATE INDEX idx_user_email ON users(email);

--changeset dev:8 context:prod
ALTER TABLE users ADD COLUMN last_login TIMESTAMP;


#Java #middle #Liquibase
Откаты (Rollback) и теги в Liquibase

Liquibase предоставляет мощные инструменты для отката изменений в базе данных, что критически важно при возникновении ошибок или необходимости вернуться к предыдущей версии схемы.

1. Типы откатов

1.1. Автоматический откат (на основе Liquibase)
Liquibase может автоматически генерировать команды отката для некоторых операций, если они указаны в changeSet.

Пример (YAML):
- changeSet:
id: 1
author: dev
changes:
- createTable:
tableName: users
columns:
- column:
name: id
type: INT
constraints:
primaryKey: true
- rollback:
dropTable:
tableName: users


Как это работает?
При выполнении liquibase update применяется createTable.
При откате (rollback) выполнится dropTable.

Какие операции поддерживают автоматический откат?
createTable → dropTable
addColumn → dropColumn
createIndex → dropIndex


Ограничения:
Не все операции имеют автоматический откат (например, сложные ALTER TABLE).
Для них требуется ручной rollback.


1.2. Ручной откат (пользовательские SQL-скрипты)
Если Liquibase не может сгенерировать откат автоматически, его можно задать вручную.

Пример (YAML):
- changeSet:
id: 2
author: dev
changes:
- sql:
sql: "ALTER TABLE users ADD COLUMN last_login TIMESTAMP"
- rollback:
sql:
sql: "ALTER TABLE users DROP COLUMN last_login"


Когда использовать?
Для сложных SQL-запросов.
Если
Liquibase не поддерживает автоматический откат для операции.

2. Теги (Tags)

Теги позволяют помечать определённое состояние базы данных, чтобы позже можно было к нему вернуться.

2.1. Создание тега (tag)
Пометка текущего состояния БД.

Команда:
liquibase tag v1.0
или через changelog:

yaml
- changeSet:
id: 3
author: dev
changes:
- tagDatabase:
tag: v1.0


Где хранится информация о тегах?
В таблице DATABASECHANGELOG (поле TAG).

2.2. Откат к тегу (rollback <tag>)
Возврат базы данных к состоянию на момент создания тега.

Команда:
liquibase rollback v1.0


Как это работает?
Liquibase находит тег v1.0 в DATABASECHANGELOG.
Определяет, какие changeSet’ы были выполнены после этого тега.
Применяет откаты в обратном порядке.


Пример (YAML):
- changeSet:
id: 4
author: dev
changes:
- tagDatabase:
tag: v1.1


3. Практические сценарии отката

3.1. Отмена последнего changeSet’а
liquibase rollbackCount 1


Что произойдёт?
Liquibase откатит один последний применённый changeSet.

3.2. Восстановление до определённой версии

Вариант 1: По тегу
liquibase rollback v1.0


Вариант 2: По дате
liquibase rollbackToDate 2024-03-15


Вариант 3: По количеству changeSet’ов
liquibase rollbackCount 3  # Откатит 3 последних изменения


#Java #middle #Liquibase
4. Важные нюансы

Откат не всегда возможен
Если changeSet не содержит rollback,
Liquibase не сможет его отменить автоматически.
(Пример: изменение данных (INSERT/UPDATE) требует ручного rollback.)

Теги vs. Версии
Теги — это произвольные метки (например, v1.0, prod-release).
Версии — это последовательные номера changeSet’ов.


Безопасность откатов
Всегда тестируйте rollback в dev-среде, прежде чем применять в production.
Используйте preConditions, чтобы избежать ошибок.


5. Пример полного сценария

1. Изначальный changelog:

databaseChangeLog:
- changeSet:
id: 1
author: dev
changes:
- createTable:
tableName: users
columns:
- column:
name: id
type: INT
constraints:
primaryKey: true
- rollback:
dropTable:
tableName: users

- changeSet:
id: 2
author: dev
changes:
- addColumn:
tableName: users
column:
name: username
type: VARCHAR(50)
- rollback:
dropColumn:
tableName: users
columnName: username

- changeSet:
id: 3
author: dev
changes:
- tagDatabase:
tag: v1.0


2. Применяем изменения:
liquibase update


3. Откатываемся до тега v1.0:
liquibase rollback v1.0


Результат:
Будет отменён changeSet id=2 (удалён столбец username).
Таблица users останется (так как changeSet id=1 был до тега).


#Java #middle #Liquibase
Интеграция Liquibase с другими инструментами

Maven / Gradle

Maven

Добавьте зависимость в pom.xml:
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>4.25.0</version>
</dependency>


Конфигурация через pom.xml:
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>4.25.0</version>
<configuration>
<changeLogFile>src/main/resources/db/changelog/db.changelog-master.yaml</changeLogFile>
<url>jdbc:postgresql://localhost:5432/mydb</url>
<username>user</username>
<password>pass</password>
</configuration>
</plugin>


Запуск миграций:
mvn liquibase:update


Gradle

Добавьте в build.gradle:
plugins {
id 'org.liquibase.gradle' version '2.2.0'
}

dependencies {
liquibaseRuntime 'org.liquibase:liquibase-core:4.25.0'
liquibaseRuntime 'org.postgresql:postgresql:42.6.0'
}

liquibase {
changeLogFile 'src/main/resources/db/changelog/db.changelog-master.yaml'
url 'jdbc:postgresql://localhost:5432/mydb'
username 'user'
password 'pass'
}


Запуск:
gradle liquibaseUpdate


Spring Boot (автоконфигурация)

Spring Boot автоматически настраивает Liquibase, если он обнаружен в classpath.

Настройки в application.yml:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: user
password: pass
liquibase:
change-log: classpath:db/changelog/db.changelog-master.yaml
enabled: true
contexts: dev # Фильтрация по контексту


Поведение:
Миграции запускаются при старте приложения.
Можно отключить через spring.
liquibase.enabled=false.

Расширенные функции Liquibase

Динамические свойства (property)

Позволяют выносить повторяющиеся значения в переменные.

Пример (YAML):
databaseChangeLog:
- property:
name: default.schema
value: public
- changeSet:
id: 1
author: dev
changes:
- createTable:
tableName: ${default.schema}.users
columns:
- column:
name: id
type: INT


В
liquibase.properties:
default.schema=public


Через командную строку:
liquibase update -Ddefault.schema=public


Контексты (contexts) и фильтрация changeSet’ов

Контексты позволяют выбирать, какие changeSet’ы применять в определённых окружениях.

Пример (YAML):
- changeSet:
id: 2
author: dev
context: "dev,test" # Будет выполнен только в dev и test
changes:
- insert:
tableName: users
columns:
- column:
name: username
value: "test_user"

- changeSet:
id: 3
author: dev
context: "prod" # Только в production
changes:
- createIndex:
tableName: users
indexName: idx_user_email
columns:
- column:
name: email


Как передать контекст?

Через командную строку:
liquibase update --contexts="dev"


В Spring Boot:
spring:
liquibase:
contexts: dev


#Java #middle #Liquibase
Best Practices

Организация changelog-файлов

Рекомендуемая структура:
src/main/resources/db/changelog/
├── db.changelog-master.yaml # Главный файл
├── changes/
│ ├── 001-create-tables.yaml
│ ├── 002-add-indexes.yaml
│ └── 003-insert-data.yaml
└── rollback/
├── 001-rollback.yaml # Ручные откаты


Главный файл (db.changelog-master.yaml):
databaseChangeLog:
- includeAll:
path: db/changelog/changes/
relativeToChangelogFile: true


Почему так?

Удобно управлять версиями.
Легко находить изменения.


Тестирование миграций

Стратегии:
Локальная проверка:
liquibase validate  # Проверка синтаксиса
liquibase update # Применение в test-DB


Интеграционные тесты (JUnit + Testcontainers):
@Testcontainers
@SpringBootTest
class LiquibaseMigrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");

@Test
void testMigrations() {
// Spring Boot автоматически применит миграции
assertTrue(true); # Если не упало — значит, успешно
}
}


Работа в команде (избежание конфликтов)


Правила:

Именование changeSet’ов:
Используйте id: <дата>-<номер> (например, id: 20240315-1).
Указывайте автора (author: github_username).


Порядок изменений:
Не зависеть от порядка выполнения (например, не предполагать, что таблица A уже существует при создании B).

Использование preConditions:
- changeSet:
id: 20240315-1
author: dev
preConditions:
- tableExists:
tableName: users
changes:
- addColumn:
tableName: users
column:
name: phone
type: VARCHAR(20)


Регулярные обновления:
Перед началом работы выполняйте
liquibase update, чтобы получить актуальную схему.

#Java #middle #Liquibase