0

JSF ページで複数のトランザクションを実行しようとすると、次のエラーが発生します。

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 の 1 つのインスタンスを持ち、すべての 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 はありません。

私は何を間違っていますか?よろしくお願いします。

編集: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; }
    }

}

そして、それはまだ機能しません。

4

1 に答える 1

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です-マネージャーが接続を閉じたときに閉じることができるため、リークが発生しないように使用されます) 。Tablaenumデータベース内の現在のテーブル名を保持しているため、正しいDAOを返すことができます。これは何の問題もなく機能しました。クラスの残りの部分は同じなので、それを使用したい場合は、DAOUsuarioメソッドを上記のメソッドに置き換えるだけで、正常に機能するはずです。

于 2012-10-08T20:20:14.603 に答える