与Hibernate的多对多关系的循环序列化

Posted

技术标签:

【中文标题】与Hibernate的多对多关系的循环序列化【英文标题】:Cyclic serialisation with Many to Many relationship with Hibernate 【发布时间】:2010-02-08 05:07:27 【问题描述】:

我有一个父母(程序)pojo,与他们的孩子(订阅者)有多对多的关系。

问题是当它序列化一个程序时,它也会序列化程序的订阅者,这涉及到序列化他们的程序,这涉及到序列化他们的订阅者,直到它序列化数据库中的每个程序和订阅者。

ERD 看起来像:程序 订阅者

这意味着返回的 17KB 数据块 (json) 变成了 6.9MB。因此反过来又会浪费时间序列化数据然后返回。

为什么我的父母送孩子回来父母送孩子?我怎样才能阻止这种情况,以便我只获得每个程序的订阅者?我假设我的注释做错了,也许?我想保持多对多关系,但没有这种深度嵌套的数据检索。

(注意:我之前尝试过添加尽可能多的懒惰注释,看看是否有帮助。它没有。也许我也做错了?)

Program.java

@Entity
@Table(name="programs")
public class Program extends Core implements Serializable, Cloneable 
   ...
   @ManyToMany()
   @JoinTable(name="program_subscribers",
         joinColumns=@JoinColumn(name="program_uid"),
         inverseJoinColumns=@JoinColumn(name="subscriber_uid"))
   public Set<Subscriber> getSubscribers()  return subscribers; 
   public void setSubscribers(Set<Subscriber> subscribers)  this.subscribers = subscribers; 

Subscriber.java

@Entity
@Table(name="subscribers")
public class Subscriber extends Core implements Serializable 
   ...
   @ManyToMany(mappedBy="subscribers")
   public Set<Program> getPrograms()  return programs; 
   public void setPrograms(Set<Program> programs)  this.programs = programs; 

实施

public Collection<Program> list() 
  return new Programs.findAll();

【问题讨论】:

【参考方案1】:

您没有提到您用于 JSON 序列化的框架,所以我假设为 JAXB。无论如何,这个想法是以某种方式使Subscriber.getPrograms(..) 瞬态,这样它就不会被序列化。 Hibernate 会处理这些“循环”,但其他的则不会。所以:

@XmlTransient
@ManyToMany(..)
public Set<Program> getPrograms()...

如果您使用其他框架,它可能有不同的注释/配置来指定瞬态字段。就像transient 关键字一样。

另一种方法是自定义映射器以手动处理循环,但这很乏味。

【讨论】:

这是一个交给我的项目,我不是 100% 用于 JSON 序列化的东西。我看到对 com.sdicons.json.mapper.JSONMapper.SimpleMapperHelper 的引用。我们有一个方法 public JSONValue toJSON(Object pojo) throws MapperException 。 (猜猜这是你自己的序列化) 另外,如果我注释 getPrograms() @XmlTransient 这是否意味着我永远无法返回订阅者程序? 是的。另一种是自定义映射器以手动处理循环。【参考方案2】:

1) “你的”序列化是如何工作的。我的意思是它是 JAXB 还是自定义序列化或其他。 2)几乎所有的框架都让你设置序列化的深度。我的意思是你可以在 2 中设置例如深度。 3)我建议你不要序列化有孩子的对象,标记他们(孩子)瞬态,并单独序列化。

【讨论】:

【参考方案3】:

使用注解怎么样? http://thinkinginsoftware.blogspot.com/2010/08/json-and-cyclical-references.html

【讨论】:

【参考方案4】:

来自龙目岛图书馆。或者覆盖equals和hashcode。仅在哈希码内部使用唯一字段(例如 id)。

@EqualsAndHashCode(callSuper = false, of = "id")

【讨论】:

【参考方案5】:

Bozho 和 ponkin 都在正确的轨道上。我需要停止对数据进行在线序列化,但最大的问题是我无法更改发生序列化的 pojo -> toJSON 类/方法。我还担心在 toJSON() 方法上投入时间,考虑到我在序列化时受到了如此大的性能影响,我希望在获得数据之前而不是之后进行修复。

另外,由于我列出的多对多双向设计的性质,我总是会遇到这个循环程序/订阅者/程序/...问题。

解决方案:(至少现在)我删除了 Subscriber.getProgram() 方法并在 ProgramDAO 上创建了一个查找器方法,该方法返回订阅者的程序。

public List<Program> findBySubscriber(Subscriber subscriber) 
  String hql = "select p " +
     "from Program p " +
     " join p.subscribers s " +
     "where s = :sub"
     ;    

  Query q = getSession().createQuery(hql);
  q.setEntity("sub", subscriber);

  List<Program> l = q.list();
  return l;

对于任何 CRUD 工作,我认为我只需要遍历 Programs.getSubscribers,或者编写更多 hql 辅助方法。

【讨论】:

以上是关于与Hibernate的多对多关系的循环序列化的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate的多对多关联关系

Hibernate-ORM:12.Hibernate中的多对多关联关系

Hibernate的多对多关系

Hibernate学习笔记 --- 创建基于中间关联表的多对多映射关系

使用 JPA 和 Hibernate 提供程序的多对多关系不会创建主键

(十三)Hibernate中的多表操作:单向多对多