在事件中创建的新Hibernate Session中看不到保存在不同会话中的实体(但同一事务)

时间:2011-03-24 15:40:42

标签: hibernate spring transactions

我正在使用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被保存两次),并希望更清洁的解决方案(或至少更好地理解为什么它没有像我期望的那样工作。)

任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:1)

请使用session.merge()代替session.save()。并here is an excellent explaination why

希望有所帮助。

答案 1 :(得分:0)

一种解决方案是将HistoricReference的保存移动到FlushEvent。在preInsert事件中,只需获取数据并将其收集在线程本地映射/列表中,然后访问并将它们保存在FlushEvent中。