Cara memetakan properti yang dihitung dengan JPA dan Hibernate

104

Kacang Java saya memiliki properti childCount. Properti ini tidak dipetakan ke kolom database . Sebaliknya, itu harus dihitung oleh database dengan COUNT()fungsi yang beroperasi pada gabungan kacang Java saya dan anak-anaknya. Akan lebih baik lagi jika properti ini dapat dihitung sesuai permintaan / "malas", tetapi ini tidak wajib.

Dalam skenario kasus terburuk, saya dapat mengatur properti kacang ini dengan HQL atau API Kriteria, tetapi saya memilih untuk tidak melakukannya.

@FormulaAnotasi Hibernate dapat membantu, tetapi saya hampir tidak dapat menemukan dokumentasi apa pun.

Setiap bantuan sangat dihargai. Terima kasih.

Francois
sumber

Jawaban:

162

JPA tidak menawarkan dukungan apa pun untuk properti turunan sehingga Anda harus menggunakan ekstensi khusus penyedia. Seperti yang Anda sebutkan, @Formulasangat cocok untuk ini saat menggunakan Hibernate. Anda dapat menggunakan fragmen SQL:

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

Atau bahkan kueri kompleks di tabel lain:

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

Di mana idadalah identitas saat ini.

Posting blog berikut ini layak untuk dibaca: Hibernate Derived Properties - Performance and Portability .

Tanpa detail lebih lanjut, saya tidak dapat memberikan jawaban yang lebih tepat tetapi tautan di atas seharusnya dapat membantu.

Lihat juga:

Pascal Thivent
sumber
Terima kasih Pascal. Apakah Anda tahu mengapa hal berikut ini tidak berfungsi? @Formula (value = "(pilih count ( ) dari ic inner join c di mana ic.category_id = c.id dan c.id = id)") public Integer getCountInternal () {return countInternal; } Kueri OK dan saya melihatnya dijalankan di log. Pemetaan tampaknya OK: main org.hibernate.cfg.Ejb3Column - rumus penjilidan (pilih count ( ) bla bla bla Tapi countInternal tetap disetel ke nilai awal (-1) :( Saya telah mencoba menggunakan anotasi bidang alih-alih anotasi pengambil, tapi itu masih tidak berhasil.
Francois
Terima kasih, itu sangat membantu saya dengan tugas saya!
Mythul
13
@NeilMcGuigan: @FormulaAnotasi menggunakan SQL, bukan HQL.
pengguna1438038
7
Baru saja mempelajari betapa pentingnya memiliki tanda kurung di sekitar kueri. Selalu berakhir dengan Pengecualian SQL, karena kueri ini tentu saja menjadi subkueri saat objek host dimuat. MySQL tidak menyukai pemilihan sebaris tanpa tanda kurung.
sorrymissjackson
1
Tautan baru ke artikel tersebut adalah: blog.eyallupu.com/2009/07/hibernate-derived-properties.html
Adnan
53

Seperti yang dijelaskan dalam artikel ini , Anda memiliki tiga opsi:

  • baik Anda menghitung atribut menggunakan @Transientmetode
  • Anda juga dapat menggunakan @PostLoadpendengar entitas
  • atau Anda dapat menggunakan @Formulapenjelasan khusus Hibernate

Meskipun Hibernate memungkinkan Anda menggunakan @Formula , dengan JPA, Anda dapat menggunakan callback @PostLoad untuk mengisi properti transien dengan hasil dari beberapa kalkulasi:

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

Untuk pertanyaan yang lebih kompleks, Anda dapat menggunakan Hibernate @Formula, seperti yang dijelaskan di artikel ini :

@Formula(
    "round(" +
    "   (interestRate::numeric / 100) * " +
    "   cents * " +
    "   date_part('month', age(now(), createdOn)" +
    ") " +
    "/ 12) " +
    "/ 100::numeric")
private double interestDollars;
Vlad Mihalcea
sumber
7
Namun ini tidak memungkinkan untuk pertanyaan yang lebih kompleks
Hubert Grzeskowiak
2
Saya memperbarui jawabannya jadi sekarang Anda memiliki solusi untuk pertanyaan yang lebih kompleks juga.
Vlad Mihalcea
1

Lihat Blaze-Persistence Entity Views yang bekerja di atas JPA dan menyediakan dukungan DTO kelas satu. Anda dapat memproyeksikan apa saja ke atribut dalam Tampilan Entitas dan bahkan akan menggunakan kembali node gabungan yang ada untuk pengaitan jika memungkinkan.

Berikut adalah contoh pemetaan

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

Mengambil ini akan menghasilkan kueri JPQL / HQL yang mirip dengan ini

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

Berikut adalah posting blog tentang penyedia subquery khusus yang mungkin menarik bagi Anda juga: https://blazebit.com/blog/2017/entity-view-mapping-subqueries.html

Christian Beikov
sumber
0

saya telah mencoba ini pada kode saya

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

kesalahan yang saya dapatkan ketika saya menjalankan dan menampilkan tampilan saya bahkan sebelum mencoba menampilkan kolom pada kumis adalah sesuatu seperti ini

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)

pertanyaan yang saya ajukan adalah apakah ada cara untuk mendapatkan nilai kolom Terhitung menggunakan JPA / Hibernate dengan mySQL?

apa yang saya lakukan salah?

bob269
sumber