2009-10-26 33 views
10

使用休眠的一個挑戰是,管理類必須具有默認構造函數。問題在於,沒有明確指出類的初始化位置,並且可以檢查不變量。檢查休眠映射類中的不變量

如果一個類具有依賴於多個屬性的不變式,則類設計變得複雜。讓我們開始與假設綠色領域的設計:

public class A { 
    private int x; 
    private int y; 

    public A(int x, int y) { 
     this.x = x; 
     this.y = y; 
     checkInvariants(this.x, this.y); 
    } 

    private void checkInvariants(int x, int y) { 
     if (x + y « 0) throw new IllegalArgumentException(); 
    } 
} 

這是基本實現不符合休眠要求。在構造函數中檢查不變量。 (該checkInvariants的內容()方法不要緊它只是以說明的是,類不變量可以依賴於一個以上的財產。)

的類可以使用如下:

new A(0, 0); 
new A(-1, 0); //invalid 

要滿足休眠要求一個解決方法是添加一個私人默認構造函數使用字段訪問。 (我省略了休眠映射)。

public class H { 
    int x; 
    int y; 

    public H(int x, int y) { 
     this.x = x; 
     this.y = y; 
     checkInvariants(this.x, this.y); 
    } 

    H(){} 

    private void checkInvariants(int x, int y) { 
     if (x + y « 0) throw new IllegalArgumentException(); 
    } 
} 

這有兩大缺點: *你開始執行代碼取決於客戶(休眠)上。理想情況下,一個班級不知道其來電者。 *此變通辦法的一個具體問題是,由休眠啓動的實例是未檢查是否符合不變量。你信任從數據庫加載的數據是有問題的。即使您的應用程序是唯一使用此特定數據庫架構的應用程序,管理員也可能會隨時進行臨時更改。

的第二解決方法是在用戶代碼檢查不變量:

顯然,這使得用戶代碼更復雜容易出錯。這種設計不符合創建後實例一致的期望,並且在每次更改狀態(方法調用)後都保持一致。每個用戶都必須檢查他創建的每個實例的不變量(可能間接使用休眠)。

是否有更好的解決這個問題,就是:

  • 沒有過於複雜的
  • 沒有關於其用戶
  • 顯性知識,而不依賴於Hibernate框架?

我想一些約束必須放鬆才能得到實用的解決方案。唯一的硬約束是對hibernate框架沒有依賴性。 (域對象之外的Hibernate特定代碼是可以的)。

(只是出於好奇:有沒有支持「構造函數注入」的ORM框架?)

+0

這種規則應該由數據庫中的檢查約束,這將避免加載情況下違反該規則執行。 – araqnid 2009-10-26 18:05:20

+0

這個問題沒有成效。您試圖將永久約束強制的「任意哲學」強加到可變數據上 - 根據定義,這種數據在單個原子更新中不可更新。你不是在試圖解決一個有效的商業或設計問題,只是把BS推到臺車上。 – 2013-04-06 00:33:45

+1

@ThomasW每個人每時每刻都有糟糕的一天。乾杯! (也許它可以幫助你發現什麼是不變量。) – 2013-04-11 18:49:48

回答

5

首先,我要解決「缺點」你列出你的第一種方法:

你開始執行代碼 取決於客戶端(休眠)上。 理想情況下,一個班級不知道其 調用者。

您在這裏使用「依賴」這個詞有點快速和鬆散。 Hibernate不是一個「客戶端」;這是一個框架(作爲開發人員/架構師/你有什麼)選擇實現你的持久性。因此,您將在某處使用(並依賴於)Hibernate的代碼。也就是說,上面的域對象中存在對Hibernate的依賴關係NO。如果你願意,有一個無參數構造函數是一個語義要求;它不會引入實際的依賴關係。將Hibernate切換爲JPA/TopLink/raw jdbc /您有什麼和您將不必在域對象代碼中更改單個事物。

與此變通 一個具體問題是由 休眠啓動的情況下,不檢查,如果符合 的不變量。您信任從數據庫加載的數據 ,其中 存在問題。即使您的 應用程序是唯一使用此 特定數據庫架構的應用程序,也總是有可能由管理員更改專用 。

不必「信任」的數據(更多關於下面的內容)。但是,我認爲這個論點不具有優點。如果您要在多個應用程序中修改數據,則應該在某個公共底層執行驗證,而不是依賴每個單獨的應用程序來驗證數據。所述公共層可以是數據庫本身(在簡單情況下)或提供通用API的服務層以供多個應用程序使用。

此外,管理員進行更改的概念直接到數據庫的日常部分是完全荒謬的。如果你正在談論特殊情況(錯誤修復,你有什麼),他們應該被視爲這樣(也就是說,這種情況很可能很少發生,驗證這種「關鍵」變化的負擔在於誰做出了變更;而不是堆棧中的每個應用程序)。所有這一切說,如果你想要驗證你的對象時,他們是加載,它是相當簡單的實現。定義一個具有validate()方法的接口,並讓每個關注的域對象都實現它。您可以從以下位置調用該方法:

  1. 對象加載後您的DAO /服務。
  2. Hibernate Interceptor or Listener - 兩者都在Hibernate配置中設置;您只需執行其中一項即可檢查正在加載的對象是否實現了Valid,如果是,則調用該方法。
  3. 或者您可以使用Hibernate Validator,但是這會將您的域對象綁定到Hibernate,因爲您會註釋它們。

最後,就「構造函數注入」 - 我不知道任何支持它的框架直接。原因很簡單 - 它只對不可變的實體有意義(因爲一旦你有setter你必須處理驗證),因此意味着很多工作(處理構造函數參數映射等)淨效應幾乎爲零。事實上,如果你是有關具有不可變對象一個無參數的構造函數,你總是可以不將它們映射爲實體,而是通過HQL這確實支持constructor injection加載它們:

select new A(x, y) from ... 

更新(以解決托馬斯的評論點):

  1. 我只提到了攔截器的完整性;聽衆在這種情況下更合適。在實體完全初始化後調用PostLoadEventListener
  2. 再一次,沒有參數的構造函數不是依賴項。這是一個合同,是的,但它並不以任何方式將您的代碼綁定到Hibernate。就合同而言,它是javabean規範的一部分(事實上,限制較少,因爲構造函數不必是公共的),所以在大多數情況下,您仍然會遵循它。
  3. 訪問數據庫。 「數據庫重構和遷移很常見」 - 是的,它們是。但它只是代碼;您編寫它,測試它,運行集成測試,將其部署到生產環境。如果您的域模型對象使用某個實用程序類中編寫的某種StringUtil.compare()方法,那麼您沒有重新檢查結果,是嗎?同樣,域對象不應該檢查你的遷移腳本沒有破壞任何東西 - 你應該有適當的測試。 「做臨時查詢的能力......是其中的一個特點」 - 絕對是。 查詢。例如,在用於報告的「只讀」查詢中(即使如此,在很多情況下,通過API也更合適)。但手動數據操作非緊急 - 絕對不是。
  4. 可變性使構造函數注入不相關。我並不是說你不能擁有非默認的構造函數 - 你可以,並且你可以在你的代碼中使用它。但是如果你有setter方法,你不能使用你的構造函數進行驗證,所以不管它是否真的不重要。
  5. HQL構造函數注入和關聯。 嵌套構造不會,據我知道的工作(例如,你可以不寫select new A(x, y, new B(c, d));因此,爲了獲取你將需要檢索它們作爲SELECT子句的實體,這意味着他們需要有NO-協會參數構造自己:-)或者你可以有「主」實體構造函數,它所有需要的嵌套屬性作爲參數,並構造/內部填充協會但那交界瘋狂:-)
+0

hibernate攔截器和驗證接口的組合是一個不錯的解決方案。用戶代碼可以使用構造函數A(x,y)保持不變,並且私有構造函數+字段訪問是+驗證通過休眠來訪問。 (實施攔截不會那麼容易當的onLoad被稱爲實體未初始化。) – 2009-10-27 15:10:35

+0

相關性:您的域名代碼不是獨立於ORM框架。有一個強調的合同你必須履行與框架一起工作,這就是你所依賴的。 (合同對於不同的框架可能是一樣的,它仍然存在。) – 2009-10-27 15:11:30

+0

直接訪問數據庫:一次就夠了。您的申請與所有後果不一致。因此,我的工作是檢查所有數據是否一致(否則業務將在數據庫腳本中重複)。數據庫重構和遷移很常見,而不是一個特例。對關係數據庫執行即席查詢的能力是爲什麼仍然使用關係數據庫的一個特性。 – 2009-10-27 15:12:25

2

一種方法將基於您的「類I」,但是在第一次實際使用字段時,類本身會調用checkInvariants()。這允許在賦值期間(例如在調用setX()之後但在setY()之前)字段暫時無效。但它仍然保證只有有效的數據被使用。

你的榜樣類從未使用的值,所以我會假設值可以通過的getX()的getY()和DoSomething的()來訪問,如:

public int getX(){ 
    return x; 
} 

public int getY(){ 
    return y; 
} 

public void doSomething(){ 
    x = x + y; 
} 

你會改變它像這樣:

private boolean validated = false; 

public int getX(){ 
    if (!validated) { 
     checkInvariants(); 
    } 

    return x; 
} 

public int getY(){ 
    if (!validated) { 
     checkInvariants(); 
    } 
    return y; 
} 

public void doSomething(){ 
    if (!validated) { 
     checkInvariants(); 
    } 
    x = x + y; 
} 

public void checkInvariants() { 
    validated = true; 
    if (x + y « 0) throw new IllegalArgumentException(); 
} 

如果需要,您可以將'if(!validated)'移動到checkInvariants()中。

您也可以看看Hibernate驗證器,但它可能不符合您的框架獨立要求。 http://docs.huihoo.com/hibernate/annotations-reference-3.2.1/validator.html

3

的量子相干關係模型 對於 喘氣是至關重要的概念。由於建模關係數據 的原理的固有的 數學穩定性,我們可以完全確信 我們的 原始數據庫的任何查詢的結果的確會產生 有效的事實。 - 從書了SQL的藝術 - 斯特凡Faroult

您的數據庫應包括有效的事實與真理,你從數據庫中加載不應該要求任何額外的驗證數據,你拉的事實它從數據庫中出來應該足夠好。

但是,如果你擔心有壞的數據,那麼我建議,有一個更嚴重的問題。

我已經看到了解決方案,包括其所有數據經洗滌後,將進入真正的數據庫,輸入之前它具有任何手動修復的評論之前驗證一個臨時數據庫。

無論哪種方式,一旦你在你的數據庫數據損壞或虛假陳述,你還能相信誰呢?

+0

對不起,如果我有波折 – GDR 2009-10-27 07:39:08

+0

阿門到那(和+1) – ChssPly76 2009-10-27 21:49:53