Hibernate - 同一会话中的第二个事务不保存修改后的对象

时间:2016-06-02 21:34:08

标签: java hibernate transactions

好的,所以我的问题是我在同一个会话中执行第二个事务,看来任何修改过的对象作为第二个事务的一部分都没有被session.save()保存。我已经比较了第一个事务的运行,并注意到第二个事件中引用的对象都没有在Persistence Context中。这是第一笔交易:

197459 [http-nio-8080-exec-1] DEBUG com.microstar.tap2.processors.ShipmentProcessor  - Saving updated shipment!
197476 [http-nio-8080-exec-1] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager  - Initiating transaction commit
197476 [http-nio-8080-exec-1] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager  - Committing Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[com.microstar.tap2.datamodel.tap.User#23896], EntityKey[com.microstar.tap2.datamodel.tap.Shipment#778901], EntityKey[com.microstar.tap2.datamodel.tap.User#22268], EntityKey[com.microstar.tap2.datamodel.tap.User#19991 

第二项交易显示:

402754 [http-nio-8080-exec-3] DEBUG com.microstar.tap2.processors.ShipmentProcessor  - Saving updated shipment!
402754 [http-nio-8080-exec-3] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager  - Initiating transaction commit
402754 [http-nio-8080-exec-3] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager  - Committing Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@57573be6 updates=org.hibernate.engine.spi.ExecutableList@1255bf68 

我的代码执行对于两个交易都是相同的...我已多次介绍它。它是在同一数据库的同一个表中为两个不同的行执行的完全相同的方法。这是在AWS Elastic Beanstalk(Tomcat8)中运行Java 8 - 但也发生在Eclipse / Tomcat8中的Java8上运行。

此应用是REST API。这是资源定义:

@Transactional
@RequestMapping(value = "/{id}/quote", method = RequestMethod.GET)

该方法对session / hibernate没有任何作用。但它称DAO为。所有资源控制器所做的第一件事就是检查身份验证/授权:

String tapUserId = authUtil.checkAuthorization(...)

这反过来调用DAO,所有DAO的基类都有一个方法:

Session getSession()
{
    if (session == null)
    {
        try
        {
            session = sessionFactory.getCurrentSession();
            logger.debug("Got current session: " + session);
        } catch (Exception ex)
        {
            logger.debug("NO current session!");
        }
    }
    if (session == null)
    {
        session = sessionFactory.openSession();
        logger.debug("Opened new session: " + session);
    }
    if ( session != null && !session.isOpen() )
    {
        session = sessionFactory.openSession();
        logger.debug("Had to re-open session: " + session);
    }

    return session;
}

它接下来要做的是使用Hibernate获取货件:

    logger.debug("Using orderId: " + orderId);
    session = getSession();
    Query query = session.getNamedQuery(Shipment.SHIPMENT_FINDBY_ORDERID_ID); 
    query.setString("orderid", orderId);
    Object r = query.uniqueResult();
    if ( r == null )
        return null;

    return (Shipment)r;

第二个事务的日志显示调试消息"获得当前会话"并且转储会话显示PersistenceContext中的entityKeys数组是空的 - 但我希望在那一点上。而在第一个事务中,日志条目是"打开新会话" - 再次使用空的entityKeys数组。

资源控制器的最后一个功能行调用DAO来更新记录,然后执行此操作:

    session = getSession();
    session.save(shipment);
    return shipment;

我完全不知道为什么这甚至是一个问题,但我猜测我的会话/交易管理有问题。如上所示,资源都使用@Transaction - no modifiers。

那么,我错过了什么?

谢谢, -mac

2 个答案:

答案 0 :(得分:1)

正如spring documentation中所述,spring默认为每个事务一个Hibernate Session

为了使您的设置正常工作,您还需要:

  • 让spring注入SessionFactory实例
  • 一旦离开(或输入新的)交易边界(由@Transactional标记),请Session
  • 检索新的Hibernate sessionFactory.getCurrentSession()

在应用程序代码中缓存SessionFactory和/或Session实例会阻碍他的任务框架正确管理事务。框架无法知道应用程序代码决定使用哪个Session。因此,框架要求应用程序代码将准备和存储的框架用于每个sessionFactory.getCurrentSession()的检索。在那个Session框架上执行会话和事务实时循环管理。

我也发现这个Stackoverflow questionHibernate Documentation很有帮助。

答案 1 :(得分:0)

感谢@Michal,问题解决了。在我的基础DAO类中,我将会话作为实例变量,这搞砸了。不完全确定原因,但我也同意一次交易=一次会议。

因此,解决方案是使会话成为方法变量,并且基本上总是向会话工厂询问会话。