簡體   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