一对多和重复条目

ses*_*ses 0 database hibernate jpa playframework

我使用JPA-> Hibernate.PlayFramework.我想要有关系.

 Category - 1:n -> Tag
Run Code Online (Sandbox Code Playgroud)

每个类别都可以有许多标签,但标签不知道它.

所以,我喜欢这样:

@Entity
public class Category ... {
    @OneToMany
    public List<Tag> tags = new LinkedList<Tag>();
}
Run Code Online (Sandbox Code Playgroud)

我测试过:

@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();

}
Run Code Online (Sandbox Code Playgroud)

结果是:

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)
Run Code Online (Sandbox Code Playgroud)

似乎cat2.save()尝试做更多然后它应该

如果使用merge()而不是save(),它的效果很好:

 cat2.merge();
Run Code Online (Sandbox Code Playgroud)

但为什么?

ses*_*ses 6

我已经解决了这个问题.问题在于,我没有使用那个注释.所以我只是将@OneToMany改为@ManyToMany而且vo更改了 - 没有任何限制了.

但是如果说OneToMany然后似乎在数据库级别上有一个独特的限制,这阻止我们将不唯一的值放入tags_id.因此,我们无法将相同的标记放在一个类别中.即它想要许多标签的一个类别,但如果标签已经"使用" - 没办法..我试图在@JoinTable中放置unique = true/false - > @JoinColumn - 但它没有帮助.对我来说,它仍然很奇怪,但至少目前的问题是固定的.


Aar*_*lla 5

你混淆了两个概念:键和键.

只有一个PK,但FK只是意味着"在其他一些表中必须有一个带有此ID的元素".FK不限制唯一性.

[编辑]你的问题是你正在混合实体.你是如何得到tag1这是由返回save()

这个实体必须是你从Hibernate获得的实体,而不是来自的实体new.即使它看起来很疯狂,你必须这样做save():

session.save(tag);
return session.load(tag.getId());
Run Code Online (Sandbox Code Playgroud)

这样,您就可以获得由Hibernate管理的实体.只有当实体由Hibernate管理时,Hibernate才知道它何时必须保存实体以及何时已经保存.

所以当你cat2.tags.add(tag1);在上面的例子中做的时候,Hibernate认为"哦,我对这个标签一无所知,它必须是一个新标签".

并尝试再次保存标记.