使用XStream序列化Hibernate对象的问题

时间:2008-11-30 13:01:22

标签: hibernate xstream

我一直在面对这个问题,序列化上的hibernate对象产生了包含来自Hibernate的所有检测代码的意外xmls。

在序列化对象之前,我们对对象进行了一些清理。

但是,是否有可用于直接序列化对象的标准选项?

6 个答案:

答案 0 :(得分:3)

之前我没有使用过XStream,但是我已经序列化了Hibernate管理的实体。这不好玩。

有两个大问题:

  • 延迟加载;
  • 一对多的关系。

前者显而易见 - 您需要实际数据来序列化。后者不那么 - 你对集合接口声明的任何一对多关系(例如:Set<T>)将被Hibernate自己的(不可序列化的!)集合实现插入。这很可能是Hibernate的类正在流入你的对象的地方。

我最终编写了这样做的反射代码(实际上是内省的):

  1. 打开会话后,触摸整个对象图以强制加载任何已卸载的实体;
  2. 关闭Hibernate会话(包括涉及其连接的所有事务);
  3. 使用ArrayList,HashSet或HashMap(已知可序列化集合)的实例替换对象图,替换任何列表,集或映射。
  4. 请注意,第2步非常重要 - 如果您在关闭会话之前更换集合,Hibernate只会在关闭时将其自己的集合放回原位...

    编辑: @ cliff.meyers发现了我忘记提及的实现细节:如果你这样做,你需要限制对象图只走到你自己的实体,并观察循环引用路径(例如:通过缓存对已经走过的对象的引用)。

答案 1 :(得分:3)

我想出了一些足够的解决方案。在我的应用程序中,只有PersistentSets搞乱了XStream生成的XML。所以我添加了另一个转换器到XStream(它使用打开的Hibernate Session和活动对象运行):

XStream xs = new XStream();
xs.registerConverter(new CollectionConverter(xs.getMapper()) {
    @Override
    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
        org.hibernate.collection.PersistentSet ps = (PersistentSet) source;
        super.marshal(new HashSet(ps), writer, context);
    }

    @Override
    public boolean canConvert(Class type) {
        return type.isAssignableFrom(org.hibernate.collection.PersistentSet.class);
    }
}, XStream.PRIORITY_VERY_HIGH);
String s = xs.toXML(processInstance);

序列化的XML如下所示:

  <processLogs class="org.hibernate.collection.PersistentSet">
    <pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog>
      <id>813017</id>
      <entryDate>
        <time>1310832421216</time>
        <timezone>GMT</timezone>
      </entryDate>
      <eventI18NKey>process.log.action-performed</eventI18NKey>
      <additionalInfo>Wydrukuj wniosek</additionalInfo>
      <logValue>GENERATE_APPLICATION</logValue>
      <logType>PERFORM_ACTION</logType>
      <state reference="../../../definition/states/pl.net.bluesoft.rnd.processtool.model.config.ProcessStateConfiguration[8]"/>
      <processInstance reference="../../.."/>
      <user reference="../../../creator"/>
    </pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog>
    <pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog>
      <id>808211</id>
      <entryDate>
        <time>1310828206169</time>
        <timezone>GMT</timezone>
      </entryDate>
      <eventI18NKey>process.log.action-performed</eventI18NKey>
      <additionalInfo>Zaakceptuj</additionalInfo>
      <logValue>ACCEPT</logValue>
      <logType>PERFORM_ACTION</logType>
      <state reference="../../../definition/states/pl.net.bluesoft.rnd.processtool.model.config.ProcessStateConfiguration[4]"/>
      <processInstance reference="../../.."/>
      <user reference="../../../creator"/>
    </pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog>

就我而言,class属性并不重要,所以我忽略了它的值。你当然可以修补它。

答案 2 :(得分:3)

XStream现在提供了一个单独的模块/ jar来处理Hibernate集合。请参阅XStream's FAQ for an example.

答案 3 :(得分:1)

在Codehaus JIRA上有一些关于此(和示例代码)的信息:

http://jira.codehaus.org/browse/XSTR-226

我们为一些其他远程实现(Axis 1,Blaze DS等)编写了一些解决此类问题的工具。我们所做的与Dan的解决方案非常相似,尽管我们添加了声明哪些对象路径可以走路以及哪些“剪切”的能力,因为在许多情况下我们对所有数据都不感兴趣;它也会导致严重的问题,“n + 1选择”问题发生了数千次! :)我认为实现XStream转换器将是最佳方法,因为您只需要遍历对象图一次。如果你在Session上设置了FlushMode.MANUAL,那么你也应该能够在没有Hibernate做任何讨厌的情况下修改对象图。请谨慎使用,因为这是一种先进的技术。

答案 4 :(得分:1)

虽然我没有使用hibernate,但我遇到了类似的问题。我看着enitity-pruner,但它不是我想要的,因为我正在寻找一个更简单的解决方案。

我想出了一个非常简单的解决方案,它使用反射来消除CGLIB的阴影对象,如魅力。

请访问http://www.anzaan.com/2010/06/serializing-cglib-enhanced-proxy-into-json-using-xstream/例如代码。

答案 5 :(得分:1)

没有使用它,但xstream-for-beans似乎适合(引用):

  

该项目提供了在以下方面增强XStream的映射器和转换器的实现:

     
      
  1. 序列化由getter和setter公开的对象。可用于字段的XStream功能应适用于为getter / setter proprerties定义的属性。
  2.   
  3. 清理托管对象的序列化:自动省略不相关的字段和类   信息。
  4.   
  5. 处理“离线”字段和代理对象。
  6.   

我写了一次自定义XStream Converter来处理这个问题,不幸的是,它是一个闭源项目的一部分。 xstream-for-beans处理同样的问题,非常值得一试。

我过去曾成功使用过Terracotta的Pojoizer实用程序,但我不认为它已经被维护了。