2014-07-04

在 Hibernate Criteria 裡使用 SQL Function

想要執行類似以下的 SQL 語法。
select * from book order by length(title) asc;
在 Hibernate Criteria 裡要怎麼使用呢?在 Criteria、Property 與 Projections 裡怎麼也找不到相關的 API,最後 Google 才說,要用 @Formula 建立虛擬欄位。

Hibernate 提供的 @Formula 有兩種用途,第一是使用資料庫的運算,而非 JVM,第二是虛擬欄位,也就是不存在資料庫中的欄位,由其他欄位計算產生出來的結果。
@Entity
@SuppressWarnings("serial")
public class Book implements Serializable {

  private Integer uid;
  private String title;
  private int titleLength;
  private double price;
  private double priceWithTax;

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  public Integer getUid() {
    return this.uid;
  }

  public void setUid(Integer uid) {
    this.uid = uid;
  }

  @Column
  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  @Formula("length(title)")
  public int getTitleLength() {
    return this.titleLength;
  }

  // @Formula 為唯讀欄位
  private void setTitleLength(int titleLength) {
   this.titleLength = titleLength;
  }

  @Column
  public double getPrice() {
    return this.price;
  }

  public void setPrice(double price) {
    this.price = price;
  }

  @Formula("price * 1.05")
  public double getPriceWithTax() {
    return this.priceWithTax;
  }

  // @Formula 為唯讀欄位
  private void setPriceWithTax(double priceWithTax) {
   this.priceWithTax = priceWithTax;
  }
}
以上範例中的 uid、title 與 price 都是傳統的實際欄位對應,而 titleLength  則是虛擬欄位,由 SQL 的  length function 產生,另外 priceWithTax 則是借重資料庫的計算能力,當然也可以交給 JVM 去算。
@Transient
public double getPriceWithTax() {
  return this.price * 1.05;
}
@Formula 可以很複雜到用 Subselect 都沒問題,只是要擔心執行效能,由於每次撈資料都會執行,即使在程式中沒用到這個虛擬欄位,透過 Hibernate 從資料庫裡出來時就會跑一次。

最後還是說一下一開始問題的解答,雖然不值得一提。
Criteria c = this.getSession().createCriteria(Book.class);
c.addOrder(Order.asc("titleLength"));
---
---
---

沒有留言:

張貼留言