2010-09-09 23 views
6

我處於我們公司具有高度可配置的數據庫搜索服務的位置,對於此類服務,以編程方式配置查詢非常有用。 Criteria API功能強大,但是當我們的開發人員重構其中一個數據對象時,條件限制不會表示它們在我們運行單元測試之前已經損壞,或者更糟糕的是,它們在我們的生產環境中正常運行。最近,由於這個問題,我們有一個重構項目出乎意料地在工作時間翻了一番,項目規劃中存在一個缺口,如果我們知道需要多長時間,我們可能會採取另一種方法。如何克服Hibernate標準和示例API的侷限性?

我想使用示例API來解決此問題。如果我們在真正的POJO屬性中指定'where'條件,那麼Java編譯器可以大聲表明我們的查詢是borked的。但是,Example API中只有很多功能,並且在很多方面都受到限制。看看下面的例子

Product product = new Product(); 
product.setName("P%"); 
Example prdExample = Example.create(product); 
prdExample.excludeProperty("price"); 
prdExample.enableLike(); 
prdExample.ignoreCase(); 

在這裏,「名稱」屬性被查詢反對(其中名稱像「P%」),如果我是刪除或重命名字段「名」,我們會立即知道。但是,財產「價格」呢?它被排除了,因爲Product對象有一些默認值,所以我們將「price」屬性名稱傳遞給排除過濾器。現在,如果「價格」被刪除,這個查詢在語法上是無效的,並且直到運行時纔會知道。瘸。

另一個問題 - 如果我們添加了什麼第二where子句:

product.setPromo("Discounts up to 10%"); 

因爲調用enableLike()的,這個例子將匹配的宣傳文字「折扣高達10%」,但也「折扣高達1000萬美元」或其他任何匹配。通常,Example對象的查詢範圍修改(如enableLike()或ignoreCase())並不總是適用於每個正在檢查的屬性。

這是第三個主要問題 - 其他特殊標準呢?使用標準示例框架無法獲得價格高於10美元的每件產品。無法通過促銷,降序來訂購結果。如果產品對象加入某個製造商,則無法在相關的製造商對象上添加條件。沒有辦法在製造商的標準上安全地指定FetchMode(雖然這是一般的Criteria API的問題 - 無效提取的關係失敗,甚至更多的定時炸彈)

對於所有上述示例,您需要返回Criteria API並使用屬性的字符串表示形式進行查詢 - 同樣,消除了示例查詢的最大好處。

示例API還有什麼替代方法可以獲得我們需要的編譯時建議?

+0

這是非常有趣的東西,但stackoverflow不是一個論壇,你不會在這裏得到反饋,你的問題可能會被關閉在當前的形式。正如常見問題解答中所述,「只要您假裝自己處於危險境地:提問並回答自己的問題也是完全正確的:以問題的形式表述它」*。我建議改寫它,並將細節作爲答案發布。 – 2010-09-10 17:53:08

+0

@Pascal謝謝,我會繼續並重新編制問題 – 2010-09-10 18:46:25

+0

不客氣。並感謝張貼這個。 – 2010-09-11 03:17:21

回答

5

我的公司爲開發人員提供了可以試驗和開展寵物項目(Google)的日子,並且花了一些時間研究框架以使用示例查詢,同時解決了上述限制。我想出了一些可能對其他對示例查詢感興趣的人有用的東西。以下是使用產品示例的框架示例。

Criteria criteriaQuery = session.createCriteria(Product.class); 

Restrictions<Product> restrictions = Restrictions.create(Product.class); 
Product example = restrictions.getQueryObject(); 
example.setName(restrictions.like("N%")); 
example.setPromo("Discounts up to 10%"); 

restrictions.addRestrictions(criteriaQuery); 

下面是從問題解決中的代碼示例中的問題的嘗試 - 對「價格」字段不再存在默認值的問題,因爲該框架要求的標準進行明確設置。具有查詢範圍enableLike()的第二個問題消失了 - 匹配器僅在「名稱」字段中。

在這個框架中,問題中提到的其他問題也沒有了。這裏是示例實現。

product.setPrice(restrictions.gt(10)); // price > 10 
product.setPromo(restrictions.order(false)); // order by promo desc 
Restrictions<Manufacturer> manufacturerRestrictions 
     = Restrictions.create(Manufacturer.class); 
//configure manuf restrictions in the same manner... 
product.setManufacturer(restrictions.join(manufacturerRestrictions)); 
/* there are also joinSet() and joinList() methods 
    for one-to-many relationships as well */ 

更復雜的限制是可用的。

product.setPrice(restrictions.between(45,55)); 
product.setManufacturer(restrictions.fetch(FetchMode.JOIN)); 
product.setName(restrictions.or("Foo", "Bar")); 

展示給同事的框架後,他提到,許多數據映射對象有私人setter方法,使得這種基準設定困難以及(與實例API一個不同的問題!)。所以,我也解釋了這一點。取而代之的是使用setter,getter也是可查詢的。

restrictions.is(product.getName()).eq("Foo"); 
restrictions.is(product.getPrice()).gt(10); 
restrictions.is(product.getPromo()).order(false); 

我還添加了一些額外的檢查的對象,以確保更好的類型安全性 - 例如,相對標準(GT,GE,LE,LT)都需要一個值?爲參數擴展Comparable。另外,如果你在上面指定的樣式中使用getter,並且getter上存在@Transient註釋,它將引發運行時錯誤。

但是等等,還有更多!

如果您喜歡Hibernate的內置限制實用程序可以靜態導入,以便您可以執行諸如criteria.addRestriction(eq(「name」,「foo」))而不會使代碼真的變得冗長,那麼選項也是如此。

Restrictions<Product> restrictions = new Restrictions<Product>(){ 
     public void query(Product queryObject){ 
     queryObject.setPrice(gt(10)); 
     queryObject.setPromo(order(false)); 
     //gt() and order() inherited from Restrictions 
     }    
} 

這就是現在 - 非常感謝您提前的任何反饋!我們已經在Sourceforge上發佈了代碼,供有興趣的人使用。 http://sourceforge.net/projects/hqbe2/

+0

我喜歡它。看起來你受到各種嘲諷框架的啓發? – waxwing 2010-09-12 20:20:38

+0

@waxwing是啊我愛EasyMock :) – 2010-09-12 20:23:40

+0

你會如何將限制與或?如「年齡大於18歲或(年齡> 16歲且父母同意)」的人「? – meriton 2010-09-12 21:22:49

1

該API看起來不錯!

限制。秩(布爾)氣味像控制耦合。有點不清楚布爾參數的值是什麼。

我建議用orderAscending()和orderDescending()來代替或補充。

1

看看Querydsl。他們的JPA/Hibernate模塊需要代碼生成。他們的Java集合模塊使用代理,但目前不能與JPA/Hibernate一起使用。