繁体   English   中英

休眠-持久化实体时的ConstraintViolation

[英]Hibernate - ConstraintViolation when persisting entity

我有一个实体,该实体以从未有过的方式包含与另一个实体的关系,并且遇到了一个异常:“ org.hibernate.exception.ConstraintViolationException:无法执行语句”。

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

我的想法是,有很多帖子,每个帖子都引用了许多关键字,并且任何一个关键字都可以被多个帖子引用,因此应该是@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的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”。详细信息:表(post)中不存在键(id)=(1)。

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

我发现了问题所在。 正在创建一个称为“ post_keywords”的联接表。 它有2列,一列称为“帖子”,一列称为“关键字”。 每行代表一个帖子的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关系的解决方案,因为我不一定在乎或不需要关键字实体直接知道哪些帖子引用了它。 但是,这是不正确的。 我可以使该代码无错误地执行,但最终我得到的每个关键字仅归一个实体所有。 当每个帖子都尝试引用该关键字时,它将覆盖该关键字的先前所有权。 无论如何,我确实确实需要一个ManyToMany关系。

我最终找到了示例( http://en.wikibooks.org/wiki/Java_Persistence/ManyToMany ),其中显示了多个子实体引用相同父实体的表,因此我在代码和中提琴中实现了相同的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<>();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM