Java, ResultSet.close(), PreparedStatement.close()-¿para qué?
database memory-leaks (5)
AFAIK, terminará agotando recursos en su servidor de base de datos debido a los manejadores de archivos atados, los recursos necesarios para mantener el conjunto de resultados asociado a una declaración determinada, etc. Puede haber implementaciones de controladores / bases de datos inteligentes que aseguren que tan pronto a medida que se cierra la conexión, todos los recursos relacionados se liberan, pero eso no forma parte de la especificación, por lo que eventualmente podría venir y morder a la larga. ¿Alguna razón por la cual las clases prioritarias no pueden cerrar los conjuntos de resultados y las declaraciones que usan?
En mi aplicación web, hago un uso extensivo de una base de datos.
Tengo un servlet abstracto, desde el cual heredan todos los servlets que necesitan una conexión de base de datos. Ese servlet abstracto crea una conexión de base de datos, llama al método abstracto que debe ser reemplazado por los servlets heredados para hacer su lógica, y luego cierra la conexión. No uso la agrupación de conexiones porque mi aplicación tendrá un número muy limitado de usuarios y operaciones.
Mi pregunta es, ¿qué es lo peor que puede pasar si no cierro los ResultSet
s, PreparedStatement
y Statement
s que crean mis servlets heredados, si los Connection
s que los crean siempre están cerrados?
El javadoc para Statement # close () dice:
Nota: Cuando se cierra un objeto Statement, su objeto actual ResultSet, si existe, también se cierra.
Por lo tanto, no tiene que preocuparse por cerrar los ResultSets, siempre que cierre las declaraciones de manera oportuna.
El javadoc para Connection # close () no hace una garantía correspondiente, pero dice:
Libera la base de datos de este objeto Connection y los recursos JDBC de forma inmediata en lugar de esperar que se liberen automáticamente.
Lo cual razonablemente puede interpretarse como que implica que cualquier declaración será cerrada. Al observar el controlador jTDS de fuente abierta y echar un vistazo al controlador en busca de una base de datos comercial conocida y cara, puedo ver que hacen exactamente eso.
Estoy bastante seguro de que al cerrar la conexión se cerrarán las declaraciones asociadas, los ResultSets y otros objetos asociados. Sin embargo, todo esto consumirá recursos tanto en el cliente como en el servidor de la base de datos hasta que se cierre la conexión.
Si en su caso sabe que cerrará la conexión muy pronto , probablemente no arriesgue mucho, aunque no creo que esto deba considerarse como una mejor práctica.
Sin embargo, esto solo es válido en su configuración actual. Si su solicitud cambiará, puede enfrentar problemas porque no cerró sus estados de cuenta y los conjuntos de resultados.
Aunque no desea utilizar la agrupación de conexiones, creo que es una mala idea, incluso con pocos usuarios / operaciones, ya que la apertura de una conexión de base de datos no es barata. Por lo tanto, incluso en su contexto, el conjunto de conexiones puede ayudar a que su sistema sea más receptivo.
Solo una nota sobre la recolección de basura. Antes de cerrar la conexión, los Statements o ResultSets no utilizados pueden ser GCed. Pero cuando se trata de liberar recursos del sistema como archivos o, en general, recursos que no son de Java (como los cursores en un servidor de base de datos), no se debe confiar en el JVM GC. Por ejemplo, si su aplicación cliente abre muchos ResultSets pero solo usa una pequeña fracción de la memoria de montón asignada, el GC nunca entrará en funcionamiento mientras el servidor de la base de datos esté sofocado por los cursores abiertos.
Una conexión a la base de datos no es lo único que está bloqueando su aplicación. Hay otros recursos en juego.
Serán liberados en algún momento si no los liberas tú mismo, pero si puedes hacer eso, deberías hacerlo.
Es un poco de mierda Api - termina contigo escribiendo una carga de código de la placa de la caldera.
Creo que la mejor manera de hacerlo es envolver las clases y hacer que la eliminación de la conexión elimine las otras cosas (ya que puedes rastrear lo que se hace cuando sales de las llamadas envueltas).
Siempre que tengas un IDE que te genere la delegación de métodos en una clase, entonces es un trabajo trivial para envolver cosas como esta.
No me di cuenta de todo lo que necesitaba deshacerme, pero me di cuenta de que alguien lo estaba haciendo aquí. Sin embargo, estoy de suerte ya que estamos completando las clases básicas para convertir todas las molestas excepciones en RuntimeExceptions y proporcionar algunas operaciones sql de más alto nivel. .
Hice una pequeña clase para rastrear los diferentes bits de cosas:
public class CleanupList
{
private final ArrayList<AutoCloseable> _disposables;
public CleanupList()
{
_disposables = new ArrayList<>();
}
public void cleanup()
{
for(AutoCloseable closeable : _disposables){
//it sucks that they put an exception on this interface
//if anyone actually throws an exception in a close method then there''s something seriously wrong going on
//they should have copied the c# more closely imo as it has nicer syntax aswell
try
{
closeable.close();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
_disposables.clear();
}
public <T extends AutoCloseable> T track(T statement)
{
_disposables.add(statement);
return statement;
}
}
Y luego, por ejemplo, en Wrapped Connection (que es algo que envuelve una conexión de base de datos):
public class WrappedConnection implements AutoCloseable
{
private final CleanupList _cleanupList;
private Connection _connection;
public WrappedConnection(Connection connection)
{
_connection = connection;
_cleanupList = new CleanupList();
}
public void close()
{
try
{
_connection.close();
_cleanupList.cleanup();
}
catch (SQLException e)
{
throw new RuntimeException(e);
}
}
public PreparedStatement prepareStatement(String sql)
{
try
{
return trackForDisposal(_connection.prepareStatement(sql));
}
catch (SQLException e)
{
throw new RuntimeException(e);
}
}
private <T extends AutoCloseable> T trackForDisposal(T statement)
{
return _cleanupList.track(statement);
}
.... lots more methods
}
También puede pasar la misma lista en versiones envueltas de los conjuntos PreparadoEstado / Resultado (que no he mostrado aquí), etc. y usarlo de una manera similar.
No sé lo que otras personas están usando, pero en IDEA puede activar una advertencia para las cosas que se pueden cerrar automáticamente que no están en un bloque de uso (o debería decir intente con recursos):
try(SomethingThatNeedsClosing somethingThatNeedsClosing = new SomethingThatNeedsClosing()){
//do stuff
}
Estos bloques de uso intentan finalmente cerrarse automáticamente y solo pueden usarse con elementos del tipo de interfaz AutoClosable
No sé por qué esta advertencia no está activada por defecto en IDEA, pero ahí lo tienes.