web-dev-qa-db-fra.com

Exécuter plusieurs requêtes à l'aide d'un seul objet de déclaration JDBC

Veuillez lire avant de marquer ceci comme doublon. J'ai beaucoup cherché sur google et SO, mais je n'ai pas pu trouver la réponse précise que je cherche.

Ma question: Dans JDBC, puis-je utiliser un seul objet Statement pour appeler plusieurs fois executeQuery("")? Est-ce sûr? OR dois-je fermer l'objet d'instruction après chaque requête et créer un nouvel objet pour exécuter une autre requête.

PAR EXEMPLE:

Connection con;
Statement s;
ResultSet rs;
ResultSet rs2;
try
{
    con = getConnection();
    s = con.prepareStatement();

    try
    {
        rs = s.executeQuery(".......................");

        // process the result set rs
    }
    finally
    {
        close(rs);
    }

    // I know what to do to rs here
    // But I am asking, should I close the Statement s here? Or can I use it again for the next query?

    try
    {
        rs2 = s.executeQuery(".......................");

        // process the result set rs2
    }
    finally
    {
        close(rs2);
    }
}
finally
{
    close(s);
    close(con);
}
27
Syed Aqeel Ashiq

Oui, vous pouvez réutiliser un Statement (en particulier un PreparedStatement) et devez le faire en général avec JDBC. Ce serait un style inefficace et mauvais si vous ne réutilisiez pas votre instruction et créiez immédiatement un autre objet Statement identique. En ce qui concerne la fermeture, il serait approprié de la fermer dans un bloc enfin, tout comme vous le faites dans cet extrait.

Pour un exemple de ce que vous demandez, consultez ce lien: jOOq Docs

23
Durandal

Je ne sais pas pourquoi vous demandez. La conception et la documentation de l'API montrent qu'il est parfaitement correct (et même prévu) de réutiliser un objet Statement pour plusieurs appels execute, executeUpdate et executeQuery. Si cela n'était pas autorisé, cela serait explicitement documenté dans le document Java doc (et probablement l'API serait différente).

De plus, l'apidoc de Statement dit:

Toutes les méthodes d'exécution de l'interface Statement ferment implicitement l'objet [ sic ] actuel ResultSet d'un état s'il en existe un ouvert .

Ceci indique que vous pouvez l'utiliser plusieurs fois.

TL; DR: Oui, vous pouvez appeler plusieurs fois execute sur un seul objet Statement, tant que vous vous rendez compte que tout ResultSet précédemment ouvert sera fermé.

Votre exemple utilise incorrectement PreparedStatement, et vous ne pouvez pas (ou: ne devriez pas) être en mesure d'appeler l'un des execute... méthodes acceptant un String sur un PreparedStatement:

SQLException - si [...] la méthode est appelée sur un PreparedStatement ou CallableStatement

Mais pour répondre à PreparedStatement également: le but d'un PreparedStatement est de précompiler une instruction avec des espaces réservés de paramètre et de la réutiliser pour plusieurs exécutions avec différentes valeurs de paramètre.

9
Mark Rotteveel

Je ne trouve rien dans les documentation API qui indiquerait que vous ne devez pas appeler plus d'une fois executeQuery() sur une instance PreparedStatement donnée.

Cependant, votre code ne ferme pas le PreparedStatement - un appel à executeQuery() lancerait un SQLException dans ce cas - mais le ResultSet qui est retourné par executeQuery(). Un ResultSet est automatiquement fermé , lorsque vous réexécutez un PreparedStatement. Selon votre situation, vous devez le fermer lorsque vous n'en avez plus besoin. Je voudrais le fermer, car je pense que c'est un mauvais style de ne pas le faire.

[~ # ~] mise à jour [~ # ~] Upps, j'ai raté votre commentaire entre les deux blocs d'essai. Si vous fermez votre PreparedStatement à ce stade, vous ne devriez pas être en mesure d'appeler à nouveau executeQuery () sans obtenir une exception SQLException.

4
Grmpfhmbl

UNE Prepared Statement indique à la base de données de se souvenir de votre requête et d'être prête à accepter les variables paramétrées à exécuter dans cette requête. Cela ressemble beaucoup à une procédure stockée.

Prepared Statement accomplit deux choses principales:

  1. Il échappe automatiquement à vos variables de requête pour vous protéger contre l'injection SQL.

  2. Il indique à la base de données de se souvenir de la requête et d'être prêt à prendre des variables.

Le numéro 2 est important, car cela signifie que la base de données n'a à interpréter votre requête qu'une seule fois, puis que la procédure est prête à démarrer. Cela améliore donc les performances.

Vous ne devez pas fermer une instruction préparée et/ou la connexion à la base de données entre les appels d'exécution. Cela est incroyablement inefficace et entraînera plus de surcharge que l'utilisation d'un ancien Statement simple, car vous demandez à la base de données à chaque fois de créer une procédure et de vous en souvenir. Même si la base de données est configurée pour les "points chauds" et se souvient quand même de votre requête même si vous fermez le PreparedStatement, vous encourez toujours une surcharge réseau ainsi qu'un temps de traitement réduit.

En bref, laissez les Connection et PreparedStatement ouverts jusqu'à ce que vous en ayez fini.

Edit: Pour commenter le non retour d'un ResultSet de l'exécution, c'est très bien. executeQuery renverra le ResultSet pour la requête qui vient d'être exécutée.

3
SnakeDoc

Tout d'abord, je suis confus au sujet de votre code

s = con.prepareStatement();

Est-ce que cela fonctionne bien? Je ne trouve pas une telle fonction dans Java API, au moins un paramètre est nécessaire. Peut-être voulez-vous invoquer cette fonction

s = con.createStatement();

Je viens d'exécuter mon code pour accéder à DB2 deux fois avec une seule instance de déclaration sans le fermer entre deux opérations. Cela fonctionne bien. Je pense que vous pouvez également l'essayer vous-même.

    String sql = "";
    String sql2 = "";
    String driver = "com.ibm.db2.jcc.DB2Driver";
    String url = "jdbc:db2://ip:port/DBNAME";
    String user = "user";
    String password = "password";
    Class.forName(driver).newInstance();
    Connection conn = DriverManager.getConnection(url, user, password);
    Statement statement = conn.createStatement();
    ResultSet resultSet = statement.executeQuery(sql);
    int count = 0;
    while (resultSet.next()) {
        count++;
    }
    System.out.println("Result row count of query number one is: " + count);
    count = 0;
    resultSet = statement.executeQuery(sql2);
    while (resultSet.next()) {
        count++;
    }
    System.out.println("Result row count of query number two is: " + count);
1
mojiayi