2016-09-27 158 views
2

我知道,當使用Wicket和JPA框架時,不建議序列化已經持久化到數據庫的實體(因爲懶惰字段的問題並節省空間) 。在這種情況下,我們應該使用LoadableDetachableModel。但是下面的用例呢?Wicket - 持久化和非持久JPA實體的序列化

假設我們要創建一個新的實體(比如合同),它將包含持久化實體(比如說,從存儲在數據庫中的客戶端列表中選擇的客戶端)等等。正在創建的實體是某個Wicket組件的模型對象(例如,嚮導)。最後(當我們完成我們的嚮導時),我們將新實體保存到數據庫。所以我的問題是:這種模型對象的序列化問題最好的通用解決方案是什麼?我們不能使用LDM,因爲實體不在數據庫中,但我們不希望我們的內部實體(如客戶端)也被完全序列化。

我的想法是實現一個自定義檢票序列化程序,該程序檢查對象是否爲實體並且是否持久。如果是這樣,只存儲它的id,否則使用默認序列化。同樣,反序列化時使用存儲的id並從DB獲取實體或使用默認機制反序列化。但是,不確定如何以通用的方式做到這一點。我的下一個想法是,如果我們能做到這一點,那麼我們不再需要任何LDM,我們可以將所有實體存儲在簡單的org.apache.wicket.model.Model模型中,並且我們的序列化邏輯將會照顧它們,對?

下面是一些代碼:

@Entity 
    Client { 
    String clientName; 

    @ManyToOne(fetch = FetchType.LAZY) 
    ClientGroup group; 
    } 

    @Entity 
    Contract { 
    Date date; 

    @ManyToOne(fetch = FetchType.LAZY) 
    Client client; 
    } 

    ContractWizard extends Wizard { 
    ContractWizard(String markupId, IModel<Contract> model) { 
     super(markupId); 
     setDefaultModel(model); 
    } 
    } 

    Contract contract = DAO.createEntity(Contract.class); 
    ContractWizard wizard = new ContractWizard("wizard", ?); 

如何通過合同?如果我們只是說Model.of(合同),整個合同將與內部客戶端一起序列化(並且它可能很大),而且如果我們在反序列化後訪問contract.client.group,我們可能碰到問題:https://en.wikibooks.org/wiki/Java_Persistence/Relationships#Serialization.2C_and_Detaching

所以我想知道人們如何解決這些問題,我相信這是一個相當普遍的問題。

+0

幾乎相同的問題(不同的觀點)https://stackoverflow.com/questions/7070644/how-do-i-keep-entities-or-their-associations-attached-to-the-current-persisten - –

回答

0

我想有2個方法問題的方法:。

a)只保存用戶實際模型中看到的東西。在你的例子中,可能是「contractStartDate」,「contractEndDate」,clientIds列表。如果您不希望數據庫對象在您的視圖中,那麼這是主要方法。

b)編寫你自己的LoadableDetachableModel並確保你只能序列化瞬態對象。例如,如:(假設任何負面的id沒有被保存到數據庫中)

public class MyLoadableDetachableModel extends LoadableDetachableModel { 

private Object myObject; 

private Integer id; 

public MyLoadableDetachableModel(Object myObject) { 
    this.myObject = myObject; 
    this.id = myObject.getId(); 
} 

@Override 
protected Object load() { 
    if (id < 0) { 
     return myObject; 
    } 

    return myObjectDao.getMyObjectById(id); 
} 

@Override 
protected void onDetach() { 
    super.onDetach(); 
    id = myObject.getId(); 

    if (id >= 0) { 
     myObject = null; 
    } 
} 
} 

這樣做的缺陷是你必須讓你的DatabaseObjects Serializable這是不甚理想,並可能導致所有類型的問題。您還需要使用ListModel將對其他實體的引用與瞬態對象分離。

與兩種方法合作後,我個人比較喜歡第一種。從我的發現來看,整個注射道對象都會導致災難。 :)我只會使用這個視圖,只是不太大的項目。

+0

嗯,我理解第一種方法,但在我們的項目中,數據庫對象已經被廣泛使用,它們也都是可序列化的。至於第二種方法,我不太明白你的意思是如何處理內部持久實體。以場地爲基礎來做?我們每次都會得到一個新的LDM實現,對吧?如何使用更通用的方法,就像我提到的使用自定義序列化程序的方法? – koszek

+0

對於每個實體,您並不需要新的LDM,但您需要將內部持久化實體不直接存儲在實體中,而是存儲在單獨的模型中(例如LoadableDetachableListModel)。但是如果實際上需要更改視圖中內部持久性字段的值,則只需要這樣做。 –

0

我知道的大多數項目只是接受序列化參考實體(例如您的客戶端)以及編輯的實體(合同)。

對於具有複雜實體關係的應用程序,使用會話(保持Hibernate/JPA會話在多個請求上打開)是一個不錯的選擇:Hibernate會話及其實體與頁面保持分離,並且永遠不會被序列化。該組件只保留一個標識符來獲取其對話。

+1

這就是我們所做的。但我開始懷疑這是否是一個好主意。它有兩個問題。首先,JPA實體可能很大,我們希望我們的頁面儘可能輕。其次,有一個[衆所周知的問題](https://en.wikibooks.org/wiki/Java_Persistence/Relationships#Serialization.2C_and_Detaching)與延遲取回的字段。所以看起來Thorsten Wendelmuth的第一種方法是要走的路?.. – koszek

+0

恕我直言,Thorsten建議的解決方案a)如果您有許多實體並且想要編輯它們之間的關係,那麼它太複雜了。 Google針對「wicket cdi對話」來了解我的替代解決方案。 – svenmeier

+0

我理解你的想法,但你能解釋在解決方案中解決懶惰字段的問題嗎?如果用戶在瀏覽器中按下「後退」按鈕,並且在代碼中某個懶惰字段被訪問但在序列化發生之前未訪問過的地方,則不希望用戶看到錯誤頁面,對嗎? – koszek