2013-09-26 78 views
1

当前位于具有本地和远程EJB的MDB(Singleton和Stateless)的JavaEE应用程序服务器中,我正在使用JDBC-Transactions for Hibernate Core。在抛出异常时回滚事务并关闭连接

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

什么是最简单或最好的方式来确保我的休眠会话被关闭,事务回滚以防错误发生?

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

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

编辑:远程EJB实例

  • 我低级别的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); 
    } 
    

现在我使用的是大的try-catch-最后:

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); 
     } 
    } 

回答

2

一般来说这个问题正在做资源管理正确和在简单的方式。这总是需要两个要素:一个简单的API和纪律,以在任何地方使用这个API。

可能不适合使用Hibernate,但只是为了显示的总体思路你的使用情况的具体解决方案:使用贷款模式

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您可以集中决定如何正确处理:使用BMT手动提交或回滚。使用setRollbackOnly或抛出SystemException触发容器(使用CMT)等。如果没有特殊原因,请使用CMT。
  • 即使您稍后更改错误处理,它也只是一个代码片段。

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

ConnectionRunnable仅仅是一种“键入开销”:Groovy和Scala的实现以相同的方式关闭,运行时开销可以忽略不计。 Java 8将以相同的方式实现闭包。

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

从Java /过程/惯用的角度来看,它看起来很奇怪,关于异常处理的代码重用的想法也可能看起来很奇怪。

使用Scala的它看起来像这样在使用场所(直译):

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

但引擎盖下它使用类似于在JVM上的一个匿名类为好。

+0

这将需要每个方法的'ConnectionRunnable',这似乎是非常大的开销。关闭连接是最低优先级,因为这只是一个编程错误。问题更多的是未捕获的异常,当连接仍处于打开状态并且事务未回滚时,将抛出异常。 – djmj

+1

@djmj:Beryllium提出的解决方案看起来简洁而优雅,即使它需要每个连接的匿名内部类。这里的美妙之处在于,它允许您以更确定的方式管理资源并处理异常。我错过了什么吗? – scottb

+0

@scottb不错的总结!我只想补充说,在使用站点上需要更少的代码行:嵌套的异常处理在每个使用站点都会使代码混乱。 – Beryllium

相关问题