【发布时间】: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