Essayez d'écrire une source qui provoque une astuce et une erreur ORA-1000 dans une variable de liaison SQL qui utilise PreparedStatement de Java pour trouver une solution de contournement.

Erreur ORA-1000 ouverte dans la session Oracle Database Cette erreur se produit lorsque le nombre de curseurs dépasse la valeur maximale.

ORA-1000 Le nombre maximum de curseurs ouverts a été dépassé.

Préparation

Essayons-le immédiatement. 彡 (゜) (゜) Commencez par préparer la table.

CREATE TABLE TBL_A(
    C1 NUMBER
  , C2 VARCHAR2(30)
);

Source Java qui provoque une erreur ORA-1000 (* anti-pattern)

En SQL, qui est une variable de liaison de PreparedStatement, La source est écrite de manière à ce que les astuces et les erreurs ORA-1000 se produisent.

Il existe une méthode prepareStatement dans la boucle for, et le curseur est déplacé à chaque fois. Il sera nouvellement ouvert et une erreur se produira. 彡 (゚) (゚)

C'est plein de putains d'éléments de code, mais si je saute Masakari (Akan 彡 (-) (-)

import java.sql.*;
import java.util.Date;

public class InsertTest {
    public static void main(String[] args) throws Exception {
        //DB connection info
        final String path = "jdbc:oracle:thin:@127.0.0.1:1521/orcl";  //path
        final String id = "xxxxxxxx";  //ID
        final String pw = "yyyyyyyy";  //password
        int i;
        Connection conn = null;
        PreparedStatement ps = null;

        System.out.println(new Date() + " Connect...");
        try {
            //DB Connect
            conn = DriverManager.getConnection(path, id, pw);
            //AutoCommit Setting
            conn.setAutoCommit(false);
            //Insert Execute
            System.out.println(new Date() + " Insert...");
            for (i = 1; i <= 2000; i++) {
                //Prepared Statement Set.
                ps = conn.prepareStatement("INSERT INTO TBL_A (C1, C2) VALUES (?, ?)");
                ps.setInt(1, i);
                ps.setString(2, "A" + String.valueOf(i));
                ps.execute();
            }
            //Commit
            conn.commit();
            //Close
            conn.close();
        } catch(SQLException ex) {
            conn.rollback();
            ex.printStackTrace();
            System.exit(1);
        } finally {
            if (ps != null) { ps.close(); }
            if (conn != null) { conn.close(); }
        }
        //End
        System.out.println(new Date() + " End...");
    }
}

Lorsqu'elle est exécutée, l'erreur ORA-1000 suivante se produit. 彡 (゚) (゚)

$ javac ./InsertTest.java
$ java -classpath .:${ORACLE_HOME}/jdbc/lib/ojdbc8.jar InsertTest
Thu Nov 30 00:57:01 JST 2017 Connect...
Thu Nov 30 00:57:23 JST 2017 Insert...
java.sql.SQLException: ORA-01000: maximum open cursors exceeded

        at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:494)
        at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:446)
        at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:1054)
        at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:623)
        at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:252)
        at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:612)
        at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:226)
        at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:59)
        at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:910)
        at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1119)
        at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3780)
        at oracle.jdbc.driver.T4CPreparedStatement.executeInternal(T4CPreparedStatement.java:1343)
        at oracle.jdbc.driver.OraclePreparedStatement.execute(OraclePreparedStatement.java:3887)
        at oracle.jdbc.driver.OraclePreparedStatementWrapper.execute(OraclePreparedStatementWrapper.java:1079)
        at InsertTest.main(InsertTest.java:27)
Caused by: Error : 1000, Position : 0, Sql = INSERT INTO TBL_A (C1, C2) VALUES (:1 , :2 ), OriginalSql = INSERT INTO TBL_A (C1, C2) VALUES (?, ?), Error Msg = ORA-01000: maximum open cursors exceeded

        at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:498)
        ... 14 more
$ echo $?
1

Solution de contournement 1. Placez prepareStatement en dehors de la boucle for.

Si vous mettez le prepareStatement en dehors de la boucle for Vous pouvez éviter l'erreur ORA-1000. Est-ce vrai? 彡 (゜) (゜)

:
//Insert Execute
System.out.println(new Date() + " Insert...");
//Prepared Statement Set.
ps = conn.prepareStatement("INSERT INTO TBL_A (C1, C2) VALUES (?, ?)");
for (i = 1; i <= 2000; i++) {
    ps.setInt(1, i);
    ps.setString(2, "A" + String.valueOf(i));
    ps.execute();
}
//Commit
conn.commit();
:

Solution de contournement 2. Vérifiez la valeur null de l'objet et exécutez prepareStatement uniquement pour la première fois.

Vérification nulle de l'objet et du premier prepareStatement Si vous le faites, vous pouvez éviter l'erreur ORA-1000.

:
//Insert Execute
System.out.println(new Date() + " Insert...");
for (i = 1; i <= 2000; i++) {
    //Prepared Statement Set.
    if (ps == null) {
        ps = conn.prepareStatement("INSERT INTO TBL_A (C1, C2) VALUES (?, ?)");
    }
    ps.setInt(1, i);
    ps.setString(2, "A" + String.valueOf(i));
    ps.execute();
}
//Commit
conn.commit();
:

Solution de contournement 3. Adoptez la syntaxe try-with-resources.

Ce sera une variante de la solution de contournement 1. 彡 (゚) (゚) Si vous adoptez la syntaxe try-with-resources que vous avez enseignée dans l'article précédent, Éviter la première écriture anti-pattern "naturellement" L'erreur ORA-1000 ne se produit pas. Le point est "naturellement".

La source est propre et toutes les bonnes choses sont 彡 (゚) (゚)

import java.sql.*;
import java.util.Date;

public class InsertTest {
    public static void main(String[] args) {
        //DB connection info
        final String path = "jdbc:oracle:thin:@127.0.0.1:1521/orcl";  //path
        final String id = "xxxxxxxx";  //ID
        final String pw = "yyyyyyyy";  //password

        //try-with-resources Statement
        System.out.println(new Date() + " Connect...");
        try (
            //DB Connect
            Connection conn = DriverManager.getConnection(path, id, pw);
            //Prepared Statement Set.
            PreparedStatement ps = conn.prepareStatement("INSERT INTO TBL_A (C1, C2) VALUES (?, ?)");
        ) {
            //Initialize
            int i;
            //AutoCommit Setting
            conn.setAutoCommit(false);
            //Insert Execute
            System.out.println(new Date() + " Insert...");
            for (i = 1; i <= 2000; i++) {
                ps.setInt(1, i);
                ps.setString(2, "A" + String.valueOf(i));
                ps.execute();
            }
            //Commit
            conn.commit();
        } catch(SQLException ex) {
            ex.printStackTrace();
            System.exit(1);
        }
        //End
        System.out.println(new Date() + " End...");
    }
}

Solution de contournement 4. Fermez () l'objet PreparedStatement à chaque fois. * Ajouté le 27/11/2017

Objet PreparedStatement dans une boucle Vous pouvez éviter l'erreur ORA-1000 en fermant () à chaque fois.

:
//Insert Execute
System.out.println(new Date() + " Insert...");
for (i = 1; i <= 2000; i++) {
    //Prepared Statement Set.
    ps = conn.prepareStatement("INSERT INTO TBL_A (C1, C2) VALUES (?, ?)");
    ps.setInt(1, i);
    ps.setString(2, "A" + String.valueOf(i));
    ps.execute();
    ps.close();
}
//Commit
conn.commit();
:

Est-ce difficile d'analyser chaque fois que j'écris de cette façon? J'ai pensé, L'analyse dure peut être évitée à chaque fois avec la fonction de cache d'instructions

À propos du cache d'instructions https://docs.oracle.com/cd/E16338_01/java.112/b56281/stmtcach.htm#i1069942 : -Evite les analyses répétées et la création de phrases. :

Bonus (* Ajouté le 30/11/2017)

Une autre personne a demandé: "La portée des variables dans l'anti-modèle n'est-elle pas étrange en premier lieu?" On m'a fait remarquer, donc si je modifie la source pour déclarer la variable dans la clause try ... Eh bien, cela devient naturellement un code de type 1. solution de contournement. La syntaxe try-with-resources est importante, n'est-ce pas? (゜) (゜)

import java.sql.*;
import java.util.Date;

public class InsertTest {
    public static void main(String[] args) {
        //DB connection info
        final String path = "jdbc:oracle:thin:@127.0.0.1:1521/orcl";  //path
        final String id = "xxxxxxxx";  //ID
        final String pw = "yyyyyyyy";  //password

        System.out.println(new Date() + " Connect...");
        try {
            //Init
            int i;
            //DB Connect
            Connection conn = DriverManager.getConnection(path, id, pw);
            //AutoCommit Setting
            conn.setAutoCommit(false);
            //Prepared Statement Set.
            PreparedStatement ps = conn.prepareStatement("INSERT INTO TBL_A (C1, C2) VALUES (?, ?)");
            //Insert Execute
            System.out.println(new Date() + " Insert...");
            for (i = 1; i <= 2000; i++) {
                ps.setInt(1, i);
                ps.setString(2, "A" + String.valueOf(i));
                ps.execute();
            }
            //Commit
            conn.commit();
            //Close
            ps.close();
            conn.close();
        } catch(SQLException ex) {
            ex.printStackTrace();
            System.exit(1);
        }
        //End
        System.out.println(new Date() + " End...");
    }
}

Bonus 2. Exécutez SQL avec addBatch () et executeBatch () (* Ajouté 2019/8/20)

J'ai reçu un commentaire sur addBatch dans les commentaires, je l'ai donc modifié en fonction de la source supplémentaire. Je me demande si ça va comme ça ... 彡 (゚) (゚) Le manuel pour addBatch (mise à jour par lots) est [ici](https://docs.oracle.com/cd/F19136_01/jjdbc/performance-extensions.html#GUID -FEECA64F-44F4-453F-B8A8-AFBF6D29ABA4)

import java.sql.*;
import java.util.Date;

public class InsertTest {
    public static void main(String[] args) {
        //DB connection info
        final String path = "jdbc:oracle:thin:@127.0.0.1:1521/orcl";  //path
        final String id = "xxxxxxxx";  //ID
        final String pw = "yyyyyyyy";  //password

        System.out.println(new Date() + " Connect...");
        try {
            //Init
            int i;
            //DB Connect
            Connection conn = DriverManager.getConnection(path, id, pw);
            //AutoCommit Setting
            conn.setAutoCommit(false);
            //Prepared Statement Set.
            PreparedStatement ps = conn.prepareStatement("INSERT INTO TBL_A (C1, C2) VALUES (?, ?)");
            //Insert Execute
            System.out.println(new Date() + " Insert...");
            for (i = 1; i <= 2000; i++) {
                ps.setInt(1, i);
                ps.setString(2, "A" + String.valueOf(i));
                ps.addBatch();
            }
            //Execute(batch updates)
            int[] updateCounts = ps.executeBatch();
            System.out.println(new Date() + " Batch Counts..." + updateCounts.length);
            //Commit
            conn.commit();
            //Close
            ps.close();
            conn.close();
        } catch(SQLException ex) {
            ex.printStackTrace();
            System.exit(1);
        }
        //End
        System.out.println(new Date() + " End...");
    }
}
$ javac ./InsertTest.java
$ java -classpath .:/u01/app/oracle/product/version/db_1/jdbc/lib/ojdbc8.jar InsertTest
Tue Aug 20 04:18:11 EDT 2019 Connect...
Tue Aug 20 04:18:13 EDT 2019 Insert...
Tue Aug 20 04:18:14 EDT 2019 Batch Counts...2000
Tue Aug 20 04:18:14 EDT 2019 End...
$

Recommended Posts

Essayez d'écrire une source qui provoque une astuce et une erreur ORA-1000 dans une variable de liaison SQL qui utilise PreparedStatement de Java pour trouver une solution de contournement.
[Docker] Comment mettre à jour à l'aide d'un conteneur dans Heroku et comment gérer l'erreur de migration
Mémo pour écrire silencieusement la source qui semble utilisable et le résultat de la compilation execution-java