2009-07-01 74 views
9

這是一個比實現問題更多的設計,它將會很長時間以待我。這是最好用一個例子說明: 通過服務API公開Hibernate標準

比方說,我有一個商業實體,稱爲產品了一堆屬性(價格廠商,等...)。

它通過一個接口(產品)和執行(ProductImpl,在Hibernate中映射)以及基本的CRUD服務接口(ProductService)和執行(ProductServiceImpl)表示。
產品ProductService公開爲API,但它們的實現不是。

我想添加一個列表findProducts(QueryCriteria標準)方法ProductService,將返回的產品滿足給定標準的列表。 的要求是:通過直接產品性質(例如product.price gt 50.0

  • 查詢通過關聯(例如product.vendor.name = "Oracle"
  • 排序結果(例如order by product.vendor.name desc, product.price asc"

    1. 查詢
    2. 應用附加的過濾器。與由API客戶端全部指定的上述3個項目不同,服務可以基於客戶端身份(例如,客戶端調用該方法可能僅限於查看由給定供應商製造的產品)來應用額外的過濾器。這樣的過濾器的優先級高於由客戶端指定的任何標準(例如,如果過濾器被設置爲product.vendor.name = "Microsoft",查詢在上述(2)應當產生空結果集。

    的問題,因此,是什麼應該QueryCriteria通過這樣的方法使用的界面看起來像我能想到的3個解決方案,我也不喜歡他們中的一個:?

    • 允許客戶端指定HQL(從「where」子句)直接 這是最直接的解決方案,但也是最有問題的安全方面。即使假定過濾器(上述#4)是簡單的如果足夠通過Hibernate的會話過濾器來實現,HQL仍然需要被解析爲 - 至少 - 確保查詢參數被指定爲參數而不是內聯。
    • 使用纖細包裝的Hibernate的DetachedCriteria代替QueryCriteria。 「Thinly wrapped」,因爲客戶端不能被允許直接創建DetachedCriteria將無法​​控制它創建的映射實體。 此外,這不會像HQL那樣靈活,因爲某些查詢不容易(或根本不能)通過Criteria API表示。與HQL方法一樣,過濾器(上述#4)將僅限於Hibernate會話過濾器。
    • 寫我自己的QueryCriteria接口/實現將在後臺形成DetachedCriteria或HQL。儘管可能是最靈活的解決方案,但它必須從Criteria API中複製很多代碼,這似乎不太理想。

    任何有關上述方法的有效性的評論或 - 手指交叉 - 我沒有想到的簡單優雅的解決方案將不勝感激。

    P.S.在我的具體情況中,所有的API客戶端都是內部的和「半可信的」 - 那就是我沒有太在意那些試圖故意破壞某些東西的人,因爲糟糕的編程導致了5張表的笛卡爾積:-)然而,它如果能夠提供一種能夠承受API暴露於公衆的解決方案,我們感到非常高興。

  • +0

    看起來這個問題已經持續了兩個月;我很好奇你定居在什麼地方?我想我會採取#3,「寫我自己的...」的方法,並試圖將我需要的信息「扁平化」到ProductService/criteria API中作爲可查詢屬性,具體取決於使用情況。 – RMorrisey 2009-09-22 00:20:30

    +0

    @Rorrisey - 我已經添加了一個描述我的解決方案的答案。 – ChssPly76 2009-09-22 04:04:42

    回答

    2

    我實施的實際解決方案使用混合方法。使用良好定義的查詢(例如由其他服務在內部使用的方法,預先定義的報告等)

    方法有類似的HibernateTemplate的findBy方法簽名:

    public List<Entity> findEntities(String queryName, QueryParameters parameters); 
    

    其中QueryParameters是一個便利類顯式指定命名參數或從bean中獲取它們。樣品的用法是:

    List<Product> products = findProducts("latestUpdates", 
    new QueryParameters() 
        .add("vendor", "Oracle") 
        .add("price", "50.0") 
    ); 
    

    List<Product> products = findProducts("latestUpdates", 
    new QueryParameters(product, "vendor", "price")); 
    

    訪問這樣的方法被限制爲 「受信任」 的代碼;明顯使用的查詢顯然必須在Hibernate映射中定義。過濾器構建在查詢中或定義爲會話過濾器。好處是更乾淨的代碼(沒有像遍佈半頁的標準樣東西)和明確定義的HQL(如果需要,更容易優化和處理緩存)。


    方法暴露在UI或以其他方式需要更加動態的使用Search接口從Hibernate-Generic-DAO項目。它有點類似於Hibernate的DetachedCriteria,但有幾個優點:

    1. 它可以被創建而不被綁定到特定的實體。對我來說這是個大問題,因爲實體接口(用戶可見的API的一部分)和實現(在Hibernate中映射的POJO)是兩個不同的類,編譯時用戶不能使用實現。

    2. 這是一個深思熟慮的開放式界面;與DetachedCriteria完全不同,它幾乎不可能提取任何東西(是的,我知道DC不是爲此設計的;但仍然)

    3. 內置分頁/結果總數/其他一些小細節。沒有明確的關係到Hibernate(儘管我個人並不在意這一點;我不會突然放棄Hibernate,並且明天會與EclipseLink一起使用)。有Hibernate和通用的JPA實現可用。

    可以在服務端添加過濾器;那也是指定實體類的時候。如果指定了無效的屬性名稱,並且可以通過編寫我自己的ISearch/IMutableSearch實現來解決,唯一缺少的是客戶端的快速失敗,但我還沒有完成。

    0

    公開此類實現細節絕不是一個好主意。從那裏開始,你就被這個圖書館限制了。更糟糕的是,圖書館的任何api變更都會導致我改變你的服務。任何安全考慮遺留下來...

    那麼在critera中使用的bean屬性名稱(屬性名稱的三元組,具有less,equal和more以及value的enum)如何呢?使用模型上的bean包裝器,可以將其轉換爲休眠條件。

    在模型更改後,也可以將此屬性名稱轉換爲新版本。

    +1

    我意識到你可以抽象出所有東西,但是某處存在一行 - 我確信我不會切換ORM層,我懷疑在可預見的將來,Hibernate API將以向後不兼容的方式進行更改。也就是說,我並沒有建議在上面提出的3種解決方案中的任何一種中公開直接的Hibernate API。 不確定你的意思是「模型bean包裝」,但屬性/比較/值只是一個簡單的例子。一旦添加了ANDs/ORs,屬性到屬性的比較,集合等等,您將最終重寫整個Criteria API。 – ChssPly76 2009-07-01 20:38:07

    1

    嗯 - 有趣的問題。

    經過深思熟慮,編寫你自己的標準接口可能是一條可行的路。它不會將您與實施綁定,並會降低安全問題。

    也取決於涉及多少個對象考慮返回整個產品集(應用必要的過濾器),然後讓最終用戶應用lambdaj或類似的過濾器。請參閱:

    http://code.google.com/p/lambdaj/

    +0

    謝謝。 LambdaJ是一個應用後置條件過濾器(上面#4)的有趣方法,不能通過Hibernate過濾器表示。但正如你所說,由於數據量的原因,這不會對find()方法有所幫助。 – ChssPly76 2009-07-01 21:21:34

    0

    Hibernate是一個低級別的基礎設施框架,因此應保持在幕後隱藏。如果下個月你的應用程序需要切換到另一個ORM框架出於某種原因,你的API將是無用的。甚至在相同應用程序的層之間進行封裝也非常重要。

    說了這麼多之後,我認爲你的方法應該會接收到執行搜索所需信息的抽象。我建議你創建一個Product的字段枚舉,並實現一個或兩個簡單版本的限制。

    該方法的參數可以是一個相等限制列表,另一個相對限制列表,當然還有一個順序指示器(其中一個枚舉值加上一個asc/desc標誌)。

    這只是一個大方向,我希望我做了我的觀點明確= 8)

    +0

    圖層分離與第三方庫的依賴關係(或不存在)不同。如果我的應用程序要切換ORM層,那麼這個API將是我的最後一個問題(列表遠遠低於數百個Hibernate註釋實體和數千個HQL查詢,這些查詢都必須重做)。這一點不談,你的建議是我自己的(更受限制的)DetachedCriteria重新實現與財產名稱進一步包裹在枚舉的情況下,我的產品界面需要改變下個月? :-)或者我誤解了? – ChssPly76 2009-07-01 21:42:47

    2

    方案一: 如果可以擴大你的API,我建議讓您的API「富」 - 添加更多的方法,如下面的幾個,使您的服務聽起來更自然。在不增加看起來臃腫的情況下讓API變大可能會非常棘手,但如果您遵循類似的命名方案,那麼使用起來似乎很自然。

    productService.findProductsByName("Widget") 
    productService.findProductsByName(STARTS_WITH,"Widg") 
    productService.findProductsByVendorName("Oracle") 
    productService.findProductsByPrice(OVER,50) 
    

    結合的結果(應用多個限制)可以留下的東西的客戶,他們收到的結果通過使用CollectionUtils和謂詞設置後做。你甚至可以爲你的API的消費者建立一些常見的謂詞,只是爲了好。 CollectionUtils.select()很有趣。

    選項二:如果無法擴展API,那麼您的第三個項目符合我的要求。

    • 寫我自己的QueryCriteria接口/實現將形成兩種或的DetachedCriteria HQL幕後...

    你可以嘗試使用的東西到DSL式的方法應用到命名類似於Builder模式,使事情更具可讀性和自然的聲音。這得到Java中的一個有點笨拙的所有點和括號,但也許是這樣的:

    Product.Restriction restriction = new Product.Restriction().name("Widget").vendor("Oracle").price(OVER,50)); 
    productService.findProducts(restriction); 
    

    方案三:將二者結合起來的辦法,具有更豐富的API一起提供的限制式的標準。這些解決方案將是乾淨的,因爲它們隱藏了API的使用者的Hibernate實現細節。 (並不是說任何人都會想到從休眠狀態切換到休眠狀態。)

    +0

    謝謝你的回答。雖然豐富的API適用於「已知」客戶端(例如具有可預測/已知查詢的其他服務),但它不適用於UI由用戶任意指定標準/排序順序的用戶。一旦涉及到關聯/集合,QBE風格的查詢會快速地處理完整的DetachedCriteria API,因此這也不是答案。所以共識確實似乎在重塑這裏的輪子,是吧?我真的不喜歡那樣:-( – ChssPly76 2009-07-01 21:34:34