2013-12-23 150 views
0

我有一个运行在tomcat上的JSF web应用程序。 此应用程序使用休眠连接到Postgres数据库。Hibernate不关闭JDBC连接

过了一段时间,应用程序停止工作,因为它无法打开新的连接。 如果执行中的Postgres的SQL语句:

select * from pg_stat_activity 

它表明我100分的记录,并在列“状态”它让我闲着。 我追踪了导致这种情况的语句,发现多次执行一条语句会导致行为(在我的情况下,它是一个autoComplete ajax方法)。 这是结:

Connection con = ConnectDb.getConnection(); 
    ArrayList<Cidade> cidades=new ArrayList<Cidade>(); 
    try{ 
     PreparedStatement pst=con.prepareStatement("select c.cidCodigo,c.cidNome,e.estCodigo,e.estSigla from cidade c left join estado e on e.estCodigo=c.estCodigo where upper(c.cidNome) like ?"); 
     pst.setObject(1, s.toUpperCase()+"%"); 
     ResultSet rs=pst.executeQuery(); 
     while(rs.next()){ 
      Cidade c = new Cidade(rs.getInt("cidCodigo"), rs.getString("cidNome")); 
      Estado e = new Estado(); 
      e.setEstCodigo(rs.getInt("estCodigo")); 
      e.setEstSigla(rs.getString("estSigla")); 
      c.setCidEstado(e); 
      cidades.add(c); 
     } 
    }catch(Exception e){ 
     e.printStackTrace(); 
     JsfUtils.showFatalMessage("Erro ao carregar dados: "+e.getLocalizedMessage()); 
    }finally{ 
     try{ 
      con.close(); 
     }catch(Exception e){ 
      e.printStackTrace(); 
     } 
    } 

这是我得到的连接:

public class ConnectDb { 

static EntityManagerFactory emf; 

public static EntityManager getEntityManager(){ 
    return getEntityManagerFactory().createEntityManager(); 
} 

public static EntityManagerFactory getEntityManagerFactory() { 
    if(emf==null){ 
     try { 
      emf=Persistence.createEntityManagerFactory("GestaoPU"); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
    return emf; 
} 

public static Session getSession(){ 
    Session session = getEntityManager().unwrap(Session.class); 
    session.setFlushMode(FlushMode.AUTO); 
    return session; 
} 

public static Connection getConnection(){ 
    Object delegate = getEntityManager().getDelegate(); 
    SessionImpl si=(SessionImpl)delegate; 
    Connection con = si.connection(); 
    try {con.setAutoCommit(false);} catch (SQLException e) {} 
    return con; 
} 

}

什么可以引起此问题?

+0

也许你需要保持对EntityManager的引用并调用close()。只是一个猜测。 –

+0

我注意到使用Session/EntityManager时也会发生问题,所以我不认为这是这种情况... –

回答

0

Connection.close()并不总是物理上“关闭”连接。通常在生产代码中,连接被封装在由连接池生成的代理中。代理中的close方法可能不会关闭连接,而是将其返回到池中。

由于您打开ResultSet而未关闭它,因此可能会发生资源泄漏。接下来,既然你已经从休眠中“借用”了连接,你不应该显式地将连接返回到池中(或者在hibernate的后面关闭它)。第三,由于您从getDelegate()返回的会话的唯一引用位于ConnectDb.getConnection()中,因此对于该会话会发生什么情况有点不清楚。这取决于它被调用的上下文。它可能立即有资格进行垃圾回收,或者可能由本地线程持有。如果它实际上立即被gc'd,那么在你的代码有机会使用它之前,hibernate会话将返回它到池的连接。这可能会导致不可预知的行为或难以追踪错误。

第四,您创建的准备好的语句永远不会关闭,可能是另一个资源泄漏。

+0

首先,Connection的关闭不会自动关闭它的Statements和ResultSets? 其次,我从来没有在应用程序上定义连接池,是否有使用休眠的“默认”连接池?第四,同样的第一,PreparedStatement的关闭没有自动关闭与它关联的ResultSets? –

+0

关闭属于连接池的连接并不一定会导致其语句和结果集关闭。它仅将连接返回到池。这可能取决于实现,因为连接池实现可能提供代理或连接,该代理或连接本质上跟踪在连接的活动周期内创建的资源,因此在连接返回到池时会关闭这些资源。实现我知道关闭关闭语句时生成的结果集。 –

+0

这篇链接文章的作者提供了一些关于如何正确关闭jdbc资源的更多见解。 HTTP://博客。shinetech.com/2007/08/04/how-to-close-jdbc-resources-properly-every-time/ 在我看来,虽然,他的异常处理是有缺陷作为例外不被任何接近的语句将抑制由迭代和结果集处理产生的任何异常。 –