Як відобразити обчислені властивості за допомогою JPA та Hibernate


104

Мій боб Java має властивість childCount. Це властивість не відображається у стовпці бази даних . Натомість вона повинна обчислюватися базою даних з COUNT()функцією, що працює на з'єднанні мого Java-боба та його дітей. Було б навіть краще, якби цю нерухомість можна було обчислити на вимогу / "лінь", але це не обов'язково.

У гіршому випадку я можу встановити властивість цього боба за допомогою HQL або API критеріїв, але я вважаю за краще це не робити.

@FormulaАнотація зі сплячки може допомогти, але я ледве змогла знайти будь-яку документацію.

Будь-яка допомога дуже вдячна. Дякую.

Відповіді:


162

JPA не пропонує жодної підтримки похідних ресурсів, тому вам доведеться використовувати розширення, визначені провайдером. Як ви вже згадували, @Formulaідеально підходить для цього під час використання сплячки. Ви можете використовувати фрагмент 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поточне підприємство.

Наступне повідомлення в блозі варто прочитати: Влаштовані у сплячому режимі властивості - продуктивність та портативність .

Без додаткових деталей я не можу дати більш точну відповідь, але вищенаведене посилання має бути корисним.

Дивитися також:


Спасибі Паскаль. Чи маєте ви уявлення, чому наступне не працює? @Formula (value = "(виберіть count ( ) з ic внутрішнього об'єднання c, де ic.category_id = c.id і c.id = id)") public Integer getCountInternal () {return countInternal; } Запит в порядку, і я бачу, що він працює в журналах. Зображення здається нормальним: основна org.hibernate.cfg.Ejb3Column - прив'язка формули (виберіть count ( bla bla bla blah) Але countInternal залишається встановленим на початкове значення (-1) :( я спробував використовувати полеві анотації замість анотацій getter, але це все ще не працює.
— Франсуа

Дякую, це дійсно мені дуже допомогло із своїм завданням!
— Mythul

13
@NeilMcGuigan: В @Formulaанотації використовується SQL, а не HQL.
— користувач1438038

7
Щойно дізнався, як важливо мати круглі дужки навколо запиту. Завжди закінчувався винятком SQL, оскільки цей запит, звичайно, стає підзапитом при завантаженні об'єкта хоста. MySQL не сподобався вбудованому рядку без дужок.
— sorrymissjackson

1
Нове посилання на статтю: blog.eyallupu.com/2009/07/hibernate-derived-properties.html
— Аднан

53

Як пояснено в цій статті , у вас є три варіанти:

  • або ви обчислюєте атрибут за допомогою @Transientметоду
  • ви також можете використовувати @PostLoadслухач сутностей
  • або ви можете використовувати специфічну @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;
}

Для більш складних запитів ви можете використовувати сплячку @Formula, як пояснено в цій статті :

@Formula(
    "round(" +
    "   (interestRate::numeric / 100) * " +
    "   cents * " +
    "   date_part('month', age(now(), createdOn)" +
    ") " +
    "/ 12) " +
    "/ 100::numeric")
private double interestDollars;

7
Однак це не дозволяє отримати більш складні запити
— Hubert Grzeskowiak

2
Я оновив відповідь, тому тепер у вас є рішення і для більш складних запитів.
— Влад Міхалча

1

Погляньте на Blaze-Persistent 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


0

я спробував це на своєму коді

 @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)

запитання, яке я запитав , чи є спосіб отримати значення обчисленої колонки за допомогою JPA / Hibernate за допомогою mySQL?

що я роблю неправильно?

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.