2016-09-27 120 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