2010-11-03 10 views
2

Dans le contexte d'une application Java utilisant SQLIte pour persister des données, j'utilise le pilote JDBC Zentus. J'utilise donc le package java.sql pour accéder à ma base de données.Existe-t-il un outil ou une technique pour identifier ouvert ResultSet

Je suis confronté à des problèmes étranges (dans un environnement avec plusieurs objets Connexion sur la même base de données) et je suis assez sûr que mes problèmes proviennent de ResultSet non fermé.

Existe-t-il un outil ou une technique me permettant de repérer où chercher dans mon code source pour trouver ces objets non fermés?

Modifier Peut-être utiliser AspectJ ??

Répondre

3

Il semble qu'un aspect puisse être utile.

Que diriez-vous d'envelopper les méthodes qui retournent un ensemble de résultats dans un aspect. Quelque chose comme:

execution(public java.sql.ResultSet+ java.sql.Statement+.*(..)) 

Un autre aspect peut surveiller la méthode close sur ResultSets. Peut-être:

execution(public * java.sql.ResultSet.close()) 

Le premier aspect serait, au retour de chaque ResultSet, créez un nouvel objet d'exception et le stocker dans une carte statique en utilisant quelque part le hachage de la clé comme ResultSet. Le deuxième aspect, à la fermeture de l'ensemble de résultats, supprimerait l'exception de la carte en utilisant le même code comme une clé. A tout moment, la carte doit avoir une instance d'exception pour chaque ResultSet ouvert. De l'exception, vous pouvez obtenir une trace de pile pour voir où le ResultSet a été ouvert.

Vous pourriez peut-être stocker un objet plus grand qui inclut une exception et d'autres informations contextuelles; temps que le ResultSet a été créé, etc.

1

Un Google Search m'a pointé directement à JAMon. It vous permet également de surveiller les connexions et les curseurs JDBC.

Personnellement, je voudrais vérifier le code et assurez-vous que tous Déclaration, PreparedStatement et ResultSet sont fermés lorsqu'ils ne sont pas nécessaires. Même en utilisant Connection Pooling, seules les connexions JDBC sont renvoyées dans le pool et les instructions et ResultSet sont fermées.

Cet exemple montre comment je réalise la fermeture ResultSet et PreparedStatement dans le enfin close (pour la garantie):

PreparedStatement ps = null; 
ResultSet rs = null; 
UserRequest request = null; 

try { 
ps = getConnection().prepareStatement(SQL_RETRIEVE); 
ps.setLong(1, id); 
rs = ps.executeQuery(); 
if (rs != null && rs.next()) { 
    request = mapEntity(rs); 
} 
} catch (SQLException e) { 
// TODO Auto-generated catch block 
throw new DAOException(e); 
} finally { 
try { 
    close(rs, ps); 
} catch (SQLException e) { 
    // TODO Auto-generated catch block 
    logger.error("Error closing statement or resultset.", e); 
} 
} 

Ce sont mes 2 cents vaut la peine ... il vous aide à espérer.

+0

Merci pour le look JAMon. La solution manuelle pour "vérifier le code et s'assurer que tout Statement, PreparedStatement et ResultSet sont fermés" est ennuyeux ;-) et je veux l'éviter. –

+0

La gestion des curseurs ouverts à l'aide d'un outil n'est pas aussi simple. Une chose que vous devrez faire est de voir si ces curseurs sont utilisés ou stagnants et ce n'est pas facile à dire. –

2

Une suggestion pratique consiste à ajouter un peu de code de débogage et de création et de fermeture de fichiers de résultats dans un fichier csv. Plus tard, vous pouvez examiner ce fichier et vérifier s'il y a une entrée "close" pour chaque "create".

Donc, en supposant que vous avez une classe utilitaire avec des méthodes statiques qui permet aux cordes d'écriture dans un fichier, vous pouvez le faire comme ceci:

ResultSet rs = stmt.executeQuery(query); 
Util.writeln(rs.hashcode() + ";create"); // add this line whenever a 
             // new ResultSet is created 

et

rs.close(); 
Util.writeln(rs.hashcode() + ";closed"); // add this line whenever a 
             // ResultSet is closed 

Ouvrez le fichier csv avec Excel ou tout autre programme de feuille de calcul, trie la table et regarde si les ensembles de résultats ne sont pas fermés. Si c'est le cas, ajoutez plus d'informations de débogage pour identifier clairement les ensembles ouverts.


BTW - Enveloppement des interfaces (comme JAMon) est assez facile, si vous avez éclipser ou quelque chose d'autre, son code en moins de 15 minutes. Vous auriez besoin d'enrouler Connection, Statement (et PreparedStatement?) Et ResultSet, l'emballage ResultSet pourrait être instrumenté pour suivre et surveiller la création et la fermeture des jeux de résultats:

public MonitoredConnection implements Connection { 
    Connection wrappedConnection = null; 

    public MonitoredConnection(Connection wrappedConnection) { 
    this.wrappedConnection = wrappedConnection; 
    } 

    // ... implement interface methods and delegate to the wrappedConnection 

    @Override 
    public Statement createStatement() { 
    // we need MonitoredStatements because later we want MonitoredResultSets 
    return new MonitoredStatement(wrappedConnection.createStatemet()); 
    } 

    // ... 
} 

Même chose pour MonitoredStatement et MonitoredResultSet (MonitoredStatement retournera enveloppées ResultSets):

public MonitoredStatement implements Statement { 
    private Statement wrappedStatement = null; 

    @Override 
    public ResultSet executeQuery(String sql) throws SQLException 
    MonitoredResultSet rs = wrappedStatement.executeQuery(sql); 
    ResultSetMonitor.create(rs.getWrappedResultSet()); // some static utility class/method 
    return rs; 
    } 

    // ... 
} 

et

public MonitoredResultSet implements ResultSet { 
    private ResultSet wrappedResultSet; 

    @Override 
    public void close() { 
    wrappedResultSet.close(); 
    ResultSetMonitor.close(wrappedResultSet); // some static utility class/method 
    } 

    // ... 
} 

a la fin, vous ne devriez avoir besoin de modifier une seule ligne dans votre code:

Connection con = DriverManager.getConnection(ur); 

à

Connection con = new MonitoredConnection(DriverManager.getConnection(ur)); 
+0

Mon problème est que j'ai beaucoup d'appel à stmt.executeQuery et donc je cherche un moyen d'éviter d'avoir heck manuellement le code et assurez-vous que tous les Statement, PreparedStatement et ResultSet sont fermés. Manu –

+0

Pas une mauvaise idée! @Andreas_D –

1

Il devrait être relativement simple instrument avec votre code AOP de votre choix. Il y a quelques années, j'utilisais AspectWerkz pour faire du tissage de l'application Web au moment du chargement et recueillir des statistiques sur les performances. Aussi, si vous utilisez un framework IOC, tel que Spring, il est très facile d'enrouler vos DataSources et de tracer les appels à getConnection(), etc.