2012-10-07 153 views
0

當我嘗試在JSF頁面做一個以上的交易,我得到以下錯誤:Glassfish的「連接已關閉」的錯誤使用連接池,JDBC和SQL Server 2008

A potential connection leak detected for connection pool MSSQL. The stack trace of the thread is provided below : 
com.sun.enterprise.resource.pool.ConnectionPool.setResourceStateToBusy(ConnectionPool.java:324) 
com.sun.enterprise.resource.pool.ConnectionPool.getResourceFromPool(ConnectionPool.java:758) 
com.sun.enterprise.resource.pool.ConnectionPool.getUnenlistedResource(ConnectionPool.java:632) 
com.sun.enterprise.resource.pool.AssocWithThreadResourcePool.getUnenlistedResource(AssocWithThreadResourcePool.java:196) 
com.sun.enterprise.resource.pool.ConnectionPool.internalGetResource(ConnectionPool.java:526) 
com.sun.enterprise.resource.pool.ConnectionPool.getResource(ConnectionPool.java:381) 
com.sun.enterprise.resource.pool.PoolManagerImpl.getResourceFromPool(PoolManagerImpl.java:245) 
com.sun.enterprise.resource.pool.PoolManagerImpl.getResource(PoolManagerImpl.java:170) 
com.sun.enterprise.connectors.ConnectionManagerImpl.getResource(ConnectionManagerImpl.java:338) 
com.sun.enterprise.connectors.ConnectionManagerImpl.internalGetConnection(ConnectionManagerImpl.java:301) 
com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:190) 
com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:165) 
com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:160) 
com.sun.gjc.spi.base.DataSource.getConnection(DataSource.java:113) 
cl.codesin.colegios.util.persistencia.DAOManejador.abrir(DAOManejador.java:126) 

請注意最後一行我粘貼:

cl.codesin.colegios.util.persistencia.DAOManejador.abrir(DAOManejador.java:126) 

abrir執行以下操作:

public void abrir() throws SQLException { 
    try 
    { 
     if(this.con==null || this.con.isClosed()) 
      this.con = fuenteDatos.getConnection(); 
    } 
    catch(SQLException e) 
    { 
     throw e; 
    } 
} 

它工作在一個單獨的DAO管理器:DAO管理器擁有每個DAO的一個實例並管理每個DAO共享的單個連接。當請求DAO,它具有以下功能:

public DAORegion getDAOregion() throws SQLException { 
     try 
     { 
      if(con == null) //con is the connection the DAO manager uses 
      { 
       this.abrir(); 
      } 
     } 
     catch(SQLException e) 
     { 
      throw e; 
     } 
     if(this.DAOregion==null) 
     { 
      this.DAOregion = new DAORegion(this.con); 
     } 
     return DAOregion; 
    } 

當關閉連接,管理者只是調用con.close()沒有別的。

順便說一句,我沒有persistence.xml,因爲我正在使用JDBC。

我在做什麼錯?事先謝謝你。

編輯:通過停用從Glassfish服務器泄漏檢測我可以避免例外,但我仍然收到「連接關閉」錯誤。最糟糕的是,現在我不清楚錯誤發生的位置。

編輯2:我再次改變了我的DAO管理器。這是實現。

public class DAOManejador { 

    public static DAOManejador getInstancia() { 
     return DAOManejadorSingleton.INSTANCIA; 
    } 

    //This is just a sample, every getDAOXXX works the same. 
    public DAOUsuario getDAOusuario() throws SQLException { 
     try 
     { 
      if(con == null) 
      { 
       this.abrir(); 
      } 
     } 
     catch(SQLException e) 
     { 
      throw e; 
     } 
     if(this.DAOusuario==null) 
     { 
      this.DAOusuario = new DAOUsuario(this.con, this.stmt, this.res); 
     } 
     return DAOusuario; 
    } 

    public void abrir() throws SQLException { 
     try 
     { 
      if(this.con==null || this.con.isClosed()) 
       this.con = fuenteDatos.getConnection(); 
     } 
     catch(SQLException e) 
     { 
      throw e; 
     } 
    } 

    public void iniciaTransaccion() throws SQLException { 
     try 
     { 
      con.setAutoCommit(false); 
     } 
     catch(SQLException e) 
     { 
      throw e; 
     } 
    } 

    public void cierraTransaccion() throws SQLException { 
     try 
     { 
      con.setAutoCommit(true); 
     } 
     catch(SQLException e) 
     { 
      throw e; 
     } 
    } 

    public void comprometer() throws SQLException { 
     try 
     { 
      con.commit(); 
     } 
     catch(SQLException e) 
     { 
      throw e; 
     } 
    } 

    public void deshacer() throws SQLException { 
     try 
     { 
      con.rollback(); 
     } 
     catch(SQLException e) 
     { 
      throw e; 
     } 
    } 

    public void cerrar() throws SQLException { 
     try 
     { 
      if(this.stmt!=null && !this.stmt.isClosed()) 
       stmt.close(); 

      if(this.res!=null && !this.res.isClosed()) 
       this.res.close(); 

      if(this.con!=null && !this.con.isClosed()) 
       con.close(); 
     } 
     catch(SQLException e) 
     { 
      throw e; 
     } 
    } 

    public void comprometerYTerminarTransaccion() throws SQLException { 
     try 
     { 
      this.comprometer(); 
      this.cierraTransaccion(); 
     } 
     catch(SQLException e) 
     { 
      throw e; 
     } 
    } 

    public void comprometerYCerrarConexion() throws SQLException { 
     try 
     { 
      this.comprometer(); 
      this.cierraTransaccion(); 
      this.cerrar(); 
     } 
     catch(SQLException e) 
     { 
      throw e; 
     } 
    } 

    //Protegidos 
    @Override 
    protected void finalize() throws SQLException, Throwable 
    { 
     try 
     { 
      this.cerrar(); 
     } 
     finally 
     { 
      super.finalize(); 
     } 
    } 

    //Private 
    private DataSource fuenteDatos; 
    private Connection con = null; 
    private PreparedStatement stmt = null; 
    private ResultSet res = null; 

    private DAOUsuario DAOusuario = null; 
    private DAORegion DAOregion = null; 
    private DAOProvincia DAOprovincia = null; 
    private DAOComuna DAOcomuna = null; 
    private DAOColegio DAOcolegio = null; 

    private DAOManejador() throws Exception { 
     try 
     { 
      InitialContext ctx = new InitialContext(); 
      this.fuenteDatos = (DataSource)ctx.lookup("jndi/MSSQL"); 
     } 
     catch(Exception e){ throw e; } 
    } 

    private static class DAOManejadorSingleton { 
     public static final DAOManejador INSTANCIA; 
     static 
     { 
      DAOManejador dm; 
      try 
      { 
       dm = new DAOManejador(); 
      } 
      catch(Exception e) 
      { dm=null; } 
      INSTANCIA = dm; 
     } 
    } 

} 

我現在所做的是爲每個DAO提供一個接入點。當DAO想要使用語句或資源時,它們將全部使用相同的語言或資源。當他們需要再次打開,系統將執行以下操作:

public abstract class DAOGenerico<T> { 

    //Protected 
    protected final String nombreTabla; 
    protected Connection con; 
    protected PreparedStatement stmt; 
    protected ResultSet res; 

    protected DAOGenerico(Connection con, PreparedStatement stmt, ResultSet res, String nombreTabla) { 
     this.nombreTabla = nombreTabla; 
     this.con = con; 
     this.stmt = stmt; 
     this.res = res; 
    } 

    //Prepares a query 
    protected final void prepararConsulta(String query) throws SQLException 
    {    
     try 
     { 
      if(this.stmt!=null && !this.stmt.isClosed()) 
       this.stmt.close(); 
      this.stmt = this.con.prepareStatement(query); 
     } 
     catch(SQLException e){ throw e; } 
    } 

    //Gets a ResultSet 
    protected final void obtenerResultados() throws SQLException { 
     try 
     { 
      if(this.res!=null && !this.res.isClosed()) 
       this.res.close(); 
      this.res = this.stmt.executeQuery(); 
     } 
     catch(SQLException e){ throw e; } 
    } 

} 

仍然不起作用。

回答

1

我在關閉連接時沒有做任何事情。我評論了 cerrar方法中的代碼,出於某種原因,它的工作原理!即使這是一種不好的做法!保持這種狀態是否可以,或者我應該找到一種方法來關閉連接?

無視這一點,我發現有什麼不對。我希望有人能夠在未來充分利用這一點。

問題

if(this.con==null || this.con.isClosed()) 
    this.con = fuenteDatos.getConnection(); 

每次我試圖打開一個連接,我得到一個完全嶄新的連接。這有什麼問題?

public DAOUsuario getDAOusuario() throws SQLException { 
    try 
    { 
     if(con == null) 
     { 
      this.abrir(); 
     } 
    } 
    catch(SQLException e) 
    { 
     throw e; 
    } 
    if(this.DAOusuario==null) 
    { 
     this.DAOusuario = new DAOUsuario(this.con, this.stmt, this.res); 
    } 
    return DAOusuario; 
} 

只有當我創建一個新的DAO實例時,我爲它分配一個新的連接。接下來的情況會發生什麼?

DAOManejador daoManager = DAOManejador.getInstancia(); //Get an instance of the DAO manager 
daoManager.abrir(); //Open the connection 
DAOUsuario daoUser = daoManager.getDAOusuario(); //Get a DAOUsuario, a type of DAO. It'll have the same connection as the DAOManager, and it'll be stored in the instance of the DAO manager 
... //Do database stuff 
daoManager.cerrar(); //Close the connection 
daoManager.abrir(); //Open the connection again. Note that this will be a new instance of the conection rather than the old one 

如果,從這裏,你嘗試做數據庫的東西,你會得到一個連接關閉錯誤,因爲daoUser將仍持有舊的連接。

我做了什麼

我修改了DAO管理器類。它不再擁有getDAOXXX()每DAO,而是如下:

public DAOGenerico getDAO(Tabla t) throws SQLException { 
    try 
    { 
     if(con == null || this.con.isClosed()) 
     { 
      this.abrir(); 
     } 
    } 
    catch(SQLException e) 
    { 
     throw e; 
    } 
    switch(t) 
    { 
     case REGION: 
      return new DAORegion(this.con, this.stmt, this.res); 
     case PROVINCIA: 
      return new DAOProvincia(this.con, this.stmt, this.res); 
     case COMUNA: 
      return new DAOComuna(this.con, this.stmt, this.res); 
     case USUARIO: 
      return new DAOUsuario(this.con, this.stmt, this.res); 
     case COLEGIO: 
      return new DAOColegio(this.con, this.stmt, this.res); 
     default: 
      throw new SQLException("Se intentó vincular a una tabla que no existe."); 
    } 
} 

每一個用戶請求一個DAO的時候,它會請經理返回正確類型的DAO。但不是存儲每個實例,管理器將根據當前連接創建新實例(con是連接,stmt是PreparedStatement,而res是ResultSet - 它們將用於在管理器關閉連接時關閉它們沒有泄漏)。 Tabla是保存數據庫中當前表名的enum,以便它可以返回正確的DAO。這工作沒有任何問題。 其餘的課程都是一樣的,所以如果你想使用它,只需將DAOUsuario方法替換爲上面的方法,它應該工作正常