So ordnen Sie berechnete Eigenschaften mit JPA und Hibernate zu

104

Meine Java-Bean hat eine childCount-Eigenschaft. Diese Eigenschaft ist keiner Datenbankspalte zugeordnet . Stattdessen sollte es von der Datenbank mit einer COUNT()Funktion berechnet werden, die für den Join meiner Java-Bean und ihrer untergeordneten Elemente ausgeführt wird. Es wäre sogar noch besser, wenn diese Eigenschaft auf Anfrage / "träge" berechnet werden könnte, aber dies ist nicht obligatorisch.

Im schlimmsten Fall kann ich die Eigenschaft dieser Bean mit HQL oder der Criteria-API festlegen, aber ich würde es vorziehen, dies nicht zu tun.

Die @FormulaAnnotation zum Ruhezustand mag helfen, aber ich konnte kaum Dokumentation finden.

Jede Hilfe sehr geschätzt. Vielen Dank.

Francois
quelle

Antworten:

161

JPA bietet keine Unterstützung für abgeleitete Eigenschaften, daher müssen Sie eine anbieterspezifische Erweiterung verwenden. Wie Sie bereits erwähnt haben, @Formulaist dies perfekt, wenn Sie den Ruhezustand verwenden. Sie können ein SQL-Fragment verwenden:

@Formula("PRICE*1.155")
private float finalPrice;

Oder sogar komplexe Abfragen in anderen Tabellen:

@Formula("(select min(o.creation_date) from Orders o where o.customer_id = id)")
private Date firstOrderDate;

Wo idist das idder aktuellen Entität.

Der folgende Blog-Beitrag ist eine Lektüre wert: Abgeleitete Eigenschaften im Ruhezustand - Leistung und Portabilität .

Ohne weitere Details kann ich keine genauere Antwort geben, aber der obige Link sollte hilfreich sein.

Siehe auch:

Pascal Thivent
quelle
Danke Pascal. Haben Sie eine Idee, warum das Folgende nicht funktioniert? @Formula (value = "(select count ( ) aus ic inner join c wobei ic.category_id = c.id und c.id = id)") public Integer getCountInternal () {return countInternal; } Die Abfrage ist in Ordnung und wird in den Protokollen ausgeführt. Die Zuordnung scheint in Ordnung zu sein: main org.hibernate.cfg.Ejb3Column - Bindungsformel (select count ( ) bla bla bla Aber countInternal bleibt auf den Anfangswert (-1) gesetzt :( Ich habe versucht, Feldanmerkungen anstelle von Getter-Anmerkungen zu verwenden, aber es funktioniert immer noch nicht.
Francois
Danke, es hat mir bei meiner Aufgabe sehr geholfen!
Mythul
13
@NeilMcGuigan: Die @FormulaAnnotation verwendet SQL, nicht HQL.
user1438038
7
Ich habe gerade gelernt, wie wichtig es ist, die Klammern um die Abfrage zu haben. Es endete immer mit einer SQL-Ausnahme, da diese Abfrage natürlich beim Laden des Host-Objekts zu einer Unterabfrage wird. MySQL mochte die Inline-Auswahl ohne Klammern nicht.
Sorrymissjackson
1
Der neue Link zum Artikel lautet: blog.eyallupu.com/2009/07/hibernate-derived-properties.html
Adnan
53

Wie in diesem Artikel erläutert , haben Sie drei Möglichkeiten:

  • Entweder berechnen Sie das Attribut mit einer @TransientMethode
  • Sie können auch den @PostLoadEntity Listener verwenden
  • oder Sie können die @FormulaAnnotation für den Ruhezustand verwenden

Während Sie im Ruhezustand die Verwendung von @Formula mit JPA ermöglichen, können Sie mit dem @ PostLoad- Rückruf eine vorübergehende Eigenschaft mit dem Ergebnis einiger Berechnungen füllen :

@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;
}

Für komplexere Abfragen können Sie den Ruhezustand verwenden @Formula, wie in diesem Artikel erläutert :

@Formula(
    "round(" +
    "   (interestRate::numeric / 100) * " +
    "   cents * " +
    "   date_part('month', age(now(), createdOn)" +
    ") " +
    "/ 12) " +
    "/ 100::numeric")
private double interestDollars;
Vlad Mihalcea
quelle
7
Dies erlaubt jedoch keine komplexeren Abfragen
Hubert Grzeskowiak
2
Ich habe die Antwort aktualisiert, sodass Sie jetzt auch eine Lösung für komplexere Abfragen haben.
Vlad Mihalcea
1

Schauen Sie sich Blaze-Persistence Entity Views an, die auf JPA aufbauen und erstklassige DTO-Unterstützung bieten. Sie können alles auf Attribute in Entitätsansichten projizieren und vorhandene Verknüpfungsknoten werden nach Möglichkeit sogar für Zuordnungen wiederverwendet.

Hier ist ein Beispiel für eine Zuordnung

@EntityView(Order.class)
interface OrderSummary {
  Integer getId();
  @Mapping("SUM(orderPositions.price * orderPositions.amount * orderPositions.tax)")
  BigDecimal getOrderAmount();
  @Mapping("COUNT(orderPositions)")
  Long getItemCount();
}

Wenn Sie dies abrufen, wird eine ähnliche JPQL / HQL-Abfrage generiert

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

Hier ist ein Blog-Beitrag über benutzerdefinierte Unterabfrageanbieter, der auch für Sie interessant sein könnte: https://blazebit.com/blog/2017/entity-view-mapping-subqueries.html

Christian Beikov
quelle
0

Ich habe dies auf meinem Code versucht

 @Formula(value = "(select DATEDIFF(expiry_date,issue_date)  from document_storage)")
    private String  myColumn;

Die Fehler, die ich beim Ausführen und Anzeigen meiner Ansicht bekomme, noch bevor ich versuche, die Spalte auf dem Schnurrbart anzuzeigen, sind ungefähr so

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)

Die Frage, die ich gestellt habe, war, ob es eine Möglichkeit gibt, Werte der berechneten Spalte mithilfe von JPA / Hibernate mit mySQL abzurufen.

Was mache ich falsch ?

bob269
quelle