与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-ORM:12.Hibernate中的多对多关联关系
Hibernate学习笔记 --- 创建基于中间关联表的多对多映射关系