【问题标题】:Wicket - Serialization of persisted and non-persisted JPA entitiesWicket - 持久化和非持久化 JPA 实体的序列化
【发布时间】:2016-09-27 07:58:09
【问题描述】:

我知道在将 Wicket 与 JPA 框架一起使用时,不建议序列化已经持久化到数据库的实体(因为惰性字段存在问题并节省空间)。在这种情况下,我们应该使用 LoadableDetachableModel。但是下面的用例呢?

假设我们要创建一个新实体(例如,合同),其中包括持久实体(例如,从存储在数据库中的客户端列表中选择的客户端)。正在创建的实体是某个 Wicket 组件(例如,向导)的模型对象。最后(当我们完成向导时)我们将新实体保存到数据库中。所以我的问题是:这种模型对象的序列化问题的最佳通用解决方案是什么?我们不能使用 LDM,因为实体还没有在数据库中,但我们不希望我们的内部实体(如客户端)也被完全序列化。

我的想法是实现一个自定义检票口序列化程序,以检查对象是否为实体以及它是否被持久化。如果是,则仅存储其 id,否则使用默认序列化。同样,反序列化时使用存储的 id 并从数据库中获取实体或使用默认机制进行反序列化。但是,不确定如何以通用方式做到这一点。我的下一个想法是,如果我们能做到,那么我们就不再需要任何 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) 整个合约将与内部客户端一起序列化(并且它可能很大),而且如果我们在反序列化后访问 contract.client.group 我们会遇到问题:https://en.wikibooks.org/wiki/Java_Persistence/Relationships#Serialization.2C_and_Detaching

所以我想知道人们是如何解决这些问题的,我敢肯定这是一个相当普遍的问题。

【问题讨论】:

标签: java jpa serialization wicket


【解决方案1】:

我想有两种方法可以解决您的问题:

a.) 仅保存用户在模型中实际看到的内容。在您的示例中,可能是“contractStartDate”、“contractEndDate”、clientIds 列表。如果您不希望您的数据库对象出现在您的视图中,这是主要的方法。

b.) 编写您自己的 LoadableDetachableModel 并确保您只序列化瞬态对象。比如like:(假设任何负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 将对其他实体的引用与瞬态对象分离。

在使用这两种方法后,我个人更喜欢第一种。根据我的经验,将道物注入检票口可能会导致灾难。 :) 我只会在不太大的仅查看项目中使用它。

【讨论】:

  • 嗯,我理解第一种方法,但在我们的项目中,数据库对象已经被广泛使用,它们也都是可序列化的。至于第二种方法,我不太明白你的意思是如何处理内部持久实体。逐个字段进行吗?我们每次都会得到一个新的 LDM 实现,对吧?更通用的方法怎么样,比如我提到的使用自定义序列化程序的方法?
  • 您实际上并不需要为每个实体创建一个新的 LDM,但您需要将内部持久实体存储在单独的模型中而不是直接存储在实体中(例如 LoadableDetachableListModel)。但是,只有在您确实需要更改视图中的内部持久字段的值时,您才需要这样做。
【解决方案2】:

我知道的大多数项目只接受序列化引用的实体(例如您的客户)以及已编辑的实体(合同)。

对于具有复杂实体关系的应用程序,使用对话(保持 Hibernate/JPA 会话在多个请求中打开)是一个不错的选择: Hibernate 会话及其实体与页面保持分离,并且从不序列化。该组件只保留一个标识符来获取其对话。

【讨论】:

  • 这也是我们所做的。但我开始怀疑这是否是个好主意。它有两个问题。首先,JPA 实体可能很大,我们希望我们的页面尽可能轻。其次,有一个 well-known problem 具有延迟获取的字段。所以看起来Thorsten Wendelmuth的第一种方法是要走的路吗?..
  • 恕我直言,Thorsten 建议的解决方案 a) 如果您有许多实体并且想要编辑它们之间的关系,那么它就太复杂了。谷歌搜索“wicket cdi conversation”,了解我的替代解决方案。
  • 我理解你的想法,但你能解释一下你的解决方案是如何解决惰性字段问题的吗?如果您的用户在浏览器中按下“后退”按钮,并且在代码中的某处访问了在序列化发生之前未访问过的惰性字段,您不希望您的用户看到错误页面,对吗?
  • 实体没有序列化,请看我编辑的答案。
  • 抱歉,我不认为我完全理解这将如何工作。我在我的问题中添加了一些代码来澄清它。你能用代码解释一下你的想法吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-22
  • 1970-01-01
  • 1970-01-01
  • 2011-06-07
  • 1970-01-01
  • 2013-11-06
  • 2021-05-06
相关资源
最近更新 更多