我使用JPA-> Hibernate。 PlayFramework。我想要有关系。
Category - 1:n -> Tag
每个类别都有很多标签,但标签不知道它。
所以,我喜欢这样:
@Entity public class Category ... { @OneToMany public List<Tag> tags = new LinkedList<Tag>(); }
我有测试:
@Test public void playWithTags(){
Tag tag1 = new Tag("tag1").save(); // managed by playframework
Category cat1 = new Category("cat1");
cat1.tags.add(tag1);
cat1.save();
// check if tag1 and cat1 were saved
assertEquals(1, Tag.count());
assertEquals(1, Category.count());
Category cat2 = new Category("cat2");
cat2.tags.add(tag1);
cat2.save();
}
结果是:
16:18:01,555 ERROR ~ Duplicate entry '1' for key 'tags_id'
16:18:01,555 ERROR ~ Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelp
....
java:908)
at java.lang.Thread.run(Thread.java:619)
Caused by: java.sql.BatchUpdateException: Duplicate entry '1' for key 'tags_id'
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:2020)
似乎cat2.save()尝试做的更多然后应该
如果使用merge()而不是save(),那么效果很好:
cat2.merge();
但是为什么?
答案 0 :(得分:6)
我已经解决了这个问题。问题在于,我没有使用那个注释。所以我只是将@OneToMany改为@ManyToMany而且vo更改了 - 没有任何限制了。
但是如果说OneToMany然后似乎在数据库级别上有一个独特的限制,这阻止我们将不唯一的值放到tags_id上。因此,我们无法将相同的标记放在一个类别中。即它想要许多标签的一个类别,但如果标签已经“使用” - 没办法..我试图在@JoinTable中放置unique = true / false - &gt; @JoinColumn - 但它没有帮助。对我来说,它仍然很奇怪,但至少目前的问题是固定的。
答案 1 :(得分:5)
你混淆了两个概念:主要键和外来键。
只有一个PK,但FK只是意味着“在其他一些表中必须有一个带有此ID的元素”。 FK并不限制唯一性。
[编辑]你的问题是你正在混合实体。您是如何获得tag1
返回的save()
的?{/ p>
此实体必须是您从Hibernate获得的实体,而不是new
的结果。即使看起来很疯狂,也必须在save()
:
session.save(tag);
return session.load(tag.getId());
这样,您就可以获得由Hibernate管理的实体。只有当实体由Hibernate管理时,Hibernate才知道何时需要保存实体以及何时保存实体。
因此,当您在上面的示例中执行cat2.tags.add(tag1);
时,Hibernate会认为“哦,我对此标记一无所知,它必须是新标记”。
并尝试再次保存标记。