首页 文章

回滚事务并关闭抛出异常的连接

提问于
浏览
1

目前在我的JavaEE应用程序服务器中使用本地和远程EJB,MDB(Singleton和Stateless),我正在使用JDBC-Transactions for Hibernate Core .

管理自己所有的打开和关闭,提交休眠会话和事务都可能导致连接泄漏和未经发布的事务 .
特别是在编程错误的情况下,导致自定义或未经检查的异常未被捕获并抛出到远程客户端 .

什么是最简单或最好的方法来确保我的hibernate会话被关闭并且事务回滚以防出现错误?

使用容器管理事务(CMT)还是可以关闭在返回任何EJB方法时调用的拦截器中的会话?

一种简单的方法是将会话范围的用法包装在try-catch块中并捕获任何类型的Exception,但是使用较少代码的一般方法将受到青睐 .

Edit: Remote EJB Example

  • 我的低级Hibernate DAO会关闭连接并在抛出异常时回滚事务 . 有问题的是DAO访问之间的业务逻辑,以防连接仍然打开 . *
public void doSomething(Foo foo) throws Exception
{
    // open session and transaction
    Session session = DAO.openSession();

    // retrieve data
    Bar bar = DAO.get(session, ...) 

    // call other methods which throws an exception resulting in open connection
    doOtherStuff(foo, bar)

    DAO.save(session, foo);

    // commit transaction
    DAO.closeAndCommitSession(session);
}

现在我正在使用一个很大的尝试 - 最后:

public void doSomething(Foo foo) throws Exception
    {
        // open session and transaction
        Session session = DAO.openSession();
        try
        {
            // retrieve data
            Bar bar = DAO.get(session, ...) 

            // call other methods which throws an exception resulting in open connection
            doOtherStuff(foo, bar)

            DAO.save(session, foo);
        }
        catch (final Exception e)
        {
            DAO.rollBackTransaction(session);
            throw e;
        }
        finally
        {
            DAO.closeAndCommitSession(session);
        }
    }

1 回答

  • 2

    一般来说,这个问题是关于正确地以简单的方式做 resource management . 这总是需要两个要素:一个简单的API和纪律,可以在任何地方使用此API .

    可能不是使用Hibernate的用例的具体解决方案,而只是为了显示一般的想法:使用 loan pattern

    public interface ConnectionRunnable {
        public void run(Connection conn) throws SQLException;
    }
    

    如果你使用Groovy或Scala(带闭包),这段代码会更加优雅 . 在这种情况下,不需要此接口 .

    无论如何,无论你需要连接,请使用以下内容:

    public static void withConnection(final String dataSourceName,
                ConnectionRunnable runnable) throws SQLException, NamingException {
            final InitialContext ic = new InitialContext();
            final DataSource ds = (DataSource) ic.lookup(dataSourceName);
    
            final Connection conn = ds.getConnection();
            try {
                runnable.run(conn);
            } finally {
                conn.close();
            }
        }
    

    在EJB中像这样使用:

    ConnectionUtils.withConnection("java:/jdbc/sandbox", new ConnectionRunnable() {
        public void run(Connection conn) throws SQLException {
            // Do something with the connection
        }
    });
    
    • 使用贷款模式,您根本无法忘记关闭连接

    • 如果是 SQLExceptionNamingException ,您可以 centrally 决定如何处理它 correctly :使用BMT手动提交或回滚 . 使用 setRollbackOnly 或抛出 SystemException 来触发容器(使用CMT)等 . 如果没有特殊原因,请使用CMT .

    • 即使您稍后更改了错误处理,它也只是一个代码片段 .


    更新:使用 ConnectionRunnable 接口的开销 .

    ConnectionRunnable 仅为"typing-overhead":Groovy和Scala以相同的方式实现闭包,并且运行时开销可以忽略不计 . Java 8可能以相同的方式实现闭包 .

    在这种情况下,提交或回滚数据库事务的成本无论如何都要高得多 .

    从Java /程序/通常的角度来看,它看起来很奇怪,并且关于异常处理的代码重用的想法也可能看起来很奇怪 .

    使用Scala在使用网站上看起来像这样(按字面翻译):

    ConnectionUtils.withConnection("java:/jdbc/sandbox") {
      conn =>
        // Do something with the connection
    }
    

    但在引擎盖下它也使用类似JVM上的匿名类 .

相关问题