@DynamicUpdate в Hibernate
Аннотация @DynamicUpdate (из пакета org.hibernate.annotations) указывает Hibernate генерировать SQL-запросы UPDATE только для изменившихся полей сущности, а не для всех полей. Это улучшает производительность, особенно для таблиц с большим количеством столбцов.
Применяется на уровне класса сущности:
Как работает
Если @DynamicUpdate = true (или аннотация присутствует):
Hibernate отслеживает изменения в полях сущности.
В SQL-запрос UPDATE включаются только измененные поля.
Если @DynamicUpdate = false (по умолчанию):
В UPDATE попадают все поля, даже если их значения не изменились.
Примеры SQL
Без @DynamicUpdate
Сгенерированный SQL (все поля, даже неизмененные):
С @DynamicUpdate
Тот же код, но SQL будет короче:
Жизненный цикл и обработка
При загрузке сущности
Hibernate сохраняет исходные значения полей (снимок состояния).
При изменении полей
Сравниваются текущие значения с исходными.
При выполнении session.update() или transaction.commit()
Формируется SQL только для измененных полей.
Влияние на кеширование
Кеш второго уровня обновляется только фактически измененными данными.
Настройки и интеграция
В hibernate.cfg.xml или application.properties:
Но рекомендуется использовать аннотацию для точного контроля.
Совместимость с другими аннотациями
@DynamicInsert – аналогичная оптимизация для INSERT.
@SelectBeforeUpdate – если true, Hibernate сначала загружает текущее состояние, что увеличивает точность @DynamicUpdate.
Когда использовать
Плюсы
Уменьшает объем SQL-запросов.
Ускоряет UPDATE для "широких" таблиц.
Снижает нагрузку на БД.
Минусы
Небольшой оверхед на отслеживание изменений.
Может усложнить отладку (меняется SQL).
Оптимальные сценарии
Таблицы с 20+ столбцами, где обновляются 1-2 поля.
Частые обновления части полей (например, счетчики, статусы).
Системы с высокой нагрузкой на БД.
#Java #Training #Hard #Spring #Hibernate #DynamicUpdate
Аннотация @DynamicUpdate (из пакета org.hibernate.annotations) указывает Hibernate генерировать SQL-запросы UPDATE только для изменившихся полей сущности, а не для всех полей. Это улучшает производительность, особенно для таблиц с большим количеством столбцов.
Применяется на уровне класса сущности:
@Entity
@DynamicUpdate
public class User { ... }
Как работает
Если @DynamicUpdate = true (или аннотация присутствует):
Hibernate отслеживает изменения в полях сущности.
В SQL-запрос UPDATE включаются только измененные поля.
Если @DynamicUpdate = false (по умолчанию):
В UPDATE попадают все поля, даже если их значения не изменились.
Примеры SQL
Без @DynamicUpdate
User user = session.get(User.class, 1L);
user.setName("Alice"); // Меняем только имя
Сгенерированный SQL (все поля, даже неизмененные):
UPDATE user SET name='Alice', age=30, email='alice@example.com' WHERE id=1;
С @DynamicUpdate
@DynamicUpdate
@Entity
public class User { ... }
Тот же код, но SQL будет короче:
UPDATE user SET name='Alice' WHERE id=1; -- Только измененное поле!
Жизненный цикл и обработка
При загрузке сущности
Hibernate сохраняет исходные значения полей (снимок состояния).
При изменении полей
Сравниваются текущие значения с исходными.
При выполнении session.update() или transaction.commit()
Формируется SQL только для измененных полей.
Влияние на кеширование
Кеш второго уровня обновляется только фактически измененными данными.
Настройки и интеграция
В hibernate.cfg.xml или application.properties:
hibernate.dynamic_update=true # Применяется ко всем сущностям
Но рекомендуется использовать аннотацию для точного контроля.
Совместимость с другими аннотациями
@DynamicInsert – аналогичная оптимизация для INSERT.
@SelectBeforeUpdate – если true, Hibernate сначала загружает текущее состояние, что увеличивает точность @DynamicUpdate.
Когда использовать
Плюсы
Уменьшает объем SQL-запросов.
Ускоряет UPDATE для "широких" таблиц.
Снижает нагрузку на БД.
Минусы
Небольшой оверхед на отслеживание изменений.
Может усложнить отладку (меняется SQL).
Оптимальные сценарии
Таблицы с 20+ столбцами, где обновляются 1-2 поля.
Частые обновления части полей (например, счетчики, статусы).
Системы с высокой нагрузкой на БД.
#Java #Training #Hard #Spring #Hibernate #DynamicUpdate
Что выведет код?
#Tasks
import java.util.HashSet;
import java.util.Set;
public class Task090425 {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("A");
set.add(new String("A"));
set.add("B");
set.remove(new String("A"));
System.out.println(set.size());
}
}
#Tasks
Please open Telegram to view this post
VIEW IN TELEGRAM
Вопросы с собеседования 👩💻
Какой метод возвращает квадратный корень числа?
Какой метод возвращает квадратный корень числа?
Anonymous Quiz
8%
Math.power()
14%
Math.square()
74%
Math.sqrt()
4%
Math.root()
@Filter в Hibernate
Аннотация @Filter позволяет динамически применять условия фильтрации к сущностям или коллекциям на уровне базы данных. Она используется для ограничения выборки данных в зависимости от заданных параметров. Находится в пакете org.hibernate.annotations.
Параметры и настройки
Аннотация @Filter имеет следующие атрибуты:
name (обязательный, String):
Уникальное имя фильтра, которое используется для его активации/деактивации в сессии.
Пример: @Filter(name = "activeUserFilter").
condition (необязательный, String):
SQL-условие, которое добавляется в запрос при активации фильтра.
Может содержать параметры (например, :param), которые задаются во время выполнения.
Пример: @Filter(name = "activeUserFilter", condition = "is_active = :activeStatus").
deduceAliasInjectionPoints (необязательный, boolean, default = true):
Определяет, должен ли Hibernate автоматически подставлять алиасы таблиц в условие.
Если false, условие должно содержать явные алиасы.
Жизненный цикл фильтра
Объявление фильтра:
Фильтр определяется на уровне класса сущности (@Entity) или коллекции (@ElementCollection, @OneToMany и др.).
Пример:
Активация фильтра в сессии:
Фильтр не активен по умолчанию, его нужно включать явно в Session или EntityManager.
Пример:
Применение в запросах:
После активации фильтр автоматически добавляет условие ко всем запросам, связанным с сущностью.
Для @OneToMany и других коллекций фильтр применяется при загрузке ассоциаций.
Деактивация фильтра:
Фильтр можно отключить:
Механизмы Hibernate и Spring, связанные с @Filter
1. @FilterDef и @ParamDef
@FilterDef определяет фильтр и его параметры.
@ParamDef задает тип параметра (например, String, boolean).
Должен быть указан на уровне класса (обычно на @Entity или в package-info.java).
2. Интеграция с Spring Data JPA
Фильтры можно активировать в Spring-репозиториях через EntityManager:
3. Глобальная настройка фильтров в Spring Boot
Можно автоматически включать фильтры для всех запросов через @PostConstruct:
Варианты настройки и применения
1. Фильтрация коллекций
2. Множественные фильтры
3. Динамическое управление фильтрами
#Java #Training #Hard #Spring #Hibernate #Filter #FilterDef
Аннотация @Filter позволяет динамически применять условия фильтрации к сущностям или коллекциям на уровне базы данных. Она используется для ограничения выборки данных в зависимости от заданных параметров. Находится в пакете org.hibernate.annotations.
Параметры и настройки
Аннотация @Filter имеет следующие атрибуты:
name (обязательный, String):
Уникальное имя фильтра, которое используется для его активации/деактивации в сессии.
Пример: @Filter(name = "activeUserFilter").
condition (необязательный, String):
SQL-условие, которое добавляется в запрос при активации фильтра.
Может содержать параметры (например, :param), которые задаются во время выполнения.
Пример: @Filter(name = "activeUserFilter", condition = "is_active = :activeStatus").
deduceAliasInjectionPoints (необязательный, boolean, default = true):
Определяет, должен ли Hibernate автоматически подставлять алиасы таблиц в условие.
Если false, условие должно содержать явные алиасы.
Жизненный цикл фильтра
Объявление фильтра:
Фильтр определяется на уровне класса сущности (@Entity) или коллекции (@ElementCollection, @OneToMany и др.).
Пример:
@Entity
@FilterDef(name = "activeUserFilter",
condition = "is_active = :activeStatus",
parameters = @ParamDef(name = "activeStatus", type = boolean.class))
@Filter(name = "activeUserFilter")
public class User { ... }
Активация фильтра в сессии:
Фильтр не активен по умолчанию, его нужно включать явно в Session или EntityManager.
Пример:
session.enableFilter("activeUserFilter")
.setParameter("activeStatus", true);
Применение в запросах:
После активации фильтр автоматически добавляет условие ко всем запросам, связанным с сущностью.
Для @OneToMany и других коллекций фильтр применяется при загрузке ассоциаций.
Деактивация фильтра:
Фильтр можно отключить:
session.disableFilter("activeUserFilter");
Механизмы Hibernate и Spring, связанные с @Filter
1. @FilterDef и @ParamDef
@FilterDef определяет фильтр и его параметры.
@ParamDef задает тип параметра (например, String, boolean).
Должен быть указан на уровне класса (обычно на @Entity или в package-info.java).
2. Интеграция с Spring Data JPA
Фильтры можно активировать в Spring-репозиториях через EntityManager:
@Repository
public class UserRepository {
@PersistenceContext
private EntityManager em;
public List<User> findActiveUsers() {
em.unwrap(Session.class)
.enableFilter("activeUserFilter")
.setParameter("activeStatus", true);
return em.createQuery("FROM User", User.class).getResultList();
}
}
3. Глобальная настройка фильтров в Spring Boot
Можно автоматически включать фильтры для всех запросов через @PostConstruct:
@Component
public class HibernateFilterConfig {
@PersistenceContext
private EntityManager em;
@PostConstruct
public void init() {
Session session = em.unwrap(Session.class);
session.enableFilter("activeUserFilter")
.setParameter("activeStatus", true);
}
}
Варианты настройки и применения
1. Фильтрация коллекций
@Entity
public class Department {
@OneToMany(mappedBy = "department")
@Filter(name = "activeUserFilter", condition = "is_active = :activeStatus")
private List<User> users;
}
2. Множественные фильтры
@Entity
@FilterDef(name = "activeFilter", condition = "is_active = :active")
@FilterDef(name = "roleFilter", condition = "role = :roleName")
@Filter(name = "activeFilter")
@Filter(name = "roleFilter")
public class User { ... }
3. Динамическое управление фильтрами
// Включение разных параметров в runtime
session.enableFilter("activeUserFilter")
.setParameter("activeStatus", true);
session.enableFilter("roleFilter")
.setParameter("roleName", "ADMIN");
#Java #Training #Hard #Spring #Hibernate #Filter #FilterDef
@Formula в Hibernate
Аннотация @Formula позволяет задавать вычисляемые (производные) поля в сущностях Hibernate, значения которых не хранятся в базе данных, а вычисляются на основе SQL-выражения. Находится в пакете org.hibernate.annotations.
Атрибуты аннотации
value (String, обязательный):
SQL-выражение, которое выполняется при загрузке сущности из БД.
Может включать столбцы таблицы, агрегатные функции (SUM, AVG), подзапросы и даже вызовы SQL-функций.
Пример:
Жизненный цикл
Загрузка сущности:
При выборке сущности из БД (EntityManager.find(), HQL, Criteria API) Hibernate выполняет SQL из @Formula и подставляет результат в поле.
Вычисление происходит на стороне БД, а не в Java-коде.
Обновление сущности:
Поле, помеченное @Formula, не сохраняется в БД при INSERT/UPDATE.
Если SQL-выражение зависит от других полей, его значение пересчитывается при каждой загрузке.
Кэширование:
Если сущность кэшируется (например, через @Cacheable), значение @Formula также кэшируется.
Обработка @Formula
Во время компиляции Hibernate:
Hibernate парсит SQL-выражение и проверяет его синтаксис (но не валидирует до выполнения запроса).
Если выражение содержит ссылки на другие таблицы, Hibernate не создает JOIN автоматически – нужно явно указывать подзапросы.
В рантайме:
При генерации SQL для загрузки сущности Hibernate подставляет формулу как есть (без параметризации).
Зависимость от диалекта БД
Поскольку @Formula использует "сырой" SQL, выражение должно быть совместимо с диалектом Hibernate (MySQLDialect, PostgreSQLDialect и т. д.).
Пример для PostgreSQL:
Варианты использования и ограничения
1. Динамические вычисления
Расчет рейтинга на основе связанных данных:
2. Использование SQL-функций
Форматирование даты:
3. Ограничения
Нет поддержки JPA-стандарта: @Formula – это аннотация Hibernate, а не JPA.
Нет параметризации: SQL в @Formula статичен, нельзя передавать параметры.
Производительность: Сложные подзапросы могут замедлять загрузку сущностей.
Альтернативы
@PostLoad + вычисление в Java:
Плюс: независимость от SQL. Минус: вычисление в Java, а не в БД.
Представления (Database Views) + @Immutable:
Создать VIEW в БД и отобразить его как неизменяемую сущность.
#Java #Training #Hard #Spring #Hibernate #Formula
Аннотация @Formula позволяет задавать вычисляемые (производные) поля в сущностях Hibernate, значения которых не хранятся в базе данных, а вычисляются на основе SQL-выражения. Находится в пакете org.hibernate.annotations.
Атрибуты аннотации
value (String, обязательный):
SQL-выражение, которое выполняется при загрузке сущности из БД.
Может включать столбцы таблицы, агрегатные функции (SUM, AVG), подзапросы и даже вызовы SQL-функций.
Пример:
@Formula("(SELECT COUNT(*) FROM orders o WHERE o.customer_id = id)")
private int orderCount;
Жизненный цикл
Загрузка сущности:
При выборке сущности из БД (EntityManager.find(), HQL, Criteria API) Hibernate выполняет SQL из @Formula и подставляет результат в поле.
Вычисление происходит на стороне БД, а не в Java-коде.
Обновление сущности:
Поле, помеченное @Formula, не сохраняется в БД при INSERT/UPDATE.
Если SQL-выражение зависит от других полей, его значение пересчитывается при каждой загрузке.
Кэширование:
Если сущность кэшируется (например, через @Cacheable), значение @Formula также кэшируется.
Обработка @Formula
Во время компиляции Hibernate:
Hibernate парсит SQL-выражение и проверяет его синтаксис (но не валидирует до выполнения запроса).
Если выражение содержит ссылки на другие таблицы, Hibernate не создает JOIN автоматически – нужно явно указывать подзапросы.
В рантайме:
При генерации SQL для загрузки сущности Hibernate подставляет формулу как есть (без параметризации).
Зависимость от диалекта БД
Поскольку @Formula использует "сырой" SQL, выражение должно быть совместимо с диалектом Hibernate (MySQLDialect, PostgreSQLDialect и т. д.).
Пример для PostgreSQL:
@Formula("CONCAT(first_name, ' ', last_name)")
private String fullName;
Для MySQL CONCAT работает аналогично, но в Oracle нужно использовать ||.
Варианты использования и ограничения
1. Динамические вычисления
Расчет рейтинга на основе связанных данных:
@Formula("(SELECT AVG(r.rating) FROM reviews r WHERE r.product_id = id)")
private Double averageRating;
2. Использование SQL-функций
Форматирование даты:
@Formula("TO_CHAR(created_at, 'YYYY-MM-DD')")
private String creationDate;
3. Ограничения
Нет поддержки JPA-стандарта: @Formula – это аннотация Hibernate, а не JPA.
Нет параметризации: SQL в @Formula статичен, нельзя передавать параметры.
Производительность: Сложные подзапросы могут замедлять загрузку сущностей.
Альтернативы
@PostLoad + вычисление в Java:
@Transient
private int orderCount;
@PostLoad
private void calculateOrderCount() {
this.orderCount = orderRepository.countByCustomerId(this.id);
}
Плюс: независимость от SQL. Минус: вычисление в Java, а не в БД.
Представления (Database Views) + @Immutable:
Создать VIEW в БД и отобразить его как неизменяемую сущность.
#Java #Training #Hard #Spring #Hibernate #Formula
Что выведет код?
#Tasks
import java.util.HashSet;
import java.util.Set;
class Person1004 {
String name;
Person1004(String name) { this.name = name; }
public boolean equals(Object o) {
return this.name.length() == ((Person1004)o).name.length();
}
}
public class Task100425 {
public static void main(String[] args) {
Set<Person1004> set = new HashSet<>();
set.add(new Person1004("Alice"));
set.add(new Person1004("Bob"));
set.add(new Person1004("Charlie"));
set.add(new Person1004("Dave"));
System.out.println(set.size());
}
}
#Tasks
Please open Telegram to view this post
VIEW IN TELEGRAM
Вопросы с собеседования 👩💻
Что такое default метод в интерфейсе?
Что такое default метод в интерфейсе?
Anonymous Quiz
13%
Метод без реализации
87%
Метод с реализацией по умолчанию
0%
Приватный метод
0%
Статический метод
@Generated в Hibernate
Аннотация @Generated указывает, что значение поля автоматически генерируется базой данных (например, через DEFAULT, TRIGGER, AUTO_INCREMENT или вычисляемый столбец). В отличие от @Formula, поле сохраняется в БД, но Hibernate обновляет его после вставки или обновления. Находится в пакете org.hibernate.annotations.
Атрибуты аннотации
GenerationTime (обязательный):
Определяет, когда значение генерируется:
GenerationTime.INSERT – только при вставке (например, AUTO_INCREMENT).
GenerationTime.ALWAYS – при вставке и обновлении (например, триггеры или DEFAULT-значения).
value (необязательный, String):
SQL-выражение, которое генерирует значение (если БД поддерживает, например, DEFAULT CURRENT_TIMESTAMP).
Используется редко, так как обычно генерация определяется на стороне БД.
Жизненный цикл
При сохранении (INSERT)
Если GenerationTime.INSERT или GenerationTime.ALWAYS:
Hibernate выполняет INSERT, пропуская это поле.
После вставки делает SELECT (или использует RETURNING в PostgreSQL), чтобы получить сгенерированное значение.
Пример для AUTO_INCREMENT:
При обновлении (UPDATE)
Если GenerationTime.ALWAYS:
Hibernate пропускает поле в UPDATE.
После обновления делает SELECT для получения нового значения.
Пример для триггера, обновляющего last_modified:
Кэширование
Если включен кэш второго уровня (@Cacheable), Hibernate обновляет значение в кэше после SELECT.
Механизмы Hibernate и настройки
1. Как Hibernate обрабатывает @Generated
Генерация SQL:
Поле исключается из INSERT/UPDATE.
Для получения значения выполняется дополнительный SELECT (если БД не поддерживает RETURNING).
Поддержка RETURNING (PostgreSQL, Oracle, SQL Server):
Если диалект поддерживает RETURNING, Hibernate использует его вместо отдельного SELECT:
2. Зависимость от диалекта БД
Поведение зависит от возможностей БД:
MySQL: AUTO_INCREMENT, DEFAULT CURRENT_TIMESTAMP.
PostgreSQL: SERIAL, GENERATED ALWAYS AS IDENTITY, триггеры.
Oracle: SEQUENCE + триггеры, DEFAULT-значения.
Примеры использования
1. Автоинкрементный ID
Эквивалент Hibernate:
2. Временная метка создания
3. Триггер для аудита
Ограничения и альтернативы
Ограничения:
Не все БД поддерживают RETURNING:
Для MySQL требуется дополнительный SELECT.
Нет контроля над генерацией:
Логика полностью определяется БД (триггеры, DEFAULT).
Альтернативы:
@PrePersist и @PreUpdate (JPA):
#Java #Training #Hard #Spring #Hibernate #Generated
Аннотация @Generated указывает, что значение поля автоматически генерируется базой данных (например, через DEFAULT, TRIGGER, AUTO_INCREMENT или вычисляемый столбец). В отличие от @Formula, поле сохраняется в БД, но Hibernate обновляет его после вставки или обновления. Находится в пакете org.hibernate.annotations.
Атрибуты аннотации
GenerationTime (обязательный):
Определяет, когда значение генерируется:
GenerationTime.INSERT – только при вставке (например, AUTO_INCREMENT).
GenerationTime.ALWAYS – при вставке и обновлении (например, триггеры или DEFAULT-значения).
value (необязательный, String):
SQL-выражение, которое генерирует значение (если БД поддерживает, например, DEFAULT CURRENT_TIMESTAMP).
Используется редко, так как обычно генерация определяется на стороне БД.
Жизненный цикл
При сохранении (INSERT)
Если GenerationTime.INSERT или GenerationTime.ALWAYS:
Hibernate выполняет INSERT, пропуская это поле.
После вставки делает SELECT (или использует RETURNING в PostgreSQL), чтобы получить сгенерированное значение.
Пример для AUTO_INCREMENT:
INSERT INTO users (name) VALUES ('Alice'); -- id не указывается
SELECT last_insert_id(); -- Hibernate получает сгенерированный ID
При обновлении (UPDATE)
Если GenerationTime.ALWAYS:
Hibernate пропускает поле в UPDATE.
После обновления делает SELECT для получения нового значения.
Пример для триггера, обновляющего last_modified:
UPDATE users SET name = 'Bob' WHERE id = 1; -- last_modified не указывается
SELECT last_modified FROM users WHERE id = 1; -- Hibernate получает новое значение
Кэширование
Если включен кэш второго уровня (@Cacheable), Hibernate обновляет значение в кэше после SELECT.
Механизмы Hibernate и настройки
1. Как Hibernate обрабатывает @Generated
Генерация SQL:
Поле исключается из INSERT/UPDATE.
Для получения значения выполняется дополнительный SELECT (если БД не поддерживает RETURNING).
Поддержка RETURNING (PostgreSQL, Oracle, SQL Server):
Если диалект поддерживает RETURNING, Hibernate использует его вместо отдельного SELECT:
INSERT INTO users (name) VALUES ('Alice') RETURNING id, created_at;
2. Зависимость от диалекта БД
Поведение зависит от возможностей БД:
MySQL: AUTO_INCREMENT, DEFAULT CURRENT_TIMESTAMP.
PostgreSQL: SERIAL, GENERATED ALWAYS AS IDENTITY, триггеры.
Oracle: SEQUENCE + триггеры, DEFAULT-значения.
Примеры использования
1. Автоинкрементный ID
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // Аналог для JPA
private Long id;
Эквивалент Hibernate:
@Id
@Generated(GenerationTime.INSERT)
private Long id;
2. Временная метка создания
@Column(insertable = false) // Не указывается в INSERT
@Generated(GenerationTime.INSERT)
private LocalDateTime createdAt; // DEFAULT CURRENT_TIMESTAMP в БД
3. Триггер для аудита
@Column(updatable = false)
@Generated(GenerationTime.ALWAYS)
private LocalDateTime lastModified; // Обновляется триггером БД
Ограничения и альтернативы
Ограничения:
Не все БД поддерживают RETURNING:
Для MySQL требуется дополнительный SELECT.
Нет контроля над генерацией:
Логика полностью определяется БД (триггеры, DEFAULT).
Альтернативы:
@PrePersist и @PreUpdate (JPA):
@PrePersist
private void prePersist() {
this.createdAt = LocalDateTime.now();
}
Плюс: работает везде. Минус: значение генерируется в Java, а не в БД.
#Java #Training #Hard #Spring #Hibernate #Generated
@Immutable в Hibernate
Аннотация @Immutable помечает сущность или коллекцию как неизменяемую, что означает, что Hibernate игнорирует любые попытки обновления или удаления таких объектов. Это полезно для оптимизации производительности и предотвращения случайных изменений.
Пакет: org.hibernate.annotations.
1. Применение и параметры
@Immutable не имеет параметров — это маркерная аннотация.
Где может применяться:
На уровне класса (сущности):
На уровне коллекции:
2. Жизненный цикл и поведение
При загрузке:
Объекты загружаются как обычно, но Hibernate не отслеживает изменения (не добавляет их в PersistenceContext для dirty-checking).
При попытке обновления:
Выбрасывается исключение UnsupportedOperationException (если попытаться изменить поле или вызвать entityManager.merge()).
При удалении:
Игнорируется (если только не используется @SQLDelete с кастомным запросом).
Кэширование:
Сущности с @Immutable идеально подходят для кэширования (например, @Cacheable), так как никогда не меняются.
Механизмы Hibernate и Spring Boot
1. Как Hibernate обрабатывает @Immutable
Оптимизации:
Пропускает dirty-checking для таких сущностей, что ускоряет flush().
Не создает прокси для ленивых загрузок (если @Immutable применен к коллекции).
Исключения:
Session.update(), Session.merge(), Session.delete() игнорируются или выбрасывают исключение.
2. Интеграция с JPA и Spring Boot
JPA-аналоги:
Стандарт JPA не имеет прямой замены @Immutable, но можно эмулировать через:
Примеры использования
1. Справочные данные (Countries, Currencies)
Запросы только на чтение:
2. Логи и аудит (неизменяемые записи)
3. Неизменяемые коллекции
Ограничения и обходные пути
1. Что нельзя сделать с @Immutable
Обновлять поля: Даже через нативный SQL (entityManager.createNativeQuery("UPDATE...")), если только не отключить проверки Hibernate.
Каскадные операции: CascadeType.PERSIST работает, но MERGE, DELETE — нет.
2. Альтернативы
Read-only транзакции (Spring):
DTO и проекции:
#Java #Training #Hard #Spring #Hibernate #Immutable
Аннотация @Immutable помечает сущность или коллекцию как неизменяемую, что означает, что Hibernate игнорирует любые попытки обновления или удаления таких объектов. Это полезно для оптимизации производительности и предотвращения случайных изменений.
Пакет: org.hibernate.annotations.
1. Применение и параметры
@Immutable не имеет параметров — это маркерная аннотация.
Где может применяться:
На уровне класса (сущности):
@Entity
@Immutable
public class LogEntry { ... }
Все экземпляры LogEntry становятся read-only.
На уровне коллекции:
@OneToMany
@Immutable
private List<LogEntry> logs; // Элементы коллекции нельзя изменить
2. Жизненный цикл и поведение
При загрузке:
Объекты загружаются как обычно, но Hibernate не отслеживает изменения (не добавляет их в PersistenceContext для dirty-checking).
При попытке обновления:
Выбрасывается исключение UnsupportedOperationException (если попытаться изменить поле или вызвать entityManager.merge()).
При удалении:
Игнорируется (если только не используется @SQLDelete с кастомным запросом).
Кэширование:
Сущности с @Immutable идеально подходят для кэширования (например, @Cacheable), так как никогда не меняются.
Механизмы Hibernate и Spring Boot
1. Как Hibernate обрабатывает @Immutable
Оптимизации:
Пропускает dirty-checking для таких сущностей, что ускоряет flush().
Не создает прокси для ленивых загрузок (если @Immutable применен к коллекции).
Исключения:
Session.update(), Session.merge(), Session.delete() игнорируются или выбрасывают исключение.
2. Интеграция с JPA и Spring Boot
JPA-аналоги:
Стандарт JPA не имеет прямой замены @Immutable, но можно эмулировать через:
@Entity
@DynamicUpdate(false) // Отключает генерацию UPDATE
@org.hibernate.annotations.OptimisticLocking(type = NONE) // Отключает версионирование
public class LogEntry { ... }
Примеры использования
1. Справочные данные (Countries, Currencies)
@Entity
@Immutable
public class Currency {
@Id
private String code; // USD, EUR
private String name;
// Нет сеттеров
}
Запросы только на чтение:
List<Currency> currencies = entityManager
.createQuery("SELECT c FROM Currency c", Currency.class)
.getResultList();
2. Логи и аудит (неизменяемые записи)
@Entity
@Immutable
public class AuditLog {
@Id @GeneratedValue
private Long id;
private String action;
@Column(updatable = false)
private LocalDateTime createdAt;
}
3. Неизменяемые коллекции
@Entity
public class User {
@Id private Long id;
@OneToMany
@Immutable
private List<LoginHistory> history; // История логинов только для чтения
}
Ограничения и обходные пути
1. Что нельзя сделать с @Immutable
Обновлять поля: Даже через нативный SQL (entityManager.createNativeQuery("UPDATE...")), если только не отключить проверки Hibernate.
Каскадные операции: CascadeType.PERSIST работает, но MERGE, DELETE — нет.
2. Альтернативы
Read-only транзакции (Spring):
@Transactional(readOnly = true) // Оптимизация на уровне JDBC
public List<Currency> getCurrencies() { ... }
DTO и проекции:
public interface CurrencyView {
String getCode();
String getName();
}
Плюс: не требуют Hibernate-сущностей.
#Java #Training #Hard #Spring #Hibernate #Immutable