【问题标题】:One To Many and Duplicate entry一对多和重复条目
【发布时间】:2011-08-15 11:45:55
【问题描述】:

我使用 JPA->Hibernate。播放框架。我想有关系。

 Category - 1:n -> Tag

每个类别可以有很多标签,但标签不知道。

所以,我喜欢这样:

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

我有测试:

@测试 公共无效 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();

但是为什么?

【问题讨论】:

    标签: database hibernate jpa playframework


    【解决方案1】:

    我已经解决了这个问题。问题在于,我使用了 NOT THAT 注释。所以我只是将@OneToMany 更改为@ManyToMany,然后瞧——不再有任何限制。

    但是,如果说到 OneToMany,那么似乎在数据库级别存在唯一限制,这阻止了我们将非唯一值放入 tags_id。因此,我们不能将相同的标签放在一个类别中。 IE。它想要许多标签的一个类别,但如果标签已经“使用” - 没办法..我试图将 unique=true/false 放入 @JoinTable -> @JoinColumn - 但它没有帮助。对我来说这仍然很奇怪,但至少当前的问题得到了解决。

    【讨论】:

      【解决方案2】:

      你混淆了两个概念:主键外键

      只能有一个 PK,但 FK 仅表示“在其他表中必须存在具有此 ID 的元素”。 FK 不限制唯一性。

      [编辑] 您的问题是您正在混合实体。你是怎么得到save()返回的tag1的?

      此实体必须是您从 Hibernate 获得的,而不是来自 new 的结果。即使看起来很疯狂,您也必须在save() 中执行此操作:

      session.save(tag);
      return session.load(tag.getId());
      

      这样,您将获得一个由 Hibernate 管理的实体。只有当实体由 Hibernate 管理时,Hibernate 才知道何时必须保存该实体以及何时已保存。

      因此,当您在上面的示例中执行cat2.tags.add(tag1); 时,Hibernate 会认为“哦,我对这个标签一无所知,它一定是一个新标签”。

      并尝试再次保存标签。

      【讨论】:

      • 是的。但我的错误是:无法添加或更新子行:外键约束失败..... FOREIGN KEY('tags_id')引用'Tag('id')'
      • 该错误表示标签未写入 TAB 表。添加标签后您是否正确提交?如果你运行一个普通的 SQL 查询,你能从数据库中读取标签吗?
      • 标签 tag1 = new Tag("tag1").save();这是由 PlayFramework 管理的。当它在这里返回 tag1 时,它已经由 hibernate 管理 - 它有 id,与 session 等相关。不,他不认为“它必须是一个新的”。不应该 - 因为 tag1 由 play 的 save() 保存并返回。
      • 是什么让您认为 tag1 与会话相关联?错误说不是。这也是merge() 解决问题的原因:它将实体附加到会话。
      • 即使我在我的 sql 编辑器中使用 SQL 子句“插入” - 如果 tags_id 将被复制,也无法将数据插入第三个表 category_tag(category_id, tags_id)。这就是为什么我认为问题不在会话中。
      猜你喜欢
      • 2022-01-19
      • 1970-01-01
      • 1970-01-01
      • 2014-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-15
      • 1970-01-01
      相关资源
      最近更新 更多