Cách ánh xạ các thuộc tính được tính toán với JPA và Hibernate


104

Đậu Java của tôi có thuộc tính childCount. Thuộc tính này không được ánh xạ tới một cột cơ sở dữ liệu . Thay vào đó, nó sẽ được tính toán bởi cơ sở dữ liệu với một COUNT()hàm hoạt động trên phép nối của bean Java của tôi và các con của nó. Sẽ tốt hơn nữa nếu tài sản này có thể được tính toán theo yêu cầu / "lười biếng", nhưng điều này không bắt buộc.

Trong trường hợp xấu nhất, tôi có thể đặt thuộc tính của bean này bằng HQL hoặc API Criteria, nhưng tôi không muốn.

@FormulaChú thích Hibernate có thể hữu ích, nhưng tôi hầu như không thể tìm thấy bất kỳ tài liệu nào.

Bất kỳ trợ giúp đánh giá rất cao. Cảm ơn.

Câu trả lời:


162

JPA không cung cấp bất kỳ hỗ trợ nào cho thuộc tính dẫn xuất vì vậy bạn sẽ phải sử dụng tiện ích mở rộng dành riêng cho nhà cung cấp. Như bạn đã đề cập, @Formulalà hoàn hảo cho việc này khi sử dụng Hibernate. Bạn có thể sử dụng một đoạn SQL:

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

Hoặc thậm chí các truy vấn phức tạp trên các bảng khác:

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

Trong trường hợp ididcác tổ chức hiện hành.

Bài đăng blog sau đây rất đáng để đọc: Thuộc tính có nguồn gốc Hibernate - Hiệu suất và tính di động .

Nếu không có thêm chi tiết, tôi không thể đưa ra câu trả lời chính xác hơn nhưng liên kết trên sẽ hữu ích.

Xem thêm:


Cảm ơn Pascal. Bạn có biết tại sao phần sau không hoạt động không? @Formula (value = "(select count ( ) from ic internal join c where ic.category_id = c.id and c.id = id)") public Integer getCountInternal () {return countInternal; } Truy vấn OK và tôi thấy nó đang được chạy trong nhật ký. Ánh xạ có vẻ ổn: main org.hibernate.cfg.Ejb3Column - công thức ràng buộc (select count ( ) blah blah blah Nhưng countInternal vẫn được đặt thành giá trị ban đầu (-1) :( Tôi đã thử sử dụng chú thích trường thay vì chú thích getter, nhưng nó vẫn không hoạt động.
Francois

Cảm ơn, nó thực sự đã giúp tôi rất nhiều với nhiệm vụ của mình!
Mythul

13
@NeilMcGuigan: @FormulaChú thích sử dụng SQL, không phải HQL.
user1438038

7
Vừa mới biết tầm quan trọng của việc có dấu ngoặc đơn xung quanh truy vấn. Luôn luôn kết thúc với một Ngoại lệ SQL, vì truy vấn này tất nhiên sẽ trở thành một truy vấn con khi đối tượng máy chủ được tải. MySQL không thích lựa chọn nội tuyến không có dấu ngoặc đơn.
sorrymissjackson

1
Các liên kết mới để bài viết là: blog.eyallupu.com/2009/07/hibernate-derived-properties.html
Adnan

53

Như được giải thích trong bài viết này , bạn có ba tùy chọn:

  • hoặc bạn đang tính toán thuộc tính bằng một @Transientphương pháp
  • bạn cũng có thể sử dụng trình @PostLoadnghe thực thể
  • hoặc bạn có thể sử dụng @Formulachú thích Hibernate cụ thể

Mặc dù Hibernate cho phép bạn sử dụng @Formula , với JPA, bạn có thể sử dụng lệnh gọi lại @PostLoad để điền một thuộc tính tạm thời với kết quả của một số phép tính:

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

Đối với các truy vấn phức tạp hơn, bạn có thể sử dụng Hibernate @Formula, như được giải thích trong bài viết này :

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

7
Tuy nhiên điều này không cho phép cho các truy vấn phức tạp hơn
Hubert Grzeskowiak

2
Tôi đã cập nhật câu trả lời nên bây giờ bạn cũng có giải pháp cho các truy vấn phức tạp hơn.
Vlad Mihalcea

1

Hãy xem Blaze-Persistence Entity Views hoạt động trên JPA và cung cấp hỗ trợ DTO hạng nhất. Bạn có thể chiếu bất kỳ thứ gì lên các thuộc tính trong Entity Views và nó thậm chí sẽ sử dụng lại các nút tham gia hiện có cho các liên kết nếu có thể.

Đây là một ví dụ về ánh xạ

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

Tìm nạp điều này sẽ tạo ra một truy vấn JPQL / HQL tương tự như thế này

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

Đây là một bài đăng trên blog về các nhà cung cấp truy vấn con tùy chỉnh cũng có thể thú vị với bạn: https://blazebit.com/blog/2017/entity-view-mapping-subqueries.html


0

tôi đã thử điều này trên mã của tôi

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

những lỗi tôi gặp phải khi chạy và hiển thị chế độ xem của mình ngay cả trước khi cố gắng hiển thị cột trên ria mép là một cái gì đó như thế này

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)

câu hỏi tôi đã hỏi là có cách nào để lấy giá trị của cột Tính toán bằng JPA / Hibernate với mySQL không?

tôi đang làm gì sai

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.