Glassfish“连接已关闭”错误与连接池、JDBC 和 SQL Server 2008
Posted
技术标签:
【中文标题】Glassfish“连接已关闭”错误与连接池、JDBC 和 SQL Server 2008【英文标题】:Glassfish "Connection closed" error with a connection pool, JDBC, and SQL Server 2008 【发布时间】:2012-10-07 21:45:02 【问题描述】:当我尝试在 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 的一个实例,并管理每个 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
如果您从这里尝试执行数据库操作,您将收到 Connection closed 错误,因为 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
是一个 enum
,它保存了数据库中的当前表名,因此它可以返回正确的 DAO。这没有任何问题。 类的其余部分是一样的,所以如果你想使用它,只需将DAOUsuario
方法替换为上面的方法,它应该可以正常工作。
【讨论】:
以上是关于Glassfish“连接已关闭”错误与连接池、JDBC 和 SQL Server 2008的主要内容,如果未能解决你的问题,请参考以下文章