我有一个实体,它以前所未有的方式包含与另一个实体的关系,并且我得到了一个例外:" org.hibernate.exception.ConstraintViolationException:无法执行声明"。
父实体被称为" 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对象(请求来自Web前端的AJAX,Spring自动将其解组为Post对象)。我必须循环遍历每个关键字,并查看持久性中是否已存在具有相同值的实体。如果是这样,请递增该关键字的计数,合并它,然后将该实体添加到最终将替换请求中的Set的集合中。如果它不存在,我只使用请求中的关键字。以前,在将帖子添加到帖子并保留帖子之前,我没有独立保存/合并关键字,但我开始收到错误说明:
org.hibernate.TransientObjectException:object引用未保存的 瞬态实例 - 在刷新之前保存瞬态实例: 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:错误:在表格上插入或更新 &#34;关键字&#34;违反外键约束 &#34; fk_3tcnkw7v196mudsgmy3nriibl&#34;细节:键(id)=(1)不存在 在表格&#34; post&#34;。
嗯...根据上面的代码,它应该只添加一个带有ID的Keyword对象的引用,如果它确实在持久性中找到它。通过请求与Post对象一起进入的关键字对象应该都具有空ID,因为它们尚未保留。
答案 0 :(得分:0)
我发现问题出现在哪里。正在创建一个名为&#34; post_keywords&#34;的连接表。它有2列,一个名为&#34; post&#34;和一个名为&#34;关键字&#34;。每行代表帖子的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<>();
}