Je reçois une exception SQL ORA-01000. J'ai donc quelques questions à ce sujet.
L'exécution d'une instruction préparée dans une boucle est-elle à l'origine de ce problème? (Bien sûr, j'aurais pu utiliser sqlBatch) Remarque: pStmt est fermé une fois la boucle terminée.
{ //method try starts
String sql = "INSERT into TblName (col1, col2) VALUES(?, ?)";
pStmt = obj.getConnection().prepareStatement(sql);
pStmt.setLong(1, subscriberID);
for (String language : additionalLangs) {
pStmt.setInt(2, Integer.parseInt(language));
pStmt.execute();
}
} //method/try ends
{ //finally starts
pStmt.close()
} //finally ends
Que se passera-t-il si conn.createStatement () et conn.prepareStatement (sql) sont appelées plusieurs fois sur un objet de connexion unique?
Edit1: 6. L'utilisation de l'objet de déclaration Weak/Soft aidera-t-elle à prévenir les fuites?
Edit2: 1. Y at-il un moyen, je peux trouver tous les "statement.close ()" manquants dans mon projet? Je comprends que ce n'est pas une fuite de mémoire. Mais je dois trouver une référence d'instruction (où close () n'est pas exécuté) éligible pour le ramassage des ordures? Un outil disponible? Ou dois-je l'analyser manuellement?
S'il vous plaît aidez-moi à le comprendre.
Allez à la machine ORALCE et démarrez sqlplus en tant que sysdba.
[Oracle@db01 ~]$ sqlplus / as sysdba
Puis courir
SELECT A.VALUE,
S.USERNAME,
S.SID,
S.SERIAL#
FROM V$SESSTAT A,
V$STATNAME B,
V$SESSION S
WHERE A.STATISTIC# = B.STATISTIC#
AND S.SID = A.SID
AND B.NAME = 'opened cursors current'
AND USERNAME = 'VELU';
Si possible, veuillez lire ma réponse à la fin.
ORA-01000, l'erreur de maximum-open-cursors, est une erreur extrêmement courante dans le développement de bases de données Oracle. Dans le contexte de Java, cela se produit lorsque l'application tente d'ouvrir plus de ResultSets qu'il n'y a de curseurs configurés sur une instance de base de données.
Les causes communes sont:
Erreur de configuration
Solution:
Fuite de curseur
Cette section décrit une partie de la théorie sous-jacente aux curseurs et explique comment utiliser JDBC. Si vous n'avez pas besoin de connaître l'arrière-plan, vous pouvez ignorer ceci et aller directement à "Eliminer les fuites".
Un curseur est une ressource de la base de données qui contient l’état d’une requête, en particulier la position d’un lecteur dans un ResultSet. Chaque instruction SELECT a un curseur et les procédures stockées PL/SQL peuvent s'ouvrir et utiliser autant de curseurs que nécessaire. Vous pouvez en savoir plus sur les curseurs sur Orafaq .
Une instance de base de données sert généralement plusieurs schémas, de nombreux tilisateurs chacun avec plusieurs sessions. Pour ce faire, un nombre fixe de curseurs est disponible pour tous les schémas, utilisateurs et sessions. Lorsque tous les curseurs sont ouverts (en cours d'utilisation) et qu'une requête nécessite un nouveau curseur, elle échoue avec une erreur ORA-010000.
Le numéro est normalement configuré par le DBA lors de l'installation. Le nombre de curseurs actuellement utilisés, le nombre maximal et la configuration sont accessibles dans les fonctions d’administrateur de Oracle SQL Developer . A partir de SQL, il peut être défini avec:
ALTER SYSTEM SET OPEN_CURSORS=1337 SID='*' SCOPE=BOTH;
Les objets JDBC ci-dessous sont étroitement liés aux concepts de base de données suivants:
JDBC est thread-safe: il est tout à fait correct de passer les différents objets JDBC entre les threads.
Par exemple, vous pouvez créer la connexion dans un seul thread. un autre thread peut utiliser cette connexion pour créer un PreparedStatement et un troisième thread peut traiter le jeu de résultats. La principale restriction principale est que vous ne pouvez pas avoir plus d'un ResultSet ouvert sur un seul PreparedStatement à tout moment. Voir Oracle DB prend-il en charge plusieurs opérations (parallèles) par connexion?
Notez qu'une validation de base de données a lieu sur une connexion et que tous les DML (INSERT, UPDATE et DELETE) de cette connexion seront validés ensemble. Par conséquent, si vous souhaitez prendre en charge plusieurs transactions simultanément, vous devez disposer d'au moins une connexion pour chaque transaction simultanée.
Voici un exemple typique d’exécution d’un ResultSet:
Statement stmt = conn.createStatement();
try {
ResultSet rs = stmt.executeQuery( "SELECT FULL_NAME FROM EMP" );
try {
while ( rs.next() ) {
System.out.println( "Name: " + rs.getString("FULL_NAME") );
}
} finally {
try { rs.close(); } catch (Exception ignore) { }
}
} finally {
try { stmt.close(); } catch (Exception ignore) { }
}
Notez que la clause finally ignore toutes les exceptions déclenchées par close ():
Dans Java 7, Oracle a introduit le interface auto-verrouillable , qui remplace la plupart du Java 6 par un sucre syntaxique de Nice.
Les objets JDBC peuvent être conservés en toute sécurité dans des variables locales, des instances d'objet et des membres de classe. Il est généralement préférable de:
Il existe toutefois une exception: si vous utilisez des EJB ou un conteneur Servlet/JSP, vous devez suivre un modèle de thread strict:
Un certain nombre de processus et d'outils sont disponibles pour aider à détecter et à éliminer les fuites JDBC:
Pendant le développement, la meilleure approche consiste de loin à détecter les bogues.
Pratiques de développement: de bonnes pratiques de développement devraient permettre de réduire le nombre de bogues dans votre logiciel avant qu'il ne quitte le bureau du développeur. Les pratiques spécifiques incluent:
Analyse de code statique: Utilisez un outil tel que l'excellent Findbugs pour effectuer une analyse de code statique. Cela détecte beaucoup d’endroits où la fermeture () n’a pas été gérée correctement. Findbugs a un plugin pour Eclipse, mais il fonctionne aussi de manière autonome pour des one-offs, il a des intégrations dans Jenkins CI et d'autres outils de construction
À l'exécution:
Holdability et commit
Enregistrement au moment de l'exécution.
Vous pouvez ajouter un pilote JDBC de débogage à votre projet (pour le débogage, ne le déployez pas réellement). Un exemple (je ne l'ai pas utilisé) est log4jdbc . Vous devez ensuite effectuer une analyse simple sur ce fichier pour voir quelles exécutions n'ont pas de fermeture correspondante. Compter les ouvertures et les fermetures devrait mettre en évidence s'il y a un problème potentiel
Les références faibles et faibles sont des moyens de vous permettre de référencer un objet de manière à ce que la machine virtuelle Java puisse récupérer le référent à tout moment de la façon qu'elle juge appropriée (en supposant qu'aucune chaîne de référence forte ne soit associée à cet objet).
Si vous transmettez une référenceQueue dans le constructeur à la référence souple ou faible, l'objet est placé dans la référenceQueue lorsqu'il est GC'ed lorsqu'il survient (si cela se produit du tout). Avec cette approche, vous pouvez interagir avec la finalisation de l'objet et vous pouvez fermer ou finaliser l'objet à ce moment-là.
Les références fantômes sont un peu plus bizarres; leur but est uniquement de contrôler la finalisation, mais vous ne pouvez jamais obtenir une référence à l'objet d'origine, il sera donc difficile d'appeler la méthode close () dessus.
Cependant, il est rarement judicieux d'essayer de contrôler le moment où le CPG est exécuté (Weak, Soft et PhantomReferences vous permettent de savoir après le fait que l'objet est mis en file d'attente pour GC). En fait, si la quantité de mémoire dans la machine virtuelle est grande (par exemple -Xmx2000m), vous pourriez jamais GC, et vous rencontrerez toujours l'ORA-01000. Si la mémoire de la machine virtuelle Java est petite par rapport aux exigences de votre programme, vous constaterez peut-être que les objets ResultSet et PreparedStatement sont convertis immédiatement après la création (avant de pouvoir les lire), ce qui entraînera probablement l'échec de votre programme.
TL; DR: Le mécanisme de référence faible n'est pas un bon moyen de gérer et de fermer les objets Statement et ResultSet.
J'ajoute un peu plus de compréhension.
Loggin en tant que sysdba.
Dans PuTTY (login Oracle):
[Oracle@db01 ~]$ sqlplus / as sysdba
En SqlPlus:
Nom d'utilisateur: sys as sysdba
alter session set session_cached_cursors=0
select * from V$PARAMETER where name='session_cached_cursors'
SELECT max(a.value) as highest_open_cur, p.value as max_open_cur FROM v$sesstat a, v$statname b, v$parameter p WHERE a.statistic# = b.statistic# AND b.name = 'opened cursors current' AND p.name= 'open_cursors' GROUP BY p.value;
SELECT a.value, s.username, s.sid, s.serial#
FROM v$sesstat a, v$statname b, v$session s
WHERE a.statistic# = b.statistic# AND s.sid=a.sid
AND b.name = 'opened cursors current' AND username = 'SCHEMA_NAME_IN_CAPS'
SELECT oc.sql_text, s.sid
FROM v$open_cursor oc, v$session s
WHERE OC.sid = S.sid
AND s.sid=1604
AND OC.USER_NAME ='SCHEMA_NAME_IN_CAPS'
Maintenant, déboguez le code et profitez-en !!! :)
Corrigez votre code comme ceci:
try
{ //method try starts
String sql = "INSERT into TblName (col1, col2) VALUES(?, ?)";
pStmt = obj.getConnection().prepareStatement(sql);
pStmt.setLong(1, subscriberID);
for (String language : additionalLangs) {
pStmt.setInt(2, Integer.parseInt(language));
pStmt.execute();
}
} //method/try ends
finally
{ //finally starts
pStmt.close()
}
Etes-vous sûr, que vous fermez vraiment vos déclarations, connexions et résultats?
Pour analyser les objets ouverts, vous pouvez implémenter un motif de délégation qui enveloppe le code autour de vos objets statémant, connexion et résultat. Donc, vous verrez si un objet sera fermé avec succès.
Un exemple pour: pStmt = obj. getConnection (). PrepareStatement (sql);
class obj{
public Connection getConnection(){
return new ConnectionDelegator(...here create your connection object and put it into ...);
}
}
class ConnectionDelegator implements Connection{
Connection delegates;
public ConnectionDelegator(Connection con){
this.delegates = con;
}
public Statement prepareStatement(String sql){
return delegates.prepareStatement(sql);
}
public void close(){
try{
delegates.close();
}finally{
log.debug(delegates.toString() + " was closed");
}
}
}
Si votre application est une application Java EE exécutée sur Oracle WebLogic en tant que serveur d'applications, une cause possible de ce problème est le paramètre Statement Cache Size de WebLogic.
Si le paramètre Taille du cache d'instructions d'une source de données particulière est égal ou supérieur au paramètre de nombre de curseurs ouverts de la base de données Oracle, tous les curseurs ouverts peuvent être utilisés par les instructions SQL en cache maintenues ouvertes par WebLogic, ce qui entraîne dans l'erreur ORA-01000.
Pour résoudre ce problème, réduisez le paramètre Taille du cache de l'instruction pour chaque source de données WebLogic pointant vers la base de données Oracle comme étant considérablement inférieur au paramètre de nombre de curseurs maximal défini dans la base de données.
Dans WebLogic 10 Admin Console, le paramètre Taille du cache des instructions de chaque source de données est disponible dans Services (navigation de gauche)> Sources de données> (source de données individuelle)> onglet Pool de connexions.
J'ai rencontré le même problème (ORA-01000) aujourd'hui. J'avais une boucle for dans le try {}, pour exécuter plusieurs fois une instruction SELECT dans une base de données Oracle (chaque fois que je changeais un paramètre), et enfin dans le}} j'avais le code pour fermer Resultset, PreparedStatement et Connection comme d'habitude . Mais dès que j'ai atteint un nombre spécifique de boucles (1000), j'ai eu l'erreur Oracle concernant trop de curseurs ouverts.
Sur la base du message d’Andrew Alcock ci-dessus, j’ai apporté des modifications pour que inside the loop, j’aie fermé chaque résultat et chaque instruction après avoir récupéré les données et avant de répéter la boucle, ce qui a résolu le problème.
De plus, le même problème est survenu dans une autre boucle d'instructions d'insertion, dans une autre base de données Oracle (ORA-01000), cette fois après 300 instructions. Une fois encore, il a été résolu de la même manière. Ainsi, PreparedStatement ou ResultSet, ou les deux, sont comptés comme des curseurs ouverts jusqu'à leur fermeture.
requête pour trouver SQL qui a ouvert.
SELECT s.machine, oc.user_name, oc.sql_text, count(1)
FROM v$open_cursor oc, v$session s
WHERE oc.sid = s.sid
and S.USERNAME='XXXX'
GROUP BY user_name, sql_text, machine
HAVING COUNT(1) > 2
ORDER BY count(1) DESC
J'avais moi aussi été confronté à ce problème. L'exception ci-dessous venait
Java.sql.SQLException: - ORA-01000: maximum open cursors exceeded
J'utilisais Spring Framework avec Spring JDBC pour la couche Dao.
Mon application avait l'habitude de laisser fuir les curseurs et après quelques minutes, elle me donnait cette exception.
Après beaucoup de débogage et d’analyses approfondies, j’ai découvert qu’il y avait un problème avec l’indexation, la clé primaire et les contraintes uniques dans l’un des Table utilisés dans Query que j’exécutais. .
Mon application essayait de mettre à jour les Columns qui étaient par erreur Indexed . Ainsi, chaque fois que mon application frappait la requête de mise à jour sur les colonnes indexées, la base de données essayait de réindexer en fonction du valeurs mises à jour. Il y avait une fuite de curseurs.
J'ai pu résoudre le problème en effectuant une indexation correcte sur les colonnes utilisées pour la recherche dans la requête et en appliquant les contraintes appropriées, le cas échéant.
Avez-vous défini autocommit = true? Sinon essayez ceci:
{ //method try starts
String sql = "INSERT into TblName (col1, col2) VALUES(?, ?)";
Connection conn = obj.getConnection()
pStmt = conn.prepareStatement(sql);
for (String language : additionalLangs) {
pStmt.setLong(1, subscriberID);
pStmt.setInt(2, Integer.parseInt(language));
pStmt.execute();
conn.commit();
}
} //method/try ends {
//finally starts
pStmt.close()
} //finally ends
Dans notre cas, nous utilisions Hibernate et de nombreuses variables faisaient référence à la même entité mappée Hibernate. Nous étions en train de créer et de sauvegarder ces références dans une boucle. Chaque référence ouvrait un curseur et le laissait ouvert.
Nous avons découvert cela en utilisant une requête pour vérifier le nombre de curseurs ouverts tout en exécutant notre code, en passant en revue avec un débogueur et en commentant sélectivement les choses.
Pourquoi chaque nouvelle référence ouvrait-elle un autre curseur? L'entité en question avait des collections d'autres entités mappées sur celle-ci et je pense que cela y était pour quelque chose. paramètres de cache). Hibernate lui-même a eu des bugs qui empêchaient de fermer ouvrir les curseurs, bien qu'il semble que ceux-ci aient été corrigés dans les versions ultérieures.
Comme nous n'avions de toute façon pas besoin d'avoir autant de références dupliquées à la même entité, la solution consistait à cesser de créer et de conserver toutes ces références redondantes. Une fois que nous avons fait le problème lorsque nous sommes partis.
J'ai eu ce problème avec ma source de données dans WildFly et Tomcat, la connexion à un Oracle 10g.
J'ai constaté que, dans certaines conditions, l'instruction n'était pas fermée même lorsque l'instruction.close () était invoquée . Le problème concernait le pilote Oracle que nous utilisions: ojdbc7.jar. Ce pilote est destiné aux Oracle 12c et 11g, et il semble que certains problèmes se posent lorsqu’il est utilisé avec Oracle 10g. C’est pourquoi je rétrograde en ojdbc5.jar et tout fonctionne correctement.
J'ai rencontré le même problème parce que je demandais à la base de données depuis plus de 1000 itérations… .. J'ai utilisé try et finalement dans mon code Mais obtenait toujours une erreur.
Pour résoudre ce problème, je viens de me connecter à Oracle db et d’exécuter la requête ci-dessous:
ALTER SYSTEM SET open_cursors = 8000 SCOPE = BOTH;
Et cela a résolu mon problème immédiatement.
Ce problème se produit principalement lorsque vous utilisez le regroupement de connexions, car lorsque vous fermez une connexion, cette connexion est rétablie dans le pool de connexions et tous les curseurs associés à cette connexion ne sont jamais fermés, car la connexion à la base de données est toujours ouverte. réduisez le temps de connexion inactif des connexions dans le pool. Ainsi, chaque fois que la connexion est inactive pendant 10 secondes, la connexion à la base de données sera fermée et une nouvelle connexion créée pour la mise en pool.
L'utilisation du traitement par lots entraînera moins de temps système. Voir le lien suivant pour des exemples: http://www.tutorialspoint.com/jdbc/jdbc-batch-processing.htm