2012-10-07 5 views
0

나는 JSF 페이지에 하나 이상의 트랜잭션을 수행하려고 2008, 나는 다음과 같은 오류 얻을 :글래스 피시 연결 풀, JDBC와 SQL Server와 오류를 "연결 종료"

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() 호출합니다.

그런데 JDBC로 작업하고 있으므로 persistence.xml이 없습니다.

내가 뭘 잘못하고 있니? 미리 감사드립니다.

수정 : 글래스 피시 서버에서 누출 감지를 비활성화하면 예외가 발생하지 않지만 여전히 "연결이 닫혔습니다"라는 오류가 표시됩니다. 최악의 경우, 이제 오류가 어디에서 발생하는지 정확히 알 수 없습니다.

EDIT 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가 명령문이나 리소스를 사용하고자 할 때, 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 관리자 클래스가 수정되었습니다. 그것은 더 이상 DAO 당 getDAOXXX()을 가지고 있지만, 오히려 다음

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은 올바른 DAO를 반환 할 수 있도록 데이터베이스에 현재 테이블 이름이 들어있는 enum입니다. 이것은 아무런 문제없이 작동했습니다. 나머지 클래스는 동일하므로 사용하려면 DAOUsuario 메서드를 위의 메서드로 바꿔야합니다. 그러면이 제대로 작동합니다.