Что такое Error в Java и чем он отличается от Exception? 🤓
Ответ:
Error и Exception — наследники Throwable.
Error обозначает серьезные системные ошибки, которые обычно нельзя обработать (например, OutOfMemoryError).
Exception — это ошибки, которые можно и нужно обрабатывать (например, IOException).
Программист редко обрабатывает Error, но должен учитывать Exception.
#собеседование
Ответ:
Error обозначает серьезные системные ошибки, которые обычно нельзя обработать (например, OutOfMemoryError).
Exception — это ошибки, которые можно и нужно обрабатывать (например, IOException).
Программист редко обрабатывает Error, но должен учитывать Exception.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
Best Practices и расширенные сценарии работы с Flyway
1. Организация миграций
1.1. Разделение миграций по окружениям
Проблема:
Разные БД для dev/test/prod могут требовать специфичных скриптов (например, тестовые данные только для dev).
Решение:
Структура папок:
Настройка application.yml для dev:
1.2. Работа с существующей БД (baseline)
Проблема:
Flyway ожидает пустую БД или таблицу flyway_schema_history. Если БД уже используется, требуется инициализация.
Решение:
Включить baseline-on-migrate:
Вручную через CLI:
Важно:
После baseline миграции с версией ≤ baselineVersion игнорируются.
2. Тестирование миграций
2.1. Интеграция с Testcontainers + JUnit
Цель:
Проверить, что миграции применяются без ошибок в изолированной БД.
Пример теста (Java + JUnit 5):
Что проверяем:
Применение всех миграций без исключений.
Соответствие схемы ожиданиям (можно добавить проверку через DataSource).
3. Работа в команде
3.1. Именование файлов
Правила:
Уникальность версий:
Используйте дату + порядковый номер: V20240315-1__Add_users_table.sql.
Описательные имена:
Плохо: V1__Changes.sql.
Хорошо: V20240315-2__Alter_users_add_email.sql.
3.2. Запрет изменения примененных скриптов
Проблема:
Изменение уже выполненного скрипта приводит к ошибке:
Решение:
Никогда не изменять содержимое V*-файлов после их применения.
Для исправлений:
Создать новую миграцию с корректировкой:
Если критично, использовать flyway repair (пересчитывает контрольные суммы).
Исключение:
R__-миграции (повторяемые) можно изменять — они переприменяются при изменениях.
4. Расширенные сценарии
4.1. Миграции для нескольких схем БД
Настройка:
4.2. Шаблонизация SQL
Проблема:
Разные SQL-синтаксисы для PostgreSQL/Oracle.
Решение (через Maven/Gradle):
Используйте placeholders в pom.xml:
В SQL:
4.3. Откаты (безопасная альтернатива)
Тактика:
Для деструктивных изменений (DROP, DELETE) создавать "обратные" миграции:
#Java #middle #Flyway
1. Организация миграций
1.1. Разделение миграций по окружениям
Проблема:
Разные БД для dev/test/prod могут требовать специфичных скриптов (например, тестовые данные только для dev).
Решение:
Структура папок:
db/
migration/
dev/
V1__Dev_only_data.sql
prod/
V1__Prod_indexes.sql
common/
V1__Base_schema.sql
Настройка application.yml для dev:
spring:
flyway:
locations: classpath:db/migration/common, classpath:db/migration/dev
1.2. Работа с существующей БД (baseline)
Проблема:
Flyway ожидает пустую БД или таблицу flyway_schema_history. Если БД уже используется, требуется инициализация.
Решение:
Включить baseline-on-migrate:
spring:
flyway:
baseline-on-migrate: true
baseline-version: 1.0 # Версия, с которой начнется контроль
Вручную через CLI:
flyway baseline -baselineVersion="1.0"
Важно:
После baseline миграции с версией ≤ baselineVersion игнорируются.
2. Тестирование миграций
2.1. Интеграция с Testcontainers + JUnit
Цель:
Проверить, что миграции применяются без ошибок в изолированной БД.
Пример теста (Java + JUnit 5):
import org.flywaydb.core.Flyway;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
@Testcontainers
public class FlywayMigrationTest {
@Container
private static final PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:15");
@Test
void testMigrations() {
Flyway flyway = Flyway.configure()
.dataSource(
postgres.getJdbcUrl(),
postgres.getUsername(),
postgres.getPassword()
)
.load();
flyway.migrate(); // Упадет, если есть ошибки
}
}
Что проверяем:
Применение всех миграций без исключений.
Соответствие схемы ожиданиям (можно добавить проверку через DataSource).
3. Работа в команде
3.1. Именование файлов
Правила:
Уникальность версий:
Используйте дату + порядковый номер: V20240315-1__Add_users_table.sql.
Описательные имена:
Плохо: V1__Changes.sql.
Хорошо: V20240315-2__Alter_users_add_email.sql.
3.2. Запрет изменения примененных скриптов
Проблема:
Изменение уже выполненного скрипта приводит к ошибке:
Validate failed: Migration checksum mismatch for V1__Create_table.sql
Решение:
Никогда не изменять содержимое V*-файлов после их применения.
Для исправлений:
Создать новую миграцию с корректировкой:
-- V20240315-3__Fix_users_table.sql
ALTER TABLE users ALTER COLUMN email SET NOT NULL;
Если критично, использовать flyway repair (пересчитывает контрольные суммы).
Исключение:
R__-миграции (повторяемые) можно изменять — они переприменяются при изменениях.
4. Расширенные сценарии
4.1. Миграции для нескольких схем БД
Настройка:
spring:
flyway:
schemas: public, audit
default-schema: public
В SQL-файлах укажите схему явно:
sql
-- V1__Create_audit_table.sql
CREATE TABLE audit.logs (
id SERIAL PRIMARY KEY,
message TEXT
);
4.2. Шаблонизация SQL
Проблема:
Разные SQL-синтаксисы для PostgreSQL/Oracle.
Решение (через Maven/Gradle):
Используйте placeholders в pom.xml:
<configuration>
<placeholders>
<table.prefix>${env}</table.prefix>
</placeholders>
</configuration>
В SQL:
CREATE TABLE ${table.prefix}_users (...);
4.3. Откаты (безопасная альтернатива)
Тактика:
Для деструктивных изменений (DROP, DELETE) создавать "обратные" миграции:
-- V5__Drop_users.sql
DROP TABLE users;
-- V6__Restore_users.sql (если нужно откатить)
CREATE TABLE users (...); -- Повторяем структуру из V1
INSERT INTO users (...) SELECT ... FROM backup_users;
Использовать резервные копии перед рискованными операциями.
#Java #middle #Flyway
Что выведет код?
#Tasks
import java.util.function.Supplier;
public class Task200625 {
public static void main(String[] args) {
Supplier<Integer> supplier = () -> {
System.out.print("A");
return 1;
};
System.out.print("B");
supplier.get();
System.out.print("C");
}
}
#Tasks
Продолжаем выбирать темы для разбора и голосовать за рассмотрение предложенных! 🤓
Голосуем за тему к рассмотрению в эти выходные!
Выбираем новую тему!
(можете предложить что-то из того, что предлагали на прошлой и позапрошлых неделях и что проиграло в голосовании!)
Не стесняемся!✌️
Голосуем за тему к рассмотрению в эти выходные!
Выбираем новую тему!
(можете предложить что-то из того, что предлагали на прошлой и позапрошлых неделях и что проиграло в голосовании!)
Не стесняемся!
Please open Telegram to view this post
VIEW IN TELEGRAM
Что такое Thread.sleep() и чем он отличается от Thread.yield()? 🤓
Ответ:
Thread.sleep(long millis) приостанавливает выполнение текущего потока на указанное время (в миллисекундах), не освобождая монитор. Может выбросить InterruptedException.
Thread.yield() дает возможность другим потокам с таким же приоритетом выполниться, но не гарантирует переключение.
Пример:
Thread.sleep(1000); // Пауза на 1 секунду
Thread.yield(); // Уступить процессор другим потокам
#собеседование
Ответ:
Thread.yield() дает возможность другим потокам с таким же приоритетом выполниться, но не гарантирует переключение.
Пример:
Thread.sleep(1000); // Пауза на 1 секунду
Thread.yield(); // Уступить процессор другим потокам
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
С 14.06 по 20.06
Предыдущий пост(с 07.06 по 13.06)
Воскресный мотивационный пост:
"Как я притворяюсь программистом"
Выбранная голосованием тема:
Введение в Apache Maven: автоматизация сборки и управления проектами
Запись встреч:
StreamAPI JAVA. Великолепие минимализма
Обучающие статьи:
Типы миграций и версионирование в Flyway
Интеграция Flyway с инструментами
Best Practices и расширенные сценарии работы с Flyway
Ссылочные типы в Java — интерфейсы (interfaces)
Ссылочные типы в Java — var
Полезные статьи и видео:
Виртуальные потоки Java: разбор тестовых сценариев
Почему Exceptions в Java не взлетели
Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
#memory
Предыдущий пост(с 07.06 по 13.06)
Воскресный мотивационный пост:
"Как я притворяюсь программистом"
Выбранная голосованием тема:
Введение в Apache Maven: автоматизация сборки и управления проектами
Запись встреч:
StreamAPI JAVA. Великолепие минимализма
Обучающие статьи:
Типы миграций и версионирование в Flyway
Интеграция Flyway с инструментами
Best Practices и расширенные сценарии работы с Flyway
Ссылочные типы в Java — интерфейсы (interfaces)
Ссылочные типы в Java — var
Полезные статьи и видео:
Виртуальные потоки Java: разбор тестовых сценариев
Почему Exceptions в Java не взлетели
Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
#memory
Ключевые методы Optional
Optional предоставляет множество методов для работы с данными:
isPresent(): Возвращает true, если значение присутствует.
isEmpty() (добавлен в Java 11): Возвращает true, если значение отсутствует.
ifPresent(Consumer<? super T> consumer): Выполняет действие, если значение присутствует.
orElse(T other): Возвращает значение, если оно присутствует, или other, если отсутствует.
orElseGet(Supplier<? extends T> supplier): Возвращает значение или результат выполнения Supplier, если значение отсутствует.
orElseThrow(Supplier<? extends X> exceptionSupplier): Возвращает значение или выбрасывает исключение, если значение отсутствует.
map(Function<? super T, ? extends U> mapper): Преобразует значение, если оно присутствует, возвращая новый Optional.
flatMap(Function<? super T, Optional<U>> mapper): Преобразует значение, возвращая Optional, результат которого не оборачивается в дополнительный Optional.
filter(Predicate<? super T> predicate): Возвращает Optional, содержащий значение, если оно удовлетворяет предикату, или пустой Optional.
or(Supplier<? extends Optional<? extends T>> supplier) (добавлен в Java 9): Возвращает текущий Optional, если значение присутствует, или результат Supplier.
Пример использования методов
Преимущества Optional
Безопасность от NPE: Optional явно указывает на возможность отсутствия значения, заставляя разработчика обрабатывать этот случай.
Читаемость кода: Код с Optional более выразителен, так как сигнализирует о потенциальном отсутствии значения.
Избежание явного null: Уменьшает использование проверок if (value != null), делая код чище.
Интеграция с функциональным программированием: Методы map, flatMap и filter позволяют использовать Optional в цепочках обработки данных, интегрируясь с Stream API.
Практическое использование Optional
Интеграция со Stream API
Optional часто используется в связке со Stream API для обработки данных, которые могут отсутствовать. Например:
Обработка вложенных объектов
Optional особенно полезен при работе с цепочками объектов, где каждое звено может быть null. Без Optional код выглядел бы так:
С использованием Optional код становится более компактным:
Метод flatMap полезен, если методы возвращают Optional:
Использование в API
Optional часто применяется в публичных API для возврата значений, которые могут отсутствовать. Например, метод Map.getOrDefault может быть заменен на Optional:
#Java #Training #Medium #Optional
Optional предоставляет множество методов для работы с данными:
isPresent(): Возвращает true, если значение присутствует.
isEmpty() (добавлен в Java 11): Возвращает true, если значение отсутствует.
ifPresent(Consumer<? super T> consumer): Выполняет действие, если значение присутствует.
orElse(T other): Возвращает значение, если оно присутствует, или other, если отсутствует.
orElseGet(Supplier<? extends T> supplier): Возвращает значение или результат выполнения Supplier, если значение отсутствует.
orElseThrow(Supplier<? extends X> exceptionSupplier): Возвращает значение или выбрасывает исключение, если значение отсутствует.
map(Function<? super T, ? extends U> mapper): Преобразует значение, если оно присутствует, возвращая новый Optional.
flatMap(Function<? super T, Optional<U>> mapper): Преобразует значение, возвращая Optional, результат которого не оборачивается в дополнительный Optional.
filter(Predicate<? super T> predicate): Возвращает Optional, содержащий значение, если оно удовлетворяет предикату, или пустой Optional.
or(Supplier<? extends Optional<? extends T>> supplier) (добавлен в Java 9): Возвращает текущий Optional, если значение присутствует, или результат Supplier.
Пример использования методов
Optional<String> name = Optional.ofNullable(getName()); // Может вернуть null
String result = name
.map(String::toUpperCase)
.filter(s -> s.length() > 3)
.orElse("DEFAULT");
System.out.println(result); // Выведет преобразованное значение или "DEFAULT"
Этот пример демонстрирует цепочку вызовов, характерную для функционального программирования, где Optional используется для обработки значения с возможностью отсутствия.
Преимущества Optional
Безопасность от NPE: Optional явно указывает на возможность отсутствия значения, заставляя разработчика обрабатывать этот случай.
Читаемость кода: Код с Optional более выразителен, так как сигнализирует о потенциальном отсутствии значения.
Избежание явного null: Уменьшает использование проверок if (value != null), делая код чище.
Интеграция с функциональным программированием: Методы map, flatMap и filter позволяют использовать Optional в цепочках обработки данных, интегрируясь с Stream API.
Практическое использование Optional
Интеграция со Stream API
Optional часто используется в связке со Stream API для обработки данных, которые могут отсутствовать. Например:
Optional<User> user = findUserById(123);
Stream<User> userStream = user.stream(); // Преобразует Optional в Stream
userStream.forEach(System.out::println);
Метод stream() (добавлен в Java 9) позволяет преобразовать Optional в Stream, содержащий либо одно значение, либо пустой.
Обработка вложенных объектов
Optional особенно полезен при работе с цепочками объектов, где каждое звено может быть null. Без Optional код выглядел бы так:
String city = null;
if (user != null && user.getAddress() != null && user.getAddress().getCity() != null) {
city = user.getAddress().getCity();
}
С использованием Optional код становится более компактным:
String city = Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("Unknown");
Метод flatMap полезен, если методы возвращают Optional:
Optional<String> city = Optional.ofNullable(user)
.flatMap(u -> Optional.ofNullable(u.getAddress()))
.flatMap(a -> Optional.ofNullable(a.getCity()));
Использование в API
Optional часто применяется в публичных API для возврата значений, которые могут отсутствовать. Например, метод Map.getOrDefault может быть заменен на Optional:
Map<String, String> map = new HashMap<>();
Optional<String> value = Optional.ofNullable(map.get("key"));
#Java #Training #Medium #Optional
Антипаттерны и ограничения
Использование Optional для полей класса: Optional не предназначен для хранения состояния в полях класса, так как он не сериализуем (Optional не реализует Serializable). Вместо этого используйте null для полей или коллекции.
Чрезмерное использование isPresent(): Проверки isPresent() с последующим вызовом get() сводят на нет преимущества Optional:
Вместо этого используйте orElse, orElseGet или map:
Передача Optional в методы: Передача Optional в качестве аргумента метода может усложнить API. Лучше передавать значение или null:
Производительность: Создание объектов Optional в циклах или в высокопроизводительных системах может привести к накладным расходам. В таких случаях рассмотрите использование null или оптимизируйте код.
Сериализация: Optional не реализует Serializable, что делает его непригодным для использования в сериализуемых классах. Если сериализация необходима, преобразуйте Optional в null или значение перед сохранением.
Потокобезопасность: Optional не является потокобезопасным, так как он не предназначен для конкурентного доступа. Если значение внутри Optional изменяется в многопоточной среде, используйте синхронизацию или потокобезопасные альтернативы.
Производительность и оптимизация
Создание объектов: Каждый вызов Optional.of или ofNullable создает новый объект (кроме empty(), который использует синглтон). В критически важных участках кода минимизируйте создание Optional.
Кэширование: Если Optional возвращается часто используемым методом, рассмотрите кэширование результата, чтобы избежать повторного создания объектов.
Ленивые вычисления: Используйте orElseGet вместо orElse, если альтернативное значение требует вычислений:
Рекомендации
- Используйте Optional только там, где это имеет смысл: Применяйте Optional для возвращаемых значений методов, где отсутствие значения — это ожидаемый сценарий. Не используйте его для всех случаев, чтобы избежать ненужной сложности.
- Интеграция с функциональными API: Максимально используйте методы map, flatMap и filter для обработки данных в функциональном стиле. Это улучшает читаемость и поддерживаемость кода.
- Проверка на обратную совместимость: Если вы разрабатываете публичное API, учитывайте, что клиенты на Java 7 и ниже не смогут использовать Optional. В таких случаях предоставляйте альтернативные методы, возвращающие null.
- Тестирование: Убедитесь, что тесты покрывают оба случая — присутствие и отсутствие значения в Optional. Это гарантирует корректную обработку всех сценариев.
- Рефакторинг старого кода: При рефакторинге кода, использующего null, заменяйте проверки на Optional, но только если это улучшает читаемость и безопасность. Не заменяйте null на Optional механически.
Пример реального сценария
Предположим, у нас есть сервис, который возвращает информацию о пользователе:
#Java #Training #Medium #Optional
Использование Optional для полей класса: Optional не предназначен для хранения состояния в полях класса, так как он не сериализуем (Optional не реализует Serializable). Вместо этого используйте null для полей или коллекции.
// Антипаттерн
public class User {
private Optional<String> name; // Не рекомендуется
}
Чрезмерное использование isPresent(): Проверки isPresent() с последующим вызовом get() сводят на нет преимущества Optional:
// Антипаттерн
if (optional.isPresent()) {
return optional.get();
}
Вместо этого используйте orElse, orElseGet или map:
return optional.orElse(defaultValue);
Передача Optional в методы: Передача Optional в качестве аргумента метода может усложнить API. Лучше передавать значение или null:
// Антипаттерн
void process(Optional<String> value) { ... }
// Лучше
void process(String value) { ... }
Производительность: Создание объектов Optional в циклах или в высокопроизводительных системах может привести к накладным расходам. В таких случаях рассмотрите использование null или оптимизируйте код.
Сериализация: Optional не реализует Serializable, что делает его непригодным для использования в сериализуемых классах. Если сериализация необходима, преобразуйте Optional в null или значение перед сохранением.
Потокобезопасность: Optional не является потокобезопасным, так как он не предназначен для конкурентного доступа. Если значение внутри Optional изменяется в многопоточной среде, используйте синхронизацию или потокобезопасные альтернативы.
Производительность и оптимизация
Создание объектов: Каждый вызов Optional.of или ofNullable создает новый объект (кроме empty(), который использует синглтон). В критически важных участках кода минимизируйте создание Optional.
Кэширование: Если Optional возвращается часто используемым методом, рассмотрите кэширование результата, чтобы избежать повторного создания объектов.
Ленивые вычисления: Используйте orElseGet вместо orElse, если альтернативное значение требует вычислений:
// Менее эффективно
String result = optional.orElse(computeExpensiveDefault());
// Более эффективно
String result = optional.orElseGet(() -> computeExpensiveDefault());
Рекомендации
- Используйте Optional только там, где это имеет смысл: Применяйте Optional для возвращаемых значений методов, где отсутствие значения — это ожидаемый сценарий. Не используйте его для всех случаев, чтобы избежать ненужной сложности.
- Интеграция с функциональными API: Максимально используйте методы map, flatMap и filter для обработки данных в функциональном стиле. Это улучшает читаемость и поддерживаемость кода.
- Проверка на обратную совместимость: Если вы разрабатываете публичное API, учитывайте, что клиенты на Java 7 и ниже не смогут использовать Optional. В таких случаях предоставляйте альтернативные методы, возвращающие null.
- Тестирование: Убедитесь, что тесты покрывают оба случая — присутствие и отсутствие значения в Optional. Это гарантирует корректную обработку всех сценариев.
- Рефакторинг старого кода: При рефакторинге кода, использующего null, заменяйте проверки на Optional, но только если это улучшает читаемость и безопасность. Не заменяйте null на Optional механически.
Пример реального сценария
Предположим, у нас есть сервис, который возвращает информацию о пользователе:
public class UserService {
public Optional<User> findUserById(long id) {
// Имитация поиска в базе данных
return id == 123 ? Optional.of(new User("Alice")) : Optional.empty();
}
public Optional<String> getUserCity(long id) {
return findUserById(id)
.flatMap(user -> Optional.ofNullable(user.getAddress()))
.flatMap(address -> Optional.ofNullable(address.getCity()));
}
}
Этот код демонстрирует, как Optional используется для безопасной обработки цепочки объектов, где каждое звено может быть отсутствующим.
#Java #Training #Medium #Optional
Есть предложение встретиться завтра в 16:00 по МСК на лайвкодинг!
Тема будет: MailSender - или как отправить email из Spring
Тема будет: MailSender - или как отправить email из Spring
Anonymous Poll
40%
Да, интересно, я приду!🥳
7%
Блиин, хочу но не могу 🤷♀️
47%
Посмотрю в записи 😋
7%
Не приду, фигню рассматриваете 👎