2013-12-23 261 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/ 在我看來,雖然,他的異常處理是有缺陷作爲例外不被任何接近的語句將抑制由迭代和結果集處理產生的任何異常。 –