跨方法的Hibernate会话和事务

时间:2015-07-13 08:05:17

标签: java hibernate session orm transactions

我有一个从sessionFactory.openSession()检索到的hibernate会话,以及一些复杂的计算到Entities,并且我想在计算过程中持久(UPDATE, INSERT, DELETE)一些Entities

以下是一个案例:

请注意,我有ProductEntity代表产品,OrderEntity代表产品的订单记录,UserEntity代表预订订单的用户产品。

我知道我可以通过这种方式处理预订操作:

  public void addOrder(UserEntity userEntity, ProductEntity productEntity, int quantity){
       session = sf.openSession();
       Transaction tx = session.beginTransaction();
       //do some compution and generate the orderEntity and persistent it to the db.
       try{
          tx.commit();
       }catch(Exception e){
          tx.rollback();
       }finnaly{
          session.close();
       }
 }

现在我必须向该过程添加更多操作,例如( Maybe )制作NotifyEntity并存储在db中,该数据库表示对拥有该产品的商家的通知记录。此notify记录可由orderEntity生成,与productEntityUserEntity无关,事实上,我希望此notifyMerchantByOrderEntity方法分开 - 超出addOrder程序,以便我可以重复使用此方法并澄清代码(我真的不想在同一方法中搞乱大量代码,事实上如果检查逻辑是足够复杂,addOrder方法的代码可能非常非常低。)

无论如何,我想:

  • 将一个非常长的事务分成几个方法
  • 但是这些方法应该作为整个事务一起考虑(即,当发生异常时它们应该一起回滚)

这样的事情:

 public void addOrder(UserEntity userEntity, ProductEntity productEntity, int quantity){
    Session session = sf.openSession();
    session.beginTransaction();
    invokeOtherMethod();  //invoking other method which also contains some db operation.
    try{
       tx.commit()
    }catch(Exception e){
       tx.rollback(); //this should rollback the operation in invokeOtherMethod() too.
    }finally{
       session.close();
    }
 }

2 个答案:

答案 0 :(得分:1)

您应该切换到使用Spring TransactionManager,因为它允许您将多个DAO调用分组到同一个数据库事务中。

如果您不想使用Spring,您仍然可以使用服务层方法拦截器(可能是动态代理或Javassist)来实现事务管理逻辑,该拦截器使用ThreadLocal存储将当前Thread绑定到同一个Hibernate会话

当您输入事务边界时,您打开事务,当您离开事务边界时,如果没有抛出异常则提交事务,或者如果您发现任何异常,则将其回滚:

private SessionFactory sf;

private final ThreadLocal<Session> sessionStorage =
        new ThreadLocal<Session>();

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Session session = sessionStorage.get();
    if(session == null) {
        session = sf.openSession();
    }
    Transaction tx = session.beginTransaction();
    try{
        return method.invoke(target, args);
        tx.commit();
    }catch(Exception e){
        tx.rollback();
        throw new ServiceException(e);
    }finally{
        session.close();
        sessionStorage.remove();
    }
}

现在你必须将这个拦截器编织到所有的Service方法调用中。

答案 1 :(得分:1)

我找到了另一种方法来将session传递给将要调用的方法。测试演示显示,当发生异常时,我也可以在调用的方法中回滚数据库操作,虽然给出的答案@Vlad可能值得试用。我在这里发布了这样做的方式,希望它能激发人们遇到类似的问题。

public OrderEntity addOrder(UserEntity userEntity, ProductEntity, int quantity){
      Session session = sf.openSession();
      try{
          Transaction tx = session.beginTransaction();
          OrderEntity orderEntity = new OrderEntity(userEntity, productEntity, quantity);
          session.save(orderEntity);
          notifyMerchant(orderEntity, session); //here we will invoke another method with session together.
          tx.commit();
          return orderEntity;
     }catch(Exception e){
          e.printStack();
          tx.rollback(); // if Exception occurs all the operations making to the db will be rollback, as well as those operations in method notifyMerchant();
          return null; 
     }finally{
        session.close();
     } 
}

private void notifyMerchant(OrderEntity orderEntity, Session session){
     NotifyEntity notifyEntity = new NotifyEntity(orderEntity);
     notifyEntity.notifyMerchant();
     session.save(notifyEntity); // this will save the notifyEntity to db as a sort of log stuff;
}

@Vlad可能会提供更好的方法,目前上面的工具是我的项目变化较少的方式。