@OneToMany @JoinTable和更新操作的Hibernate问题

时间:2012-07-17 22:53:18

标签: hibernate jpa orm hibernate-mapping jointable

我遇到了一个有点奇怪的问题;我有以下JPA映射:

@Entity
public class Location {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name = "LOCATION_ID")
  private Long id;

  @OneToMany( cascade = { CascadeType.ALL }, fetch = FetchType.EAGER )
  @JoinTable( joinColumns = { @JoinColumn( name = "LOCATION_ID" ) },
  inverseJoinColumns = { @JoinColumn( name = "ATTRIBUTE_ID"  ) } )
  private Set<Attribute> attributes;

和:

@Entity
public class Attribute implements IAttributeSupport {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name = "ATTRIBUTE_ID")
  private Long id;

  @Column(nullable = false) private String name;
  @Column(nullable = false) private String value;
  ...

我正在做一个简单的测试:

  • 保留具有少量属性的位置
  • 更改其中一个属性的名称
  • 将位置合并(使用更改的属性)

我的期望(考虑传播)是合并位置只会传播到属性,这将更新。这种情况(广泛地)发生 - 更改的属性值确实已更新,但随后在连接表中尝试新的INSERT,其中映射已存在。由于这个新的和不必要的插入,失败(如预期)是:

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '1-1' for key 'PRIMARY'

现在,罪魁祸首似乎是实现hashCodeequals的方式 - 看来,当Hibernate持久保存属性集时,集合持久性检查是否每个条目(每个属性) )需要插入:

if ( collection.needsInserting( entry, i, elementType ) )

所以,由于其中一个属性的名称确实发生了变化,现在这个被选中需要插入(这不是真的正确 - 不需要插入,只有更新) - 因此在Join中插入操作表。我当然可以使用id为equals和hashcode,但这不是Hibernate推荐的方式,而且我宁愿不这样做。 我在映射中遗漏了一些可能导致这种情况的内容吗?这是一个非常标准的映射 - 简单的一对多和简单的合并操作 - 任何使它工作的建议?

感谢任何帮助。

感谢。

尤金。

1 个答案:

答案 0 :(得分:1)

第一点:在OneToMany中,您实际上并不需要JoinTable,因为子实体会将ManyToOne关系保留回父级。我会把它拿出来。

您可能会遗漏几个注释。在父类上,将mappedBy添加到@OneToMany注释中,以指示子类的哪个成员与父项保持关联。在你的情况下,它可能是

@OneToMany(fetch = FetchType.EAGER, mappedBy = "location")

此外,有时Hibernate不能很好地使用标准的JPA级联注释(我正在处理同样的问题,并从直接的JPA转移到Hibernate级联注释)。完整补充将最终成为:

@Cascade(value = { org.hibernate.annotations.CascadeType.ALL, 
        org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
@OneToMany(fetch = FetchType.EAGER, mappedBy = "location")
private Set<Attribute> attributes;

然后在孩子中,你需要有一个回到父母的参考:

@ManyToOne
@JoinColumn(name = "location_id")
private Location location;

然后级联应该正常工作。