Объясните основные принципы ООП в Java? 🤓
Ответ:
Инкапсуляция: сокрытие данных (через private) и предоставление доступа через методы (геттеры/сеттеры).
Наследование: класс может наследовать свойства и методы другого класса (extends).
Полиморфизм: возможность использовать объекты разных классов через общий интерфейс или родительский класс.
Абстракция: сокрытие деталей реализации, предоставление только необходимого интерфейса (через абстрактные классы или интерфейсы).
#собеседование
Ответ:
Наследование: класс может наследовать свойства и методы другого класса (extends).
Полиморфизм: возможность использовать объекты разных классов через общий интерфейс или родительский класс.
Абстракция: сокрытие деталей реализации, предоставление только необходимого интерфейса (через абстрактные классы или интерфейсы).
#собеседование
Please open Telegram to view this post
VIEW IN 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 — строгая структура, но многословный.
YAML — лаконичный, но чувствителен к отступам.
SQL — прост для DBA, но менее гибкий.
5. Интеграция Liquibase в Java-проект (Maven/Gradle)
Maven
Добавить в pom.xml:
Конфигурация в application.properties (Spring Boot):
Gradle
Добавить в build.gradle:
#Java #middle #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
Сегодня каналу исполнился год! 🥳
Начинавшийся как попытка освоить телеграм-каналы и найти единомышленников в программировании на Java - сегодня наш канал, небольшое, но уникальное сообщество, в котором объединено стремление охватить все аспекты Java, с встречами и интересным общением!🤓
Вот немного статистики по каналу:
🔵 Сегодня нас в канале: 669 человек
🔵 Постов в канале: 2406 или 6.6 постов в день
🔵 Постов содержащих обучающую информацию: ~ 440
🔵 Постов с уникальными задачами: 257
🔵 Постов с IT-мемами: 415
🔵 Постов с IT-фактами: 89
🔵 Постов с IT-цитатами и биографиями: 88
🔵 Проведено встреч и опубликовано видео: 42
🔵 Подписчиков на YouTube: 220
🔵 Потрачено на рекламу/заработано: 0 рублей
Много это или мало - судить Вам))
Как бы то ни было, канал для меня сейчас - это прекрасная возможность не стагнировать, повторять пройденное и изучать новое, знакомиться и общаться с интересными людьми.
И в любом случае, я продолжу его развивать, наполнять информацией и стараться дать то, чего Вы все здесь ищете.
Спасибо всем, кто поддерживает канал, помогает с лайвкодингом, приходит на встречи.
Ценю🍸
С днем рождения нас❤️
Начинавшийся как попытка освоить телеграм-каналы и найти единомышленников в программировании на Java - сегодня наш канал, небольшое, но уникальное сообщество, в котором объединено стремление охватить все аспекты Java, с встречами и интересным общением!
Вот немного статистики по каналу:
Много это или мало - судить Вам))
Как бы то ни было, канал для меня сейчас - это прекрасная возможность не стагнировать, повторять пройденное и изучать новое, знакомиться и общаться с интересными людьми.
И в любом случае, я продолжу его развивать, наполнять информацией и стараться дать то, чего Вы все здесь ищете.
Спасибо всем, кто поддерживает канал, помогает с лайвкодингом, приходит на встречи.
Ценю
С днем рождения нас
Please open Telegram to view this post
VIEW IN TELEGRAM
Что произойдет при выполнении следующего кода, если соединение с БД установлено успешно?
#Tasks
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class Task270525 {
public static void main(String[] args) {
try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/test", "user", "pass");
Statement stmt = conn.createStatement()) {
conn.setAutoCommit(false);
stmt.executeUpdate("INSERT INTO users VALUES (1, 'John')");
stmt.executeUpdate("INSERT INTO users VALUES (1, 'Mike')");
conn.commit();
} catch (
SQLException e) {
System.out.println("Error occurred");
}
}
}
#Tasks
Что такое перегрузка и переопределение методов? 🤓
Ответ:
Перегрузка (overloading): методы в одном классе с одинаковым именем, но разными параметрами (по количеству или типу). Происходит на этапе компиляции.
Переопределение (overriding): подкласс переопределяет метод родительского класса с той же сигнатурой. Происходит на этапе выполнения.
#собеседование
Ответ:
Переопределение (overriding): подкласс переопределяет метод родительского класса с той же сигнатурой. Происходит на этапе выполнения.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
Глубокое изучение типа float в Java: сравнение с double и целочисленными типами
Тип float — один из двух примитивных типов с плавающей точкой в Java. Он используется для хранения чисел с десятичной частью и обеспечивает определённый баланс между точностью и потреблением памяти. Несмотря на свою "простоту", float имеет множество нюансов, особенно в сравнении с double и целочисленными типами (int, long и т. д.), и может вести себя неожиданно, если не понимать его природу.
Что такое float в Java
float — это 32-битный (4 байта) тип данных, реализующий стандарт IEEE 754 для представления чисел с плавающей точкой.
Это означает, что число хранится в следующем формате:
1 бит — знак числа
8 бит — экспонента
23 бита — мантисса (дробная часть)
Таким образом, float может хранить числа приблизительно в диапазоне от ±1.4 × 10^-45 до ±3.4 × 10^38 с точностью около 6–7 значащих десятичных цифр.
Чтобы обозначить литерал как float, нужно явно указать f или F:
Сравнение с double
double — это 64-битный тип, также реализующий IEEE 754, но имеющий:
1 бит для знака
11 бит для экспоненты
52 бита для мантиссы
Он способен хранить числа от ±4.9 × 10^-324 до ±1.7 × 10^308, с точностью около 15–16 значащих цифр.
То есть:
float — быстрее, но менее точен, занимает меньше памяти
double — точнее, но требует больше памяти и может быть чуть медленнее в вычислениях на некоторых архитектурах
В реальной практике предпочтение обычно отдают double, особенно в финансовых, статистических или инженерных вычислениях, где важна точность. float чаще применяется в графике (например, координаты вершин), машинном обучении, играх и устройствах с ограниченными ресурсами (встраиваемые системы, Android до определённых API-уровней).
Сравнение с целочисленными типами
Целочисленные типы (byte, short, int, long) хранят точные значения и не допускают погрешностей. Они идеальны для подсчётов, индексов, флагов, битовых масок и всего, что не связано с дробями.
В отличие от них, float и double — не точные типы.
Это означает:
Результаты вычислений могут быть неточными из-за ограниченной точности представления дробных чисел.
Сравнение значений на равенство (==) — рискованно и почти всегда плохая идея.
Простые на вид операции могут давать неожиданный результат:
Работа с памятью и производительность
Обе переменные — float и double — примитивные типы и, следовательно, при размещении в стеке (например, внутри метода) не требуют участия сборщика мусора. Они быстро выделяются и удаляются вместе с фреймом стека. Однако, если переменные — поля объекта, то они хранятся в куче, и их "жизненный цикл" зависит от объекта.
С точки зрения производительности:
На современных процессорах разница между float и double минимальна.
Некоторые GPU и встраиваемые процессоры всё ещё используют float как основной тип с плавающей точкой.
На JVM оба типа оптимизируются, но float может быть чуть быстрее при большом объеме операций и памяти.
#Java #для_новичков #beginner #float
Тип float — один из двух примитивных типов с плавающей точкой в Java. Он используется для хранения чисел с десятичной частью и обеспечивает определённый баланс между точностью и потреблением памяти. Несмотря на свою "простоту", float имеет множество нюансов, особенно в сравнении с double и целочисленными типами (int, long и т. д.), и может вести себя неожиданно, если не понимать его природу.
Что такое float в Java
float — это 32-битный (4 байта) тип данных, реализующий стандарт IEEE 754 для представления чисел с плавающей точкой.
Это означает, что число хранится в следующем формате:
1 бит — знак числа
8 бит — экспонента
23 бита — мантисса (дробная часть)
Таким образом, float может хранить числа приблизительно в диапазоне от ±1.4 × 10^-45 до ±3.4 × 10^38 с точностью около 6–7 значащих десятичных цифр.
Чтобы обозначить литерал как float, нужно явно указать f или F:
float pi = 3.1415927f;
Без этого литерал будет воспринят как double по умолчанию, что приведет к ошибке компиляции при попытке неявного присваивания.
Сравнение с double
double — это 64-битный тип, также реализующий IEEE 754, но имеющий:
1 бит для знака
11 бит для экспоненты
52 бита для мантиссы
Он способен хранить числа от ±4.9 × 10^-324 до ±1.7 × 10^308, с точностью около 15–16 значащих цифр.
То есть:
float — быстрее, но менее точен, занимает меньше памяти
double — точнее, но требует больше памяти и может быть чуть медленнее в вычислениях на некоторых архитектурах
В реальной практике предпочтение обычно отдают double, особенно в финансовых, статистических или инженерных вычислениях, где важна точность. float чаще применяется в графике (например, координаты вершин), машинном обучении, играх и устройствах с ограниченными ресурсами (встраиваемые системы, Android до определённых API-уровней).
Сравнение с целочисленными типами
Целочисленные типы (byte, short, int, long) хранят точные значения и не допускают погрешностей. Они идеальны для подсчётов, индексов, флагов, битовых масок и всего, что не связано с дробями.
В отличие от них, float и double — не точные типы.
Это означает:
Результаты вычислений могут быть неточными из-за ограниченной точности представления дробных чисел.
Сравнение значений на равенство (==) — рискованно и почти всегда плохая идея.
Простые на вид операции могут давать неожиданный результат:
float a = 0.1f + 0.2f;
System.out.println(a == 0.3f); // false
Это связано с тем, что не все десятичные дроби можно точно представить в двоичной системе.
Работа с памятью и производительность
Обе переменные — float и double — примитивные типы и, следовательно, при размещении в стеке (например, внутри метода) не требуют участия сборщика мусора. Они быстро выделяются и удаляются вместе с фреймом стека. Однако, если переменные — поля объекта, то они хранятся в куче, и их "жизненный цикл" зависит от объекта.
С точки зрения производительности:
На современных процессорах разница между float и double минимальна.
Некоторые GPU и встраиваемые процессоры всё ещё используют float как основной тип с плавающей точкой.
На JVM оба типа оптимизируются, но float может быть чуть быстрее при большом объеме операций и памяти.
#Java #для_новичков #beginner #float
Особенности и подводные камни
Погрешность и потеря точности
Каждое присваивание или операция с float может сопровождаться потерей точности. Например:
Нормализованные и денормализованные числа
float поддерживает очень маленькие значения, но при этом точность сильно страдает. Денормализованные значения позволяют представлять числа ближе к нулю, но с меньшей точностью.
NaN, Infinity и -Infinity
float поддерживает специальные значения:
Float.NaN — результат недопустимых операций (например, 0.0f / 0.0f)
Float.POSITIVE_INFINITY и Float.NEGATIVE_INFINITY — результат переполнения или деления на 0
Эти значения не вызывают исключений, и с ними можно работать, но это требует осторожности.
Сравнение на равенство
Из-за округлений не следует использовать == для сравнения двух float.
Вместо этого используют допустимую погрешность:
Приведение типов
При смешанных операциях с float и целочисленными типами Java автоматически приводит меньший тип к float.
Например:
Двоичное представление и неожиданное округление
Некоторые десятичные дроби (например, 0.1, 0.2) не могут быть точно представлены в двоичной системе. Это приводит к накапливающимся погрешностям, особенно при работе с циклами или большими массивами данных.
Когда использовать float, а когда — double
Используй float, если:
Работаешь в среде с ограниченной памятью или производительностью (например, Android, микроконтроллеры)
Требуется снизить объем данных (например, передача координат в 3D-движке)
Максимальная точность не критична
Используй double, если:
Точность важна (финансовые расчеты, физические симуляции)
Объёмы данных позволяют использовать больше памяти
Не хочешь постоянно контролировать потерю точности
#Java #для_новичков #beginner #float
Погрешность и потеря точности
Каждое присваивание или операция с float может сопровождаться потерей точности. Например:
float a = 1_000_000;
float b = a + 0.0001f;
System.out.println(a == b); // true — потерялась дробная часть
Нормализованные и денормализованные числа
float поддерживает очень маленькие значения, но при этом точность сильно страдает. Денормализованные значения позволяют представлять числа ближе к нулю, но с меньшей точностью.
NaN, Infinity и -Infinity
float поддерживает специальные значения:
Float.NaN — результат недопустимых операций (например, 0.0f / 0.0f)
Float.POSITIVE_INFINITY и Float.NEGATIVE_INFINITY — результат переполнения или деления на 0
Эти значения не вызывают исключений, и с ними можно работать, но это требует осторожности.
Сравнение на равенство
Из-за округлений не следует использовать == для сравнения двух float.
Вместо этого используют допустимую погрешность:
float a = 0.1f + 0.2f;
float b = 0.3f;
if (Math.abs(a - b) < 1e-6) {
System.out.println("Равны с учетом погрешности");
}
Приведение типов
При смешанных операциях с float и целочисленными типами Java автоматически приводит меньший тип к float.
Например:
int x = 3;
float y = 2.5f;
float result = x + y; // x преобразован в float
Это не вызывает проблем, но может повлиять на точность, если целое число очень большое.
Двоичное представление и неожиданное округление
Некоторые десятичные дроби (например, 0.1, 0.2) не могут быть точно представлены в двоичной системе. Это приводит к накапливающимся погрешностям, особенно при работе с циклами или большими массивами данных.
Когда использовать float, а когда — double
Используй float, если:
Работаешь в среде с ограниченной памятью или производительностью (например, Android, микроконтроллеры)
Требуется снизить объем данных (например, передача координат в 3D-движке)
Максимальная точность не критична
Используй double, если:
Точность важна (финансовые расчеты, физические симуляции)
Объёмы данных позволяют использовать больше памяти
Не хочешь постоянно контролировать потерю точности
#Java #для_новичков #beginner #float
Продолжаем выбирать темы для разбора и голосовать за рассмотрение предложенных! 🤓
Голосуем за тему к рассмотрению в эти выходные!
Выбираем новую тему!
(можете предложить что-то из того что предлагали на прошлой неделе и что проигрывает в голосовании!)
Не стесняемся!✌️
Голосуем за тему к рассмотрению в эти выходные!
Выбираем новую тему!
(можете предложить что-то из того что предлагали на прошлой неделе и что проигрывает в голосовании!)
Не стесняемся!
Please open Telegram to view this post
VIEW IN TELEGRAM
Что выведет код?
#Tasks
public class Task280525 {
public static void main(String[] args) {
float f = 16777216f;
System.out.println(f == (f + 1f));
}
}
#Tasks
Совсем недавно стал свидетелем неочевидной проблемы, когда вроде бы полностью протестированный стабильный сервис, по непонятным причинам падает на проде с ошибкой OutOfMemory.
Причинами и способами решения, сегодня я решил поделиться с Вами.
Вводные данные:
Предложенное решение (как показала практика - неверное):
Примерный код:
🧍 User
@Entity
public class User {
//стандартные поля
@OneToMany(mappedBy = "owner", fetch = FetchType.LAZY)
private List<Car> cars = new ArrayList<>();
}
🚗 Car
@Entity
public class Car {
//стандартные поля
@ManyToOne
@JoinColumn(name = "owner_id")
private User owner;
}
Пагинация + fetch join через Specification (без фильтров)
Page<User> users = userRepository.findAll(specification, PageRequest.of(0, 10));
настройка Specification
return (root, query, cb) -> {
root.fetch("cars", JoinType.LEFT);
query.distinct(true);
return cb.conjunction();
};
Проблема:
Вот примеры:
Пример 1
Пример 2
Однако в сочетании с пагинацией (Pageable) Hibernate теряет корректность подсчёта количества строк и может загрузить всю таблицу в память, чтобы затем вручную "отрезать" нужную страницу на уровне Java, тем самым использовав ВСЮ выделенную JVM память для хранения.
О последствиях такого непредсказуемого поведения можете посудить сами.
Что происходит, подробно?
Когда вы вызываете, например:
Page<User> users = userRepository.findAll(specification, PageRequest.of(0, 10));
Hibernate должен выполнить:
SELECT COUNT(*) ... — чтобы узнать общее количество строк.
SELECT ... LIMIT 10 OFFSET 0 — чтобы получить только первую страницу.
Когда вы пишете JOIN FETCH, например:
root.fetch("cars", JoinType.LEFT);
Или:
criteriaQuery.distinct(true);
Hibernate генерирует SQL примерно такого вида:
SELECT u.*, r.* FROM users u
LEFT JOIN roles r ON r.user_id = u.id
При этом:
Если у одного пользователя 3 cars, то он появится 3 раза в результате SQL-запроса.
Hibernate потом вручную собирает дубликаты в одну сущность User, у которой будет List<Car> с 3 элементами.
LIMIT/OFFSET применяются к строкам SQL, а не к "собранным" сущностям — и это вызывает проблемы.
⚠️ Проблема: LIMIT работает до агрегации
Hibernate не может корректно объединить дубликаты после применения LIMIT, потому что:
При использовании fetch join, результат SQL-разворачивается в несколько строк (по связям).
Но LIMIT обрезает эти строки до того, как Hibernate агрегирует их в объекты Java.
Поэтому Hibernate игнорирует LIMIT в SQL, чтобы корректно собрать сущности → он загружает все строки в память, затем отрезает нужную страницу на уровне Java.
А завтра я расскажу как решить данный кейс и как не попасть в подобную ловушку неочевидного поведения Hibernate ...
Понравился стиль подачи материала?
Отправь другу и ставь - 🔥
#Java #fetch #autor
Please open Telegram to view this post
VIEW IN TELEGRAM
В чем разница между ArrayList и LinkedList? 🤓
Ответ:
ArrayList использует динамический массив, быстрый доступ по индексу (O(1)), но медленные вставки/удаления в середине (O(n)).
LinkedList использует двухсвязный список, быстрые вставки/удаления (O(1)), но медленный доступ по индексу (O(n)).
#собеседование
Ответ:
LinkedList использует двухсвязный список, быстрые вставки/удаления (O(1)), но медленный доступ по индексу (O(n)).
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
Структура и основные команды Liquibase
1. Файл конфигурации (liquibase.properties)
Файл liquibase.properties содержит настройки для подключения к БД и управления Liquibase.
Основные параметры:
Разбор параметров:
url – JDBC-URL базы данных (зависит от СУБД).
username и password – учетные данные для подключения.
driver – класс JDBC-драйвера (например, org.postgresql.Driver для PostgreSQL).
changeLogFile – путь к главному файлу changelog.
Где размещается?
В корне проекта (рядом с pom.xml/build.gradle).
Или указывается явно при запуске:
2. Основные команды CLI
Liquibase предоставляет консольные команды для управления миграциями.
update – применение изменений
Применяет все невыполненные changeSet’ы из changelog.
Что происходит?
Liquibase проверяет таблицу DATABASECHANGELOG.
Находит changeSet’ы, которых нет в этой таблице.
Применяет их в порядке указания в changelog.
rollback – откат изменений
Возвращает БД к предыдущему состоянию.
status – проверка состояния БД
Показывает, какие changeSet’ы не применены.
Вывод:
validate – проверка корректности changelog
Проверяет синтаксис changelog без применения изменений.
Если есть ошибки, выведет сообщение, например:
3. Жизненный цикл изменений
Как Liquibase применяет changeSet’ы?
Парсинг changelog:
Liquibase читает главный файл (например, db.changelog-master.xml).
Загружает все вложенные changeSet’ы.
Проверка DATABASECHANGELOG:
Сравнивает список changeSet’ов с теми, что уже выполнены (хранятся в таблице DATABASECHANGELOG).
Применение изменений:
Невыполненные changeSet’ы применяются в порядке их объявления.
После успешного выполнения информация о changeSet’е записывается в DATABASECHANGELOG.
Контроль версий и порядок выполнения
Уникальность changeSet’а определяется по:
id (например, "1").
author (например, "alex").
Путь к файлу changelog.
Порядок выполнения:
Liquibase выполнит 001-create-users.xml раньше, чем 002-add-email.xml.
Атрибуты, влияющие на выполнение:
runOnChange="true" – повторно выполнит changeSet, если его содержимое изменилось.
failOnError="false" – пропустит ошибку (например, если таблица уже существует).
#Java #middle #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
Что выведет код?
#Tasks
public class Task290525 {
public static void main(String[] args) {
System.out.println(1.0 / 0.0);
}
}
#Tasks