Аннотация @Projection
Аннотация @Projection используется в Spring Data для определения проекций - специальных интерфейсов или классов, которые позволяют выбирать только определенные поля из сущностей.
Это полезно для:
Оптимизации запросов (избегания select *)
Создания DTO на лету
Кастомизации JSON-представления REST API
Проекции находятся в пакете org.springframework.data.rest.core.config.Projection и применяются к интерфейсам или классам.
Параметры аннотации
types (обязательный)
Тип: Class<?>[]
Описание: Указывает сущности, к которым применяется проекция
Пример:
name (необязательный)
Тип: String
Описание: Задает имя проекции для использования в Spring Data REST
Пример:
Типы проекций
1. Интерфейсные проекции
Наиболее распространенный подход:
2. Классовые проекции (DTO)
Используются классы вместо интерфейсов:
3. Динамические проекции
Можно создавать проекции на лету через SpEL:
Жизненный цикл проекций
Объявление проекции
Создается интерфейс/класс с аннотацией @Projection
Определяются методы, соответствующие полям сущности
Использование в репозитории
Spring Data создает прокси-реализацию при выполнении запроса
Преобразование результатов
После выполнения запроса результаты маппятся на проекцию
Механизмы Spring Data
1. Автоматический маппинг
Spring Data автоматически:
Анализирует методы проекции
Создает соответствующий SQL (только выбранные поля)
Преобразует результат в проекцию
2. Интеграция с репозиториями
3. Поддержка в Spring Data REST
Проекции можно активировать через параметр запроса:
Примеры использования
1. Базовый пример
2. Комплексная проекция
3. Вложенные проекции
Настройки Spring Boot
Автоконфигурация
Spring Boot автоматически настраивает поддержку проекций через SpringDataWebConfiguration
Ручная регистрация
Можно явно зарегистрировать проекции:
Настройка REST
В application.properties:
#Java #Training #Hard #Spring #SpringDataJPA #Projection
Аннотация @Projection используется в Spring Data для определения проекций - специальных интерфейсов или классов, которые позволяют выбирать только определенные поля из сущностей.
Это полезно для:
Оптимизации запросов (избегания select *)
Создания DTO на лету
Кастомизации JSON-представления REST API
Проекции находятся в пакете org.springframework.data.rest.core.config.Projection и применяются к интерфейсам или классам.
Параметры аннотации
types (обязательный)
Тип: Class<?>[]
Описание: Указывает сущности, к которым применяется проекция
Пример:
@Projection(types = {User.class})
public interface UserSummary {
String getUsername();
String getEmail();
}
name (необязательный)
Тип: String
Описание: Задает имя проекции для использования в Spring Data REST
Пример:
@Projection(name = "summary", types = User.class)
public interface UserSummary {...}
Типы проекций
1. Интерфейсные проекции
Наиболее распространенный подход:
@Projection(types = User.class)
public interface UserView {
String getUsername();
String getEmail();
// Можно включать связанные сущности
@Value("#{target.department.name}")
String getDepartmentName();
}
2. Классовые проекции (DTO)
Используются классы вместо интерфейсов:
@Projection(types = User.class)
public class UserDto {
private final String username;
private final String email;
public UserDto(String username, String email) {
this.username = username;
this.email = email;
}
// Геттеры
}
3. Динамические проекции
Можно создавать проекции на лету через SpEL:
@Projection(types = User.class)
public interface DynamicUserView {
@Value("#{target.username + ' (' + target.email + ')'}")
String getDisplayName();
}
Жизненный цикл проекций
Объявление проекции
Создается интерфейс/класс с аннотацией @Projection
Определяются методы, соответствующие полям сущности
Использование в репозитории
Spring Data создает прокси-реализацию при выполнении запроса
Преобразование результатов
После выполнения запроса результаты маппятся на проекцию
Механизмы Spring Data
1. Автоматический маппинг
Spring Data автоматически:
Анализирует методы проекции
Создает соответствующий SQL (только выбранные поля)
Преобразует результат в проекцию
2. Интеграция с репозиториями
public interface UserRepository extends JpaRepository<User, Long> {
<T> List<T> findBy(Class<T> type);
UserSummary findSummaryById(Long id);
}
3. Поддержка в Spring Data REST
Проекции можно активировать через параметр запроса:
GET /users/1?projection=summary
Примеры использования
1. Базовый пример
@Entity
public class User {
private Long id;
private String username;
private String email;
// геттеры/сеттеры
}
@Projection(types = User.class)
public interface UserSummary {
String getUsername();
String getEmail();
}
// В репозитории
UserSummary findSummaryById(Long id);
2. Комплексная проекция
@Projection(types = {User.class, Department.class})
public interface UserDetailView {
String getUsername();
@Value("#{target.department.name}")
String getDepartmentName();
default String getDisplayInfo() {
return getUsername() + " from " + getDepartmentName();
}
}
3. Вложенные проекции
@Projection(types = Order.class)
public interface OrderView {
String getOrderNumber();
@Value("#{target.customer}")
CustomerView getCustomer();
}
@Projection(types = Customer.class)
public interface CustomerView {
String getName();
}
Настройки Spring Boot
Автоконфигурация
Spring Boot автоматически настраивает поддержку проекций через SpringDataWebConfiguration
Ручная регистрация
Можно явно зарегистрировать проекции:
@Configuration
public class ProjectionConfig {
@Bean
public ProjectionDefinitionRegistrar projectionRegistrar() {
return registrar -> {
registrar.addProjection(UserSummary.class);
};
}
}
Настройка REST
В application.properties:
spring.data.rest.default-projection=summary
#Java #Training #Hard #Spring #SpringDataJPA #Projection
Аннотация @Any в Spring
Аннотация @Any относится к пакету org.hibernate.annotations (Hibernate, не Spring), но часто используется в Spring-приложениях, работающих с JPA/Hibernate. Она применяется для обозначения полиморфных ассоциаций, когда сущность может ссылаться на разные типы объектов.
@Any позволяет моделировать связь, где поле может содержать экземпляр любой сущности (из заданного набора), а не только строго определённого типа (как в @ManyToOne или @OneToOne).
Параметры и настройки
Аннотация @Any сама по себе не принимает параметров, но требует дополнительных аннотаций для корректной работы:
@AnyMetaDef (или @AnyMetaDefs для нескольких определений) – определяет метаданные для полиморфной связи:
name (обязательный) – уникальное имя определения, которое используется в @Any.
metaType (по умолчанию String) – тип идентификатора, хранящего информацию о классе (обычно String или Integer).
idType (обязательный) – тип первичного ключа связанных сущностей (например, Long).
metaValues – массив @MetaValue, где каждое значение связывает идентификатор класса с конкретной сущностью.
Пример:
@JoinColumn – указывает столбец в БД, хранящий ID связанной сущности.
@Column (опционально) – указывает столбец, хранящий тип сущности (если метаданные используют отдельный столбец).
Пример использования
Жизненный цикл @Any
Загрузка (@PostLoad)
Hibernate определяет тип сущности по значению в metaColumn и загружает соответствующий объект.
Сохранение (@PrePersist/@PreUpdate)
перед сохранением Hibernate записывает:
ID сущности в animal_id.
Класс сущности (например, "DOG") в animal_type.
Удаление
каскадное удаление не поддерживается автоматически, нужно обрабатывать вручную.
Механизмы Spring и Spring Boot, связанные с @Any
Поскольку @Any – это аннотация Hibernate, её работа зависит от:
spring.jpa.hibernate.ddl-auto (в application.properties/application.yml) – если стоит update или create, Hibernate автоматически создаст таблицы с полями animal_id и animal_type.
spring.jpa.show-sql – полезно для отладки запросов, связанных с полиморфными ассоциациями.
EntityManager – Spring Boot автоматически настраивает его, и через него происходят все операции с @Any.
Ограничения и альтернативы
Нет встроенной поддержки в Spring Data JPA – репозитории не могут автоматически генерировать запросы для @Any.
Альтернативы:
@OneToMany + наследование (@Inheritance(strategy = InheritanceType.SINGLE_TABLE)) – если типы известны заранее.
@ElementCollection + Map<String, Object> – если полиморфизм нужен только для хранения данных.
#Java #Training #Hard #Spring #Hibernate #Any
Аннотация @Any относится к пакету org.hibernate.annotations (Hibernate, не Spring), но часто используется в Spring-приложениях, работающих с JPA/Hibernate. Она применяется для обозначения полиморфных ассоциаций, когда сущность может ссылаться на разные типы объектов.
@Any позволяет моделировать связь, где поле может содержать экземпляр любой сущности (из заданного набора), а не только строго определённого типа (как в @ManyToOne или @OneToOne).
Параметры и настройки
Аннотация @Any сама по себе не принимает параметров, но требует дополнительных аннотаций для корректной работы:
@AnyMetaDef (или @AnyMetaDefs для нескольких определений) – определяет метаданные для полиморфной связи:
name (обязательный) – уникальное имя определения, которое используется в @Any.
metaType (по умолчанию String) – тип идентификатора, хранящего информацию о классе (обычно String или Integer).
idType (обязательный) – тип первичного ключа связанных сущностей (например, Long).
metaValues – массив @MetaValue, где каждое значение связывает идентификатор класса с конкретной сущностью.
Пример:
@AnyMetaDef(
name = "animalMetaDef",
metaType = "string",
idType = "long",
metaValues = {
@MetaValue(value = "DOG", targetEntity = Dog.class),
@MetaValue(value = "CAT", targetEntity = Cat.class)
}
)
@JoinColumn – указывает столбец в БД, хранящий ID связанной сущности.
@Column (опционально) – указывает столбец, хранящий тип сущности (если метаданные используют отдельный столбец).
Пример использования
@Entity
public class Zoo {
@Id
private Long id;
@Any(metaDef = "animalMetaDef", metaColumn = @Column(name = "animal_type"))
@JoinColumn(name = "animal_id")
private Animal animal; // Может быть Dog или Cat
}
Жизненный цикл @Any
Загрузка (@PostLoad)
Hibernate определяет тип сущности по значению в metaColumn и загружает соответствующий объект.
Сохранение (@PrePersist/@PreUpdate)
перед сохранением Hibernate записывает:
ID сущности в animal_id.
Класс сущности (например, "DOG") в animal_type.
Удаление
каскадное удаление не поддерживается автоматически, нужно обрабатывать вручную.
Механизмы Spring и Spring Boot, связанные с @Any
Поскольку @Any – это аннотация Hibernate, её работа зависит от:
spring.jpa.hibernate.ddl-auto (в application.properties/application.yml) – если стоит update или create, Hibernate автоматически создаст таблицы с полями animal_id и animal_type.
spring.jpa.show-sql – полезно для отладки запросов, связанных с полиморфными ассоциациями.
EntityManager – Spring Boot автоматически настраивает его, и через него происходят все операции с @Any.
Ограничения и альтернативы
Нет встроенной поддержки в Spring Data JPA – репозитории не могут автоматически генерировать запросы для @Any.
Альтернативы:
@OneToMany + наследование (@Inheritance(strategy = InheritanceType.SINGLE_TABLE)) – если типы известны заранее.
@ElementCollection + Map<String, Object> – если полиморфизм нужен только для хранения данных.
#Java #Training #Hard #Spring #Hibernate #Any
Аннотация @BatchSize в Spring (Hibernate)
Аннотация @BatchSize принадлежит пакету org.hibernate.annotations и используется для оптимизации загрузки коллекций или прокси-объектов в Hibernate. Она позволяет загружать элементы пакетно (batch), уменьшая количество SQL-запросов (проблема N+1).
Применяется в двух случаях:
Для коллекций (@OneToMany, @ManyToMany) – загружает несколько связанных коллекций одним запросом.
Для лениво загружаемых сущностей (@ManyToOne, @OneToOne) – загружает несколько прокси-объектов партиями.
Параметры и настройки
У аннотации есть один обязательный параметр - size: размер пакета (сколько элементов загружать за один SQL-запрос), обычно 10, 20, 50.
Примеры использования
Пакетная загрузка коллекций (N+1 Problem Fix)
Как работает:
Если загружается 100 авторов, без @BatchSize Hibernate выполнит 100+1 запрос (1 для авторов + 100 для книг каждого).
С @BatchSize(size=10) Hibernate сделает 1 запрос для авторов + 10 запросов для книг (каждый запрос загружает книги для 10 авторов).
Пакетная загрузка ленивых сущностей
Как работает:
При обращении к book.getAuthor(), Hibernate не загружает автора сразу, а ждёт, пока не потребуются несколько авторов.
Когда накопятся 5 ленивых прокси, Hibernate выполнит один запрос вида:
Жизненный цикл
Инициализация прокси/коллекции – Hibernate откладывает загрузку до первого обращения.
Пакетная загрузка – при достижении size или принудительной инициализации (например, Hibernate.initialize()).
Кэширование – загруженные объекты помещаются в кэш первого уровня (Session).
Настройки Hibernate в Spring Boot
@BatchSize работает на уровне Hibernate, но Spring Boot позволяет управлять его поведением через:
spring.jpa.properties.hibernate.default_batch_fetch_size (в application.properties)
Если @BatchSize указан на поле, он имеет приоритет.
Оптимизация запросов
JOIN FETCH (в JPQL) загружает данные одним запросом, но может привести к Cartesian Product.
@BatchSize даёт баланс между количеством запросов и объёмом данных.
@BatchSize можно комбинировать с @EntityGraph, но обычно @EntityGraph полностью отключает ленивую загрузку.
#Java #Training #Hard #Spring #Hibernate #BatchSize
Аннотация @BatchSize принадлежит пакету org.hibernate.annotations и используется для оптимизации загрузки коллекций или прокси-объектов в Hibernate. Она позволяет загружать элементы пакетно (batch), уменьшая количество SQL-запросов (проблема N+1).
Применяется в двух случаях:
Для коллекций (@OneToMany, @ManyToMany) – загружает несколько связанных коллекций одним запросом.
Для лениво загружаемых сущностей (@ManyToOne, @OneToOne) – загружает несколько прокси-объектов партиями.
Параметры и настройки
У аннотации есть один обязательный параметр - size: размер пакета (сколько элементов загружать за один SQL-запрос), обычно 10, 20, 50.
Примеры использования
Пакетная загрузка коллекций (N+1 Problem Fix)
@Entity
public class Author {
@Id
private Long id;
@OneToMany(mappedBy = "author")
@BatchSize(size = 10) // Загружает до 10 книг за один запрос
private List<Book> books;
}
Как работает:
Если загружается 100 авторов, без @BatchSize Hibernate выполнит 100+1 запрос (1 для авторов + 100 для книг каждого).
С @BatchSize(size=10) Hibernate сделает 1 запрос для авторов + 10 запросов для книг (каждый запрос загружает книги для 10 авторов).
Пакетная загрузка ленивых сущностей
@Entity
public class Book {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@BatchSize(size = 5) // Загружает авторов пачками по 5
private Author author;
}
Как работает:
При обращении к book.getAuthor(), Hibernate не загружает автора сразу, а ждёт, пока не потребуются несколько авторов.
Когда накопятся 5 ленивых прокси, Hibernate выполнит один запрос вида:
SELECT * FROM author WHERE id IN (1, 2, 3, 4, 5)
Жизненный цикл
Инициализация прокси/коллекции – Hibernate откладывает загрузку до первого обращения.
Пакетная загрузка – при достижении size или принудительной инициализации (например, Hibernate.initialize()).
Кэширование – загруженные объекты помещаются в кэш первого уровня (Session).
Настройки Hibernate в Spring Boot
@BatchSize работает на уровне Hibernate, но Spring Boot позволяет управлять его поведением через:
spring.jpa.properties.hibernate.default_batch_fetch_size (в application.properties)
spring.jpa.properties.hibernate.default_batch_fetch_size=20
Устанавливает глобальный размер пакета для всех ленивых загрузок.
Если @BatchSize указан на поле, он имеет приоритет.
Оптимизация запросов
JOIN FETCH (в JPQL) загружает данные одним запросом, но может привести к Cartesian Product.
@BatchSize даёт баланс между количеством запросов и объёмом данных.
@BatchSize можно комбинировать с @EntityGraph, но обычно @EntityGraph полностью отключает ленивую загрузку.
#Java #Training #Hard #Spring #Hibernate #BatchSize
Аннотации @Cache и @Cacheable в Hibernate
Аннотации @Cache и @Cacheable относятся к кешированию сущностей и коллекций в Hibernate. Они позволяют оптимизировать производительность за счёт хранения часто используемых данных в памяти, уменьшая количество обращений к базе данных.
@Cache (org.hibernate.annotations.Cache) – настраивает стратегию кеширования для сущности или коллекции.
@Cacheable (javax.persistence.Cacheable / jakarta.persistence.Cacheable) – указывает, что сущность может быть кеширована (используется вместе с @Cache).
@Cache (Hibernate)
Параметры:
usage (CacheConcurrencyStrategy) – стратегия кеширования:
READ_ONLY – только для чтения (если сущность не изменяется).
NONSTRICT_READ_WRITE – кеш обновляется асинхронно при изменениях.
READ_WRITE – гарантирует согласованность через мягкие блокировки.
TRANSACTIONAL – полная поддержка транзакций (для JTA).
region (опционально) – имя региона кеша (если используется несколько кешей, например, Ehcache, Hazelcast).
include (CacheInclude) – указывает, включать ли ленивые поля в кеш (ALL или NON_LAZY).
Пример:
@Cacheable (JPA)
Указывает, что сущность поддерживает кеширование.
Работает в паре с @Cache (Hibernate) или настройками JPA-провайдера.
Если @Cacheable(false), сущность игнорируется кешем, даже если кеширование включено глобально.
Пример:
Механизмы кеширования в Hibernate
Уровни кеширования
Кеш первого уровня (Session)
Автоматический, живет в рамках одной сессии (EntityManager).
Не требует @Cache.
Кеш второго уровня (SessionFactory)
Общий для всех сессий.
Требует явного объявления (@Cache + @Cacheable).
Кеш запросов (Query Cache)
Кеширует результаты JPQL/HQL-запросов.
Включается отдельно:
Настройка в Spring Boot
Включение кеша второго уровня
Указание кешируемых сущностей
Через @Cache + @Cacheable.
Или глобально в application.properties:
Настройка поставщика кеша
Например, для Ehcache:
Когда использовать?
@Cache + @Cacheable – для часто читаемых, редко изменяемых данных (справочники, настройки).
READ_ONLY – если сущности никогда не обновляются (например, Country, City).
READ_WRITE – если данные изменяются, но требуется высокая скорость чтения.
Избегать для часто изменяемых данных (кеш будет постоянно инвалидироваться).
#Java #Training #Hard #Spring #Hibernate #Cache #Cacheable
Аннотации @Cache и @Cacheable относятся к кешированию сущностей и коллекций в Hibernate. Они позволяют оптимизировать производительность за счёт хранения часто используемых данных в памяти, уменьшая количество обращений к базе данных.
@Cache (org.hibernate.annotations.Cache) – настраивает стратегию кеширования для сущности или коллекции.
@Cacheable (javax.persistence.Cacheable / jakarta.persistence.Cacheable) – указывает, что сущность может быть кеширована (используется вместе с @Cache).
@Cache (Hibernate)
Параметры:
usage (CacheConcurrencyStrategy) – стратегия кеширования:
READ_ONLY – только для чтения (если сущность не изменяется).
NONSTRICT_READ_WRITE – кеш обновляется асинхронно при изменениях.
READ_WRITE – гарантирует согласованность через мягкие блокировки.
TRANSACTIONAL – полная поддержка транзакций (для JTA).
region (опционально) – имя региона кеша (если используется несколько кешей, например, Ehcache, Hazelcast).
include (CacheInclude) – указывает, включать ли ленивые поля в кеш (ALL или NON_LAZY).
Пример:
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "employees")
public class Employee { ... }
@Cacheable (JPA)
Указывает, что сущность поддерживает кеширование.
Работает в паре с @Cache (Hibernate) или настройками JPA-провайдера.
Если @Cacheable(false), сущность игнорируется кешем, даже если кеширование включено глобально.
Пример:
@Entity
@Cacheable(true) // Включено кеширование (по умолчанию true)
public class Product { ... }
Механизмы кеширования в Hibernate
Уровни кеширования
Кеш первого уровня (Session)
Автоматический, живет в рамках одной сессии (EntityManager).
Не требует @Cache.
Кеш второго уровня (SessionFactory)
Общий для всех сессий.
Требует явного объявления (@Cache + @Cacheable).
Кеш запросов (Query Cache)
Кеширует результаты JPQL/HQL-запросов.
Включается отдельно:
spring.jpa.properties.hibernate.cache.use_query_cache=true
Настройка в Spring Boot
Включение кеша второго уровня
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
(Пример для Ehcache; также поддерживаются Infinispan, Hazelcast, Redis.)
Указание кешируемых сущностей
Через @Cache + @Cacheable.
Или глобально в application.properties:
spring.jpa.properties.javax.persistence.sharedCache.mode=ALL
(Варианты: ALL, NONE, ENABLE_SELECTIVE, DISABLE_SELECTIVE.)
Настройка поставщика кеша
Например, для Ehcache:
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
И файл ehcache.xml в resources/.
Когда использовать?
@Cache + @Cacheable – для часто читаемых, редко изменяемых данных (справочники, настройки).
READ_ONLY – если сущности никогда не обновляются (например, Country, City).
READ_WRITE – если данные изменяются, но требуется высокая скорость чтения.
Избегать для часто изменяемых данных (кеш будет постоянно инвалидироваться).
#Java #Training #Hard #Spring #Hibernate #Cache #Cacheable
Аннотация @Cascade в Hibernate
Аннотация @Cascade (org.hibernate.annotations.Cascade) управляет каскадными операциями (сохранение, обновление, удаление) для ассоциаций между сущностями. Она дополняет или заменяет стандартные каскадные операции JPA (CascadeType).
Отличается от javax.persistence.CascadeType тем, что поддерживает специфичные для Hibernate каскады, например, CascadeType.LOCK или CascadeType.REPLICATE.
Параметры
Принимает одно или несколько значений из перечисления org.hibernate.annotations.CascadeType:
PERSIST – сохраняет связанную сущность при сохранении родителя (аналог CascadeType.PERSIST).
MERGE – обновляет связанную сущность при обновлении родителя (аналог CascadeType.MERGE).
REMOVE – удаляет связанную сущность при удалении родителя (аналог CascadeType.REMOVE).
REFRESH – обновляет связанную сущность при обновлении родителя (аналог CascadeType.REFRESH).
DETACH – отключает связанную сущность от контекста (аналог CascadeType.DETACH).
SAVE_UPDATE – сохраняет или обновляет связанную сущность при сохранении/обновлении родителя (устарело в Hibernate 6+, вместо этого используйте PERSIST + MERGE).
LOCK – блокирует связанную сущность при блокировке родителя (специфично для Hibernate).
REPLICATE – реплицирует связанную сущность при репликации родителя (специфично для Hibernate).
DELETE – удаляет связанную сущность при удалении родителя (аналог REMOVE, но работает в некоторых случаях иначе).
DELETE_ORPHAN – удаляет связанную сущность, если она больше не связана с родителем (работает только для коллекций).
Примеры использования
Каскадное сохранение и удаление
Что делает:
При сохранении Parent автоматически сохраняются все Child.
При удалении Parent автоматически удаляются все Child.
Каскадное обновление и управление "сиротами"
Что делает:
При обновлении Author обновляются все связанные Book.
Если книга удаляется из списка books, она автоматически удаляется из БД (orphanRemoval).
Специфичные каскады Hibernate (LOCK, REPLICATE)
Что делает:
При блокировке Order блокируются все Item.
При репликации Order реплицируются все Item.
Разница между @Cascade и cascade в JPA
JPA (javax.persistence.CascadeType)
Стандартный механизм JPA.
Поддерживает только базовые операции (PERSIST, MERGE, REMOVE, REFRESH, DETACH, ALL).
Hibernate (@Cascade)
Расширенный функционал (например, DELETE_ORPHAN, LOCK, REPLICATE).
Работает только в Hibernate.
Когда использовать @Cascade?
Если нужны специфичные для Hibernate каскады (LOCK, REPLICATE).
Если требуется удаление "сирот" (DELETE_ORPHAN).
В остальных случаях лучше использовать стандартный cascade из JPA.
Оптимизация производительности
Каскадные операции могут приводить к неожиданным DELETE/UPDATE.
Рекомендуется:
Использовать orphanRemoval только там, где это необходимо.
Избегать CascadeType.ALL (может привести к неявным удалениям).
#Java #Training #Hard #Spring #Hibernate #Cascade
Аннотация @Cascade (org.hibernate.annotations.Cascade) управляет каскадными операциями (сохранение, обновление, удаление) для ассоциаций между сущностями. Она дополняет или заменяет стандартные каскадные операции JPA (CascadeType).
Отличается от javax.persistence.CascadeType тем, что поддерживает специфичные для Hibernate каскады, например, CascadeType.LOCK или CascadeType.REPLICATE.
Параметры
Принимает одно или несколько значений из перечисления org.hibernate.annotations.CascadeType:
PERSIST – сохраняет связанную сущность при сохранении родителя (аналог CascadeType.PERSIST).
MERGE – обновляет связанную сущность при обновлении родителя (аналог CascadeType.MERGE).
REMOVE – удаляет связанную сущность при удалении родителя (аналог CascadeType.REMOVE).
REFRESH – обновляет связанную сущность при обновлении родителя (аналог CascadeType.REFRESH).
DETACH – отключает связанную сущность от контекста (аналог CascadeType.DETACH).
SAVE_UPDATE – сохраняет или обновляет связанную сущность при сохранении/обновлении родителя (устарело в Hibernate 6+, вместо этого используйте PERSIST + MERGE).
LOCK – блокирует связанную сущность при блокировке родителя (специфично для Hibernate).
REPLICATE – реплицирует связанную сущность при репликации родителя (специфично для Hibernate).
DELETE – удаляет связанную сущность при удалении родителя (аналог REMOVE, но работает в некоторых случаях иначе).
DELETE_ORPHAN – удаляет связанную сущность, если она больше не связана с родителем (работает только для коллекций).
Примеры использования
Каскадное сохранение и удаление
@Entity
public class Parent {
@Id
private Long id;
@OneToMany(mappedBy = "parent")
@Cascade({CascadeType.PERSIST, CascadeType.REMOVE})
private List<Child> children;
}
Что делает:
При сохранении Parent автоматически сохраняются все Child.
При удалении Parent автоматически удаляются все Child.
Каскадное обновление и управление "сиротами"
@Entity
public class Author {
@Id
private Long id;
@OneToMany(mappedBy = "author")
@Cascade({CascadeType.MERGE, CascadeType.DELETE_ORPHAN})
private List<Book> books;
}
Что делает:
При обновлении Author обновляются все связанные Book.
Если книга удаляется из списка books, она автоматически удаляется из БД (orphanRemoval).
Специфичные каскады Hibernate (LOCK, REPLICATE)
@Entity
public class Order {
@Id
private Long id;
@OneToMany(mappedBy = "order")
@Cascade({CascadeType.LOCK, CascadeType.REPLICATE})
private List<Item> items;
}
Что делает:
При блокировке Order блокируются все Item.
При репликации Order реплицируются все Item.
Разница между @Cascade и cascade в JPA
JPA (javax.persistence.CascadeType)
@OneToMany(mappedBy = "parent", cascade = {javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REMOVE})
Стандартный механизм JPA.
Поддерживает только базовые операции (PERSIST, MERGE, REMOVE, REFRESH, DETACH, ALL).
Hibernate (@Cascade)
@OneToMany(mappedBy = "parent")
@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
Расширенный функционал (например, DELETE_ORPHAN, LOCK, REPLICATE).
Работает только в Hibernate.
Когда использовать @Cascade?
Если нужны специфичные для Hibernate каскады (LOCK, REPLICATE).
Если требуется удаление "сирот" (DELETE_ORPHAN).
В остальных случаях лучше использовать стандартный cascade из JPA.
Оптимизация производительности
Каскадные операции могут приводить к неожиданным DELETE/UPDATE.
Рекомендуется:
Использовать orphanRemoval только там, где это необходимо.
Избегать CascadeType.ALL (может привести к неявным удалениям).
#Java #Training #Hard #Spring #Hibernate #Cascade
@ColumnTransformer в Hibernate / JPA
Аннотация @ColumnTransformer (из пакета org.hibernate.annotations) позволяет кастомизировать SQL-выражения для чтения (read) и записи (write) значений столбца в БД.
Используется, когда необходимо:
Применить функцию БД при загрузке (SELECT).
Преобразовать значение перед вставкой/обновлением (INSERT/UPDATE).
Работать с зашифрованными или вычисляемыми полями.
Аннотация принимает следующие атрибуты:
read (String):
SQL-выражение, применяемое при чтении значения из БД.
Может включать функции БД (например, decrypt, CAST, COALESCE).
Использует плейсхолдер ? для исходного значения столбца.
Пример:
write (String):
SQL-выражение, применяемое при записи значения в БД.
Также использует плейсхолдер ? для нового значения.
Пример:
forColumn (String, опционально):
Указывает имя столбца, если оно отличается от имени поля в сущности.
Жизненный цикл и обработка
При загрузке сущности (SELECT)
Hibernate заменяет прямое обращение к столбцу на выражение из read.
Пример SQL:
При сохранении (INSERT/UPDATE)
Значение поля преобразуется выражением из write.
Пример SQL:
Нативные запросы
Если используется EntityManager.createNativeQuery(), @ColumnTransformer игнорируется (требуется ручное применение функций БД).
Механизмы Hibernate и интеграция
Генерация SQL
Обработка @ColumnTransformer происходит на этапе компиляции HQL в SQL (через ASTQueryTranslatorFactory).
Зависимость от диалекта БД
Выражения в read/write должны быть совместимы с диалектом БД (например, AES_ENCRYPT в MySQL, PGP_SYM_ENCRYPT в PostgreSQL).
Интеграция с JPA
Аннотация является Hibernate-специфичной и не входит в стандарт JPA.
Примеры использования
1. Шифрование данных
2. Преобразование типов
3. Условные значения
Ограничения и настройки
Производительность
Сложные выражения могут замедлять запросы.
Поддержка в JPQL
В JPQL-запросах @ColumnTransformer не применяется (только в SQL, генерируемом Hibernate).
Альтернативы
Для сложной логики можно использовать:
@Convert (JPA 2.1).
Реализацию UserType в Hibernate.
Настройка через hibernate.xml
Нет глобальной настройки для @ColumnTransformer — только через аннотации.
#Java #Training #Hard #Spring #Hibernate #ColumnTransformer
Аннотация @ColumnTransformer (из пакета org.hibernate.annotations) позволяет кастомизировать SQL-выражения для чтения (read) и записи (write) значений столбца в БД.
Используется, когда необходимо:
Применить функцию БД при загрузке (SELECT).
Преобразовать значение перед вставкой/обновлением (INSERT/UPDATE).
Работать с зашифрованными или вычисляемыми полями.
Аннотация принимает следующие атрибуты:
read (String):
SQL-выражение, применяемое при чтении значения из БД.
Может включать функции БД (например, decrypt, CAST, COALESCE).
Использует плейсхолдер ? для исходного значения столбца.
Пример:
@ColumnTransformer(read = "decrypt(credit_card_num, 'my_secret_key')")
private String creditCardNumber;
write (String):
SQL-выражение, применяемое при записи значения в БД.
Также использует плейсхолдер ? для нового значения.
Пример:
@ColumnTransformer(write = "encrypt(?, 'my_secret_key')")
private String password;
forColumn (String, опционально):
Указывает имя столбца, если оно отличается от имени поля в сущности.
Жизненный цикл и обработка
При загрузке сущности (SELECT)
Hibernate заменяет прямое обращение к столбцу на выражение из read.
Пример SQL:
SELECT decrypt(credit_card_num, 'my_secret_key') FROM users WHERE id = 1;
При сохранении (INSERT/UPDATE)
Значение поля преобразуется выражением из write.
Пример SQL:
INSERT INTO users (password) VALUES (encrypt('qwerty', 'my_secret_key'));
Нативные запросы
Если используется EntityManager.createNativeQuery(), @ColumnTransformer игнорируется (требуется ручное применение функций БД).
Механизмы Hibernate и интеграция
Генерация SQL
Обработка @ColumnTransformer происходит на этапе компиляции HQL в SQL (через ASTQueryTranslatorFactory).
Зависимость от диалекта БД
Выражения в read/write должны быть совместимы с диалектом БД (например, AES_ENCRYPT в MySQL, PGP_SYM_ENCRYPT в PostgreSQL).
Интеграция с JPA
Аннотация является Hibernate-специфичной и не входит в стандарт JPA.
Примеры использования
1. Шифрование данных
@Entity
public class User {
@Id
private Long id;
@ColumnTransformer(
read = "pgp_sym_decrypt(credit_card_num, 'secret_key')",
write = "pgp_sym_encrypt(?, 'secret_key')"
)
@Column(name = "credit_card_num")
private String creditCardNumber;
}
2. Преобразование типов
@ColumnTransformer(
read = "CAST(price AS decimal(10,2))",
write = "CAST(? AS varchar)"
)
private BigDecimal price;
3. Условные значения
@ColumnTransformer(
read = "COALESCE(status, 'DEFAULT')"
)
private String status;
Ограничения и настройки
Производительность
Сложные выражения могут замедлять запросы.
Поддержка в JPQL
В JPQL-запросах @ColumnTransformer не применяется (только в SQL, генерируемом Hibernate).
Альтернативы
Для сложной логики можно использовать:
@Convert (JPA 2.1).
Реализацию UserType в Hibernate.
Настройка через hibernate.xml
Нет глобальной настройки для @ColumnTransformer — только через аннотации.
#Java #Training #Hard #Spring #Hibernate #ColumnTransformer
@DynamicInsert в Hibernate
Аннотация @DynamicInsert (из пакета org.hibernate.annotations) указывает Hibernate генерировать SQL-запросы INSERT только для ненулевых полей сущности. Это позволяет оптимизировать вставку данных, исключая из запроса столбцы со значениями null.
Применяется на уровне класса сущности:
У @DynamicInsert нет настраиваемых атрибутов — это маркерная аннотация (присутствие/отсутствие влияет на поведение).
Как работает
Если @DynamicInsert = true (или аннотация присутствует):
Hibernate анализирует, какие поля сущности не null.
В SQL-запрос INSERT включаются только эти поля.
Если @DynamicInsert = false (по умолчанию):
В INSERT попадают все поля, включая null.
Примеры SQL
Без @DynamicInsert
Сгенерированный SQL (все поля, даже age=null):
С @DynamicInsert
Тот же код, но SQL будет короче:
Жизненный цикл и обработка
Во время компиляции маппинга
Hibernate определяет, нужно ли учитывать @DynamicInsert для сущности.
При выполнении session.save()
Проверяются значения полей.
Формируется SQL с учетом только ненулевых полей.
При генерации прокси-классов (если используется ленивая загрузка)
Не влияет напрямую, но может уменьшить объем данных в кеше.
Настройки и интеграция
В hibernate.cfg.xml или application.properties:
Но лучше использовать аннотацию для точечного контроля.
Совместимость с другими аннотациями
@DynamicUpdate – аналогично оптимизирует UPDATE.
@SelectBeforeUpdate – может конфликтовать (проверяет изменения перед UPDATE).
Когда использовать
Плюсы
Уменьшает размер SQL-запросов.
Ускоряет вставку, если много null-полей.
Полезно для таблиц с большим числом столбцов.
Минусы
Усложняет отладку (меняется SQL).
Незначительный оверхед на проверку null.
Бесполезен, если все поля обычно заполнены.
Оптимальные сценарии
Таблицы с 50+ столбцами, где большинство значений null.
Частые вставки частично заполненных объектов.
Работа с унаследованными системами, где NULL в столбцах нежелателен.
Примеры
Пример 1: Игнорирование null
При сохранении Product(id=1, name="Laptop", stock=null):
Комбинация с @ColumnDefault
При Account(id=1, balance=null) в БД запишется balance=0.
Ограничения
Не работает с @GeneratedValue (Hibernate требует все поля для идентификации).
Не влияет на JPQL/HQL – только на SQL, генерируемый Hibernate.
Проблемы с кешированием – если кеш второго уровня ожидает полный объект.
#Java #Training #Hard #Spring #Hibernate #DynamicInsert
Аннотация @DynamicInsert (из пакета org.hibernate.annotations) указывает Hibernate генерировать SQL-запросы INSERT только для ненулевых полей сущности. Это позволяет оптимизировать вставку данных, исключая из запроса столбцы со значениями null.
Применяется на уровне класса сущности:
@Entity
@DynamicInsert
public class User { ... }
У @DynamicInsert нет настраиваемых атрибутов — это маркерная аннотация (присутствие/отсутствие влияет на поведение).
Как работает
Если @DynamicInsert = true (или аннотация присутствует):
Hibernate анализирует, какие поля сущности не null.
В SQL-запрос INSERT включаются только эти поля.
Если @DynamicInsert = false (по умолчанию):
В INSERT попадают все поля, включая null.
Примеры SQL
Без @DynamicInsert
User user = new User();
user.setId(1L);
user.setName("Alice"); // age = null
Сгенерированный SQL (все поля, даже age=null):
INSERT INTO user (id, name, age) VALUES (1, 'Alice', NULL);
С @DynamicInsert
@DynamicInsert
@Entity
public class User { ... }
Тот же код, но SQL будет короче:
INSERT INTO user (id, name) VALUES (1, 'Alice'); -- поле age пропущено!
Жизненный цикл и обработка
Во время компиляции маппинга
Hibernate определяет, нужно ли учитывать @DynamicInsert для сущности.
При выполнении session.save()
Проверяются значения полей.
Формируется SQL с учетом только ненулевых полей.
При генерации прокси-классов (если используется ленивая загрузка)
Не влияет напрямую, но может уменьшить объем данных в кеше.
Настройки и интеграция
В hibernate.cfg.xml или application.properties:
hibernate.dynamic_insert=true # Применяется ко всем сущностям!
Но лучше использовать аннотацию для точечного контроля.
Совместимость с другими аннотациями
@DynamicUpdate – аналогично оптимизирует UPDATE.
@SelectBeforeUpdate – может конфликтовать (проверяет изменения перед UPDATE).
Когда использовать
Плюсы
Уменьшает размер SQL-запросов.
Ускоряет вставку, если много null-полей.
Полезно для таблиц с большим числом столбцов.
Минусы
Усложняет отладку (меняется SQL).
Незначительный оверхед на проверку null.
Бесполезен, если все поля обычно заполнены.
Оптимальные сценарии
Таблицы с 50+ столбцами, где большинство значений null.
Частые вставки частично заполненных объектов.
Работа с унаследованными системами, где NULL в столбцах нежелателен.
Примеры
Пример 1: Игнорирование null
@DynamicInsert
@Entity
public class Product {
@Id
private Long id;
private String name;
private Integer stock; // Часто null для новых товаров
}
При сохранении Product(id=1, name="Laptop", stock=null):
INSERT INTO product (id, name) VALUES (1, 'Laptop');
Комбинация с @ColumnDefault
@DynamicInsert
@Entity
public class Account {
@Id
private Long id;
@ColumnDefault("0")
private BigDecimal balance; // Если null, БД подставит 0
}
При Account(id=1, balance=null) в БД запишется balance=0.
Ограничения
Не работает с @GeneratedValue (Hibernate требует все поля для идентификации).
Не влияет на JPQL/HQL – только на SQL, генерируемый Hibernate.
Проблемы с кешированием – если кеш второго уровня ожидает полный объект.
#Java #Training #Hard #Spring #Hibernate #DynamicInsert
@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
@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