How to map calculated properties with JPA and Hibernate
Мой Java-бин имеет свойство childCount. Это свойство не отображается в столбце базы данных. Вместо этого он должен быть рассчитан базой данных с COUNT()
функция, работающая на соединении моего Java-бина и его потомков. Было бы еще лучше, если бы это свойство можно было рассчитывать по требованию / "лениво", но это не обязательно.
В худшем случае я могу установить свойство этого компонента с помощью HQL или Criteria API, но я бы предпочел этого не делать.
Спящий @Formula
аннотация может помочь, но я едва мог найти какую-либо документацию.
Любая помощь с благодарностью. Благодарю.
3 ответа
JPA не предлагает никакой поддержки для производного свойства, поэтому вам придется использовать расширение для конкретного поставщика. Как вы упомянули, @Formula
идеально подходит для этого при использовании Hibernate. Вы можете использовать фрагмент SQL:
@Formula("PRICE*1.155")
private float finalPrice;
Или даже сложные запросы к другим таблицам:
@Formula("(select min(o.creation_date) from Orders o where o.customer_id = id)")
private Date firstOrderDate;
куда id
это id
текущего лица.
Следующее сообщение в блоге стоит того, чтобы его прочитать: Hibernate Derived Properties - производительность и переносимость.
Без более подробной информации я не могу дать более точный ответ, но приведенная выше ссылка должна быть полезной.
Смотрите также:
- Раздел 5.1.22. Элементы столбцов и формул (документация Hibernate Core)
- Раздел 2.4.3.1. Формула (документация Hibernate Annotations)
Как объясняется в этой статье, у вас есть три варианта:
- либо вы рассчитываете атрибут, используя
@Transient
метод - Вы также можете использовать
@PostLoad
слушатель сущностей - или вы можете использовать специальный Hibernate
@Formula
аннотирование
В то время как Hibernate позволяет вам использовать @Formula, с JPA вы можете использовать обратный вызов @PostLoad для заполнения переходного свойства результатом некоторого вычисления:
@Column(name = "price")
private Double price;
@Column(name = "tax_percentage")
private Double taxes;
@Transient
private Double priceWithTaxes;
@PostLoad
private void onLoad() {
this.priceWithTaxes = price * taxes;
}
Для более сложных запросов вы можете использовать Hibernate @Formula
, как объясняется в этой статье:
@Formula(
"round(" +
" (interestRate::numeric / 100) * " +
" cents * " +
" date_part('month', age(now(), createdOn)" +
") " +
"/ 12) " +
"/ 100::numeric")
private double interestDollars;
Взгляните на Blaze-Persistence Entity Views, который работает поверх JPA и обеспечивает первоклассную поддержку DTO. Вы можете проецировать что-либо на атрибуты в Entity Views, и он даже будет использовать существующие узлы соединения для ассоциаций, если это возможно.
Вот пример сопоставления
@EntityView(Order.class)
interface OrderSummary {
Integer getId();
@Mapping("SUM(orderPositions.price * orderPositions.amount * orderPositions.tax)")
BigDecimal getOrderAmount();
@Mapping("COUNT(orderPositions)")
Long getItemCount();
}
Извлечение этого сгенерирует запрос JPQL/HQL, подобный этому
SELECT
o.id,
SUM(p.price * p.amount * p.tax),
COUNT(p.id)
FROM
Order o
LEFT JOIN
o.orderPositions p
GROUP BY
o.id
Вот запись в блоге о пользовательских провайдерах подзапросов, которая также может быть вам интересна: https://blazebit.com/blog/2017/entity-view-mapping-subqueries.html
Я пробовал это на своем коде
@Formula(value = "(select DATEDIFF(expiry_date,issue_date) from document_storage)")
private String myColumn;
ошибки, которые я получаю, когда я запускаю и отображаю свое представление, даже до того, как пытаюсь отобразить столбец на усах, выглядят примерно так
java.lang.NullPointerException
at java.base/java.lang.String$CaseInsensitiveComparator.compare(String.java:1241)
at java.base/java.lang.String$CaseInsensitiveComparator.compare(String.java:1235)
at java.base/java.util.TreeMap.getEntryUsingComparator(TreeMap.java:374)
at java.base/java.util.TreeMap.getEntry(TreeMap.java:343)
at java.base/java.util.TreeMap.get(TreeMap.java:277)
at com.mysql.cj.result.DefaultColumnDefinition.findColumn(DefaultColumnDefinition.java:182)
вопрос, который я задал, заключался в том, есть ли способ получить значения столбца Calculated с помощью JPA / Hibernate с mySQL?
Что я делаю неправильно?