Java 如何在不丢失封装的情况下进行事务处理?

Java 如何在不丢失封装的情况下进行事务处理?,java,hibernate,transactions,Java,Hibernate,Transactions,我有一个代码保存一个bean,并通过Hibernate更新数据库中的另一个bean。它必须在同一事务中执行,因为如果发生错误(f.ex启动异常),则必须对这两个操作执行回滚 public class BeanDao extends ManagedSession { public Integer save(Bean bean) { Session session = null; try { session = createNewSessionAndTransact

我有一个代码保存一个bean,并通过Hibernate更新数据库中的另一个bean。它必须在同一事务中执行,因为如果发生错误(f.ex启动异常),则必须对这两个操作执行回滚

public class BeanDao extends ManagedSession {

public Integer save(Bean bean) {
    Session session = null;
    try {
        session = createNewSessionAndTransaction();

        Integer idValoracio = (Integer) session.save(bean);  // SAVE
        doOtherAction(bean);                                 // UPDATE

        commitTransaction(session);

        return idBean;
    } catch (RuntimeException re) {
        log.error("get failed", re);
        if (session != null) {
            rollbackTransaction(session);
        }
        throw re;
    }
}

private void doOtherAction(Bean bean) {
    Integer idOtherBean = bean.getIdOtherBean();
    OtherBeanDao otherBeanDao = new OtherBeanDao();
    OtherBean otherBean = otherBeanDao.findById(idOtherBean);
    .
    . (doing operations)
    .
    otherBeanDao.attachDirty(otherBean)
}
}
问题是:

万一

session.save(bean)
启动一个错误,然后我得到AssertionFailure,因为函数doOtherAction(在项目的其他部分中使用)在抛出异常后使用会话

我想到的第一件事是提取doOtherAction函数的代码,但是我有相同的代码副本,而且似乎不是这样做的最佳实践


重构的最佳方法是什么?

通常在DAO之上的一个级别、服务或其他业务逻辑类中管理事务。这样,基于业务/服务逻辑,在一种情况下,您可以在一个事务中执行两个DAO操作,在另一种情况下,您可以在单独的事务中执行它们。

我非常喜欢声明式事务管理。如果你能抽出时间让它工作(用GlassFish或JBoss之类的工具就很容易了,而且使用起来也很简单)。如果您使用
@TransactionAttribute(必需)
注释您的业务方法(甚至可以将其设置为默认值),并且它调用两个DAO方法,您将得到您想要的结果:所有内容都立即提交或回滚到异常。
这个解决方案是松散耦合的。

其他解决方案是正确的,因为它们考虑了当前的常见做法

但这并不能真正帮助你目前的做法

您应该做的是创建两个新的DAO方法。例如CreateGlobalSession和CommitGlobalSession

它们所做的与您当前的创建和提交例程相同

区别在于它们设置了一个“全局”会话变量(很可能最好使用ThreadLocal)。然后更改当前例程,以便它们检查此全局会话是否已存在。如果create检测到全局会话,则只需返回它。如果提交检测到全局会话,那么它什么也不做

现在,如果要使用它,请执行以下操作:

try {
    dao.createGlobalSession();
    beanA.save();
    beanb.save();
    Dao.commitGlobalSession();
} finally {
    dao.rollbackGlobalSession();
}
确保将进程包装在try块中,以便在出现错误时可以重置全局会话


虽然其他技术被认为是最佳实践,理想情况下,有一天你可以发展成类似的技术,但这将使你通过3种新方法和改变两种现有方法度过难关。之后,您的其余代码保持不变。

作为一个不得不维护大量使用共享会话/事务(反)模式的代码的人,我可以说,在开发阶段仅仅为了避免一些配置工作和重构而使用它是不明智的。这种解决方案往往会增加维护成本。在某个时刻,有人会忘记提交事务,会话会泄漏,您会在调度作业或集群应用程序等方面遇到问题。从一个完全实用的角度来看,我不是在谈论“理想”的方法,而是“从长远来看,经证明是便宜的”.我只是想修正一下我所说的:共享会话/事务是一种可以快速复制和粘贴到任何地方的变通方法,迟早会成为项目中事务管理的某种标准。请不要制造怪物。@Anthony,当然,但事情是这样的。这种情况只需要实际的交易,这种情况(显然)到目前为止是罕见的。因此,DAO的正常机制仍然存在(当然,它仍然可能泄漏)。然后,如果有人想稍后添加另一个multi-bean事务,他们可能会剪切并粘贴此原始代码,从而保留模式。当然,作为副作用。但是,他也没有提到他的代码是在服务器上,还是在客户机上。要执行您建议的操作,他需要EJB、Spring或其他框架。这对他来说可能不太实际。我明白了,在纯Java SE环境中,我仍然希望重构代码,在DAO构造函数中接收会话,并在业务层使用事务划分代码(见鬼,如果需要,甚至创建一些动态代理)。至少更容易看到事务必须从何处开始和结束)。所以我想说的是,即使你的代码可以在一小时内解决OP的问题,而不是几个小时/天,我仍然推荐Olaf方法。非主题:1+用于hibernate“尝试使用资源”支持:D。