Як відобразити обчислені властивості за допомогою 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.