@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