我正在使用Hibernate的PreInsertEventListener
来审核我的表的字段。
我也使用Spring来创建SessionFactory和TransactionManager。
可能相关的其他一些细节:
使用OpenSessionInViewFilter
Hibernate版本:3.5.1-最终版
春季版:3.0.3.RELEASE
Oracle 10g数据库
在事件中使用Session是不安全的,因为它可能正在被刷新,因此我创建了一个新的Session。
public boolean onPreInsert(final PreInsertEvent event) {
final SessionFactory factory = triggeringSession.getSessionFactory();
Session triggeringSession = event.getSession();
triggeringSession.doWork(new Work() {
public void execute(Connection connection) throws SQLException {
Session newSession = factory.openSession(connection);
saveStuff(newSession);
newSession.flush();
}
});
return false;
}
在我的saveStuff
方法中,我需要保存一个新实体。
private void saveStuff(Session session) {
HistoricReference historicReference = new HistoricReference();
historicReference.setId("12345"); // This is actually an unique ID based on a generated auditing number and table that was inserted into
// If this haven't been created I want it to be saved
if (session.get(HistoricReference.class, "12345") == null) {
session.save(historicReference);
}
// Other stuff saved (regarding old/new values etc.) that historicReference as id (as part of composite id)
}
我的问题是,如果我在同一个事务中插入2个相同类型的新实体,我会在HistoricReference表上获得唯一的约束违规。我调试了,检查它是否已经创建的代码总是返回false,但最后当事务被提交时,它会尝试插入两次。这是因为每次都会创建一个新会话吗?
示例:
MyEntity entity1 = new MyEntity();
MyEntity entity2 = new MyEntity();
session.save(entity1);
session.save(entity2);
我会在请求结束时(尝试提交时)获得唯一约束错误。这是因为当为entity2调用onPreInsert(...)
时,它将再次调用session.save(historicReference)
。如果我不保存entity2,则不会遇到任何问题。
即使session.get(HistoricReference.class, "12345")
保存在不同的会话中但是同一个未提交的事务,TransactionSynchronizationManager
会返回一个值吗?
我有一个问题的解决方法,但它有点hacky(一个布尔值绑定到事务使用{{1}},以防止historicalReference被保存两次),并希望更清洁的解决方案(或至少更好地理解为什么它没有像我期望的那样工作。)
任何帮助将不胜感激。
答案 0 :(得分:1)
请使用session.merge()
代替session.save()
。并here is an excellent explaination why。
希望有所帮助。
答案 1 :(得分:0)
一种解决方案是将HistoricReference的保存移动到FlushEvent。在preInsert事件中,只需获取数据并将其收集在线程本地映射/列表中,然后访问并将它们保存在FlushEvent中。