วิธีแมปคุณสมบัติจากการคำนวณด้วย JPA และ Hibernate


105

Java bean ของฉันมีคุณสมบัติ childCount ที่พักแห่งนี้ไม่ได้แมปไปยังคอลัมน์ฐานข้อมูล แต่ควรคำนวณโดยฐานข้อมูลด้วยCOUNT()ฟังก์ชันที่ทำงานร่วมกับ Java bean ของฉันและลูกของมัน จะดียิ่งขึ้นถ้าคุณสมบัตินี้สามารถคำนวณได้ตามความต้องการ / "เกียจคร้าน" แต่ไม่ได้บังคับ

ในกรณีที่เลวร้ายที่สุดฉันสามารถตั้งค่าคุณสมบัติของ bean นี้ด้วย HQL หรือ Criteria API แต่ฉันไม่ต้องการ

@Formulaคำอธิบายประกอบไฮเบอร์เนตอาจช่วยได้ แต่ฉันแทบไม่พบเอกสารใด ๆ

ความช่วยเหลือใด ๆ ที่ชื่นชมอย่างมาก ขอบคุณ.

คำตอบ:


164

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ของนิติบุคคลในปัจจุบัน

โพสต์บล็อกต่อไปนี้เป็นมูลค่าการอ่าน: Hibernate มา Properties - ผลการดำเนินงานและพกพา

หากไม่มีรายละเอียดเพิ่มเติมฉันไม่สามารถให้คำตอบที่แม่นยำกว่านี้ได้ แต่ลิงก์ด้านบนน่าจะเป็นประโยชน์

ดูสิ่งนี้ด้วย:


ขอบคุณ Pascal คุณมีความคิดว่าเหตุใดสิ่งต่อไปนี้จึงใช้งานไม่ได้ @Formula (value = "(เลือก count ( ) จาก ic inner join c โดยที่ ic.category_id = c.id และ c.id = id)") public Integer getCountInternal () {return countInternal; } คำค้นหานี้ใช้ได้และฉันเห็นว่ามีการเรียกใช้ในบันทึก การแมปดูเหมือนว่า OK: main org.hibernate.cfg.Ejb3Column - สูตรการผูก (เลือก count ( ) blah blah blah แต่ countInternal ยังคงตั้งค่าเป็นค่าเริ่มต้น (-1) :( ฉันได้ลองใช้คำอธิบายประกอบฟิลด์แทนคำอธิบายประกอบ getter แต่ ก็ยังใช้ไม่ได้
Francois

ขอบคุณมันช่วยงานของฉันได้มากจริงๆ!
ตำนาน

13
@NeilMcGuigan: @Formulaคำอธิบายประกอบใช้ SQL ไม่ใช่ HQL
user1438038

7
เพิ่งได้เรียนรู้ว่าการมีวงเล็บรอบแบบสอบถามนั้นสำคัญเพียงใด มักจะลงเอยด้วยข้อยกเว้นของ SQL เนื่องจากแบบสอบถามนี้จะกลายเป็นแบบสอบถามย่อยเมื่อโหลดวัตถุโฮสต์ MySQL ไม่ชอบการเลือกแบบอินไลน์โดยไม่มีวงเล็บ
sorrymissjackson

1
ลิงก์ใหม่ไปยังบทความนี้: blog.eyallupu.com/2009/07/hibernate-derived-properties.html
Adnan

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
ฉันอัปเดตคำตอบแล้วตอนนี้คุณก็มีวิธีแก้ปัญหาสำหรับคำถามที่ซับซ้อนมากขึ้นเช่นกัน
Vlad Mihalcea

1

ดูที่Blaze-Persistence Entity Viewsซึ่งทำงานร่วมกับ JPA และให้การสนับสนุน DTO ชั้นหนึ่ง คุณสามารถฉายอะไรก็ได้ให้กับแอตทริบิวต์ภายในมุมมองเอนทิตีและจะใช้โหนดการรวมที่มีอยู่ซ้ำสำหรับการเชื่อมโยงหากเป็นไปได้

นี่คือตัวอย่างการทำแผนที่

@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 หรือไม่?

ผมทำอะไรผิดหรือเปล่า ?

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.