如何调试“找到同一个集合的两个表示”?

我已经找到了几个 关于这个的问题 ,但没有一个问题的完整解释,以及如何调试它 – 答案都是轶事。

问题是在Play 1.2.4 JPA测试中,当我save()模型时,我得到了这个exception:

org.hibernate.HibernateException:找到了两个相同集合的表示:models.Position.projects

我想知道:

  1. 是否有关于此问题的文档,与Play无关? 这个问题处于hibernate状态,但很多Google上的结果都在Play应用中。
  2. 有哪些基本的最佳做法可以避免这个问题?
  3. 它是由Play引起的吗? 或者我做错了什么?
  4. 如何解决我的具体情况?

这是github上问题的再现 。 我有四个实体:

 @Entity public class Person extends Model { public String name; @OneToMany(cascade = CascadeType.ALL) public List positions; } @Entity public class Position extends Model { public Position(){} public Position(Company companies) { this.companies = companies; this.projects = new ArrayList(); } @OneToOne public Company companies; @ManyToOne public Person person; @OneToMany public List projects; } @Entity public class Company extends Model { public String name; } @Entity public class Project extends Model { public Project(){} public Project(String field, String status){ this.theField = field; this.status = status; } @ManyToOne public Position position; public String theField; public String status; } 

我的持久性代码:

 Company facebook = new Company(); facebook.name = "Facebook"; facebook.save(); Company twitter = new Company(); twitter.name = "Twitter"; twitter.save(); Person joe = new Person(); joe.name = "Joe"; joe.save(); joe.positions = new ArrayList(); Position joeAtFacebook = new Position(facebook); joeAtFacebook.projects.add(new Project("Stream", "Architect")); joeAtFacebook.projects.add(new Project("Messages", "Lead QA")); joe.positions.add(joeAtFacebook); Position joeAtTwitter = new Position(twitter); joeAtTwitter.projects.add(new Project("Steal stuff from Facebook", "CEO")); joe.positions.add(joeAtTwitter); joe.save(); 

顺便说一下,我已经尝试过将一个人建议添加Play关联模块 ,但它似乎没有帮助。

我确实在某种意义上,创建的表确实是重复的:

我有一个person_position表和一个position table ,其中两个都包含类似的字段: person_position包含Person_idpositions_id ,而position表包含id (意思是位置id), person_idcompanies_id 。 所以我理解我的模型定义创建了某种非预期的冗余,但我真的不明白如何解决它。

我认为这可能与双向映射有关,但这里是模型是单向的分支 (我删除了一些反向引用) – 问题仍然存在。

据我所知,错误是由以下任意组合引起的:

  • @OneToMany注释上缺少/缺少mappedBy参数。 此参数应该接收目标模型中引用该模型的字段的名称。
  • 旧的hibernate – 播放1.2.4随hibernate 3.6.1 …升级到3.6.8似乎解决了另一个这样的问题(只需将以下内容添加到dependencies.yml,并播放deps)

- org.hibernate -> hibernate-core 3.6.8.Final:

force: true

对我来说,上述步骤解决了这个问题

它实际上是hibernate中的一个错误,因为它在持久化对象时被抛出,而它实际上意味着在创建模式时应该检测到的“设计时”问题。

我用来调试的步骤:

  • 写了一个复制问题的测试
  • 添加了关联模块 – 我不确定它是否解决了问题的一部分,或者使问题变得更糟。
  • 通过hibernate代码调试,并意识到这可能表示一个hibernate问题,而不是用户/配置错误。
  • 注意到hibernate在3.6.1之后有相当多的bugfix版本,并决定尝试我的运气和升级。
  • 同样重要的是,清理tmp文件夹不会受到影响 – 在那里缓存已编译的jar文件,并且在像升级hibernate版本之类的重大更改之后,清除它可能是值得的。

尝试

  @OneToMany(mappedBy="position")    public List projects; 

首先,我认为你错过了最后一行:

 joe.positions.add(joeAtTwitter); 

第二:我认为你不应该这样做

 joe.positions = new ArrayList(); 

而是将Person更改为:

 @Entity public class Person extends Model { public String name; @OneToMany(cascade = CascadeType.ALL) public List positions = new ArrayList(); } 

它将解决您的问题,并且这是一种最佳实践,通常使用空集合而不是null值(请参阅Effective Java),特别是使用Hibernate托管对象。 请阅读此处的第一段,了解为什么您最好使用空集合进行初始化。

现在我想到的是:当你调用joe.save()你已经对对象进行了管理(通过Hibernate)然后你用新的集合覆盖了一个属性,我无法理解为什么你得到的错误是关于model.Position.projects ,但我认为就是这样。