【问题标题】:Hibernate - ConstraintViolation when persisting entityHibernate - 持久化实体时的 ConstraintViolation
【发布时间】:2014-12-04 16:19:38
【问题描述】:

我有一个实体,它以我以前从未遇到过的方式包含与另一个实体的关系,并且我遇到了一个异常:“org.hibernate.exception.ConstraintViolationException: could not execute statement”。

父实体称为“Post”。一篇文章可以包含多个关键字实体。关键字实体的值是唯一的,也就是说,如果两个帖子包含相同的关键字,则两个帖子都引用相同的关键字实体。

我的想法是,帖子很多,每个帖子引用很多关键字,任何一个关键字都可以被多个帖子引用,所以应该是@ManyToMany关系。显然,它不起作用。检查数据库表明它在开始失败之前成功地保留了一些帖子。只要所有关键字都是唯一的,它似乎就可以了,但我认为,每当它试图使用已被另一个帖子引用的关键字来持久化帖子时,它就会死去。不知道如何解决这个问题。

这是类的样子(短版):

帖子:

@Entity
public class Post implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "post_id_seq")
    @SequenceGenerator(name = "post_id_seq", sequenceName = "post_id_seq", allocationSize = 1)
    private Long id;

    @ElementCollection(fetch = FetchType.EAGER)
    @ManyToMany(cascade = CascadeType.ALL)
    private Set<Keyword> keywords = new HashSet<>();
}

关键字:

@Entity
public class Keyword implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "keyword_id_seq")
    @SequenceGenerator(name = "keyword_id_seq", sequenceName = "keyword_id_seq", allocationSize = 1)
    private Long id;

    @Column(name = "KEYWORD_VALUE")
    private String value;
    private int count = 1;
}

更新:

这是我在服务类中用于向帖子添加关键字的代码。基本上,我已经有一个 Post 对象,其中填充了关键字(请求通过 AJAX 从 Web 前端进入,Spring 将其自动解组为 Post 对象)。我必须遍历每个关键字,看看持久性中是否已经存在具有相同值的实体。如果是这样,则增加该关键字的计数,将其合并,然后将该实体添加到最终替换请求中的 Set 的集合中。如果它不存在,我只使用请求中的关键字。以前,在将关键字添加到帖子并保留帖子之前,我没有单独保存/合并关键字,但我开始收到错误说明:

org.hibernate.TransientObjectException: 对象引用了一个未保存的 瞬态实例 - 在刷新之前保存瞬态实例: com.saic.jswe.clients.swtc.domain.social.Keyword

无论如何,这是我的服务代码:

public void addPost(Post post){
Set<Keyword> keywords = new HashSet<>();
        for (Keyword keyword : post.getKeywords()) {
            Keyword persistedKeyword = keywordDao.findByValue(keyword.getValue());
            if (persistedKeyword != null) {
                persistedKeyword.setCount(persistedKeyword.getCount() + 1);
                keywordDao.merge(persistedKeyword);
                keywords.add(persistedKeyword);

            } else {
                keywordDao.persist(keyword);
                keywords.add(keyword);
            }
        }
        post.setKeywords(keywords);
postDao.persist(post);
}

另外,在我的测试过程中,当我收到此错误时,它只是一个线程尝试一次添加一个测试 Post 对象。

检查日志,这里是实际的约束违规:

rg.postgresql.util.PSQLException: 错误: 在表上插入或更新 “关键字”违反外键约束 “fk_3tcnkw7v196mudsgmy3nriibl”详细信息:密钥 (id)=(1) 不存在 在“post”表中。

嗯...根据上面的代码,它应该只添加对具有 ID 的关键字对象的引用,如果它确实在持久性中找到它。通过请求与 Post 对象一起传入的关键字对象都应具有空 ID,因为它们尚未持久化。

【问题讨论】:

  • 错误信息应包含约束名称。使用它,您可以找出违反了哪些约束。
  • 我想您添加关键字而不检查它是否存在。或者有多个线程添加关键字。
  • 请检查我在原始问题中的更新
  • 我不确定事务是如何配置的。如果最后提交,hibernate 可能会尝试以错误的顺序持久化事物。也检查生成的 sql,可能会给你一些见解
  • 您不需要keywordDao.merge(persistedKeyword);,因为persistedKeyword 已经在会话缓存中。但这不可能是问题的原因。

标签: java sql hibernate many-to-many


【解决方案1】:

我找到了问题所在。正在创建一个名为“post_keywords”的连接表。它有 2 列,一列称为“post”,一列称为“keyword”。每行代表一个帖子的 ID 和该帖子中包含的关键字的 ID。如果帖子中有多个关键字,则帖子列中可能存在重复条目。但是,一旦不同的帖子实体尝试引用已使用的关键字,它就会抱怨该 ID 已经存在。这是视觉效果:

post | keyword
-----+--------
 1   |   1
 1   |   2
 1   |   4
 2   |   3
 2   |   4   <--- this would be a problem since keyword 4 is already related to post 1

所以我对 JPA 的知识/理解非常薄弱,但我只需要真正的基本关系。鉴于我了解问题出在哪里,我决定停止玩游戏和尝试并开始阅读。

有一分钟,我想我找到了一个仅使用 OneToMany 关系的解决方案,因为我不一定关心或需要关键字实体直接知道哪些帖子引用它。然而,这是不正确的。我可以让该代码在没有错误的情况下执行,但最终每个关键字都只属于一个实体。当每个帖子都试图引用该关键字时,它只会覆盖该关键字的先前所有权。无论如何,我确实需要多对多关系。

我最终找到了显示多个子实体引用相同父实体的表的示例 (http://en.wikibooks.org/wiki/Java_Persistence/ManyToMany),因此我只是在我的代码和 viola 中实现了相同的 JPA 属性,它起作用了。下面是代码现在的样子:

@Entity
public class Post implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "post_id_seq")
    @SequenceGenerator(name = "post_id_seq", sequenceName = "post_id_seq", allocationSize = 1)
    @Column(name="POST_ID")
    private Long id;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
      name="POST_KEYWORD",
      joinColumns={@JoinColumn(name="POST_ID", referencedColumnName="POST_ID")},
      inverseJoinColumns={@JoinColumn(name="KEYWORD_ID", referencedColumnName="ID")})
    private Set<Keyword> keywords = new HashSet<>();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-29
    • 1970-01-01
    • 2018-06-09
    • 1970-01-01
    • 1970-01-01
    • 2016-04-01
    • 2021-06-26
    • 2017-10-21
    相关资源
    最近更新 更多