Question

au travail, nous devons traiter plusieurs fichiers MS Access mdb. Nous utilisons donc le pilote JdbcOdbcBridge par défaut fourni avec la machine virtuelle Java Sun et, dans la plupart des cas, cela fonctionne très bien.

Le problème est que lorsque nous devons traiter des fichiers plus volumineux, nous sommes confrontés à plusieurs exceptions avec le message "Impossible d'ouvrir d'autres tables". Comment pouvons-nous éviter cela?

Nous fermons déjà toutes nos instances de PreparedStatements et RecordSets, et mettons même leurs variables à null, mais même si cette exception continue à se produire. Que devrions nous faire? Comment pouvons-nous éviter ces mauvaises exceptions? Est-ce que quelqu'un ici sait comment faire?

Existe-t-il une configuration supplémentaire des pilotes ODBC sous Windows que nous pouvons modifier pour éviter ce problème?

Était-ce utile?

La solution

" Impossible d'ouvrir d'autres tables " est un meilleur message d'erreur que le message «Impossible d'ouvrir plus de bases de données», qui est plus communément rencontré dans mon expérience. En fait, ce dernier message masque presque toujours le premier.

Le moteur de base de données Jet 4 a une limite de 2048 tables manipulées . Ce n'est pas tout à fait clair pour moi si cela est simultané ou cumulatif dans la vie d'une connexion. J'ai toujours pensé que c'était cumulatif, car en pratique, ouvrir moins de jeux d'enregistrements semble permettre d'éviter le problème.

Le problème est que " les poignées de table " ne se réfère pas seulement aux poignées de table, mais à quelque chose de beaucoup plus.

Considérez un objet QueryDef enregistré avec ce code SQL:

  SELECT tblInventory.* From tblInventory;

L'exécution de cette requête QueryDef utilise deux handles de table.

Quoi ?, pourriez-vous demander? Il utilise seulement une table! Mais Jet utilise un handle de table pour la table et un handle de table pour le QueryDef enregistré.

Ainsi, si vous avez un QueryDef comme celui-ci:

  SELECT qryInventory.InventoryID, qryAuthor.AuthorName
  FROM qryInventory JOIN qryAuthor ON qryInventory.AuthorID = qryAuthor.AuthorID

... si chacune de vos requêtes source contient deux tables, vous utilisez ces descripteurs de table, un pour chaque:

  Table 1 in qryInventory
  Table 2 in qryInventory
  qryInventory
  Table 1 in qryAuthor
  Table 2 in qryAuthor
  qryAuthor
  the top-level QueryDef

Donc, vous pourriez penser que vous n'avez que quatre tables impliquées (car il n'y a que quatre tables de base), mais vous utiliserez en fait 7 handles de table afin d'utiliser ces 4 tables de base.

Si vous vous trouvez dans un jeu d'enregistrements, vous utilisez ensuite le QueryDef enregistré qui utilise 7 handles de table, vous avez déjà utilisé un autre handle de table, pour un total de 8.

De retour dans Jet 3.5 jours, la limite de la table d'origine était de 1024, et je l'ai heurté à une date limite lorsque j'ai répliqué le fichier de données après avoir conçu une application fonctionnelle. Le problème était que certaines des tables de réplication sont ouvertes à tout moment (peut-être pour chaque jeu d'enregistrements?), Ce qui a épuisé le nombre de handles de table nécessaires pour placer l'application au-dessus.

Dans la conception originale de cette application, j'ouvrais un grand nombre de formulaires très lourds avec beaucoup de sous-formulaires, de listes déroulantes et de listes déroulantes. À cette époque, j’utilisais beaucoup de QueryDef enregistrés pour prémonter des jeux d’enregistrements standard que j’utilisais dans nombreux endroits (comme vous le feriez avec des vues sur n’importe quelle base de données de serveur). Ce qui a résolu le problème était:

  1. charger les sous-formulaires uniquement lorsqu'ils sont affichés.

  2. charger les sources de lignes des listes déroulantes et des listes de sélection uniquement lorsqu'elles étaient à l'écran.

  3. se débarrasser de tous les objets QueryDef enregistrés et utiliser des instructions SQL reliant les tables brutes, dans la mesure du possible.

Cela m'a permis de déployer cette application dans le bureau de Londres une semaine plus tard que prévu. Lorsque Jet SP2 est sorti, il a doublé le nombre de poignées de table, ce qui est toujours le même dans Jet 4 (et, je suppose, l’ACE).

En ce qui concerne l'utilisation de Jet à partir de Java via ODBC, le point clé serait, je pense:

  1. utilisez une seule connexion dans l'ensemble de votre application, plutôt que de les ouvrir et les fermer selon vos besoins (vous risquez donc de ne pas les fermer).

  2. n'ouvrez les jeux d'enregistrements que lorsque vous en avez besoin, puis nettoyez et libérez leurs ressources lorsque vous avez terminé.

À présent, il se peut que des fuites de mémoire se produisent quelque part dans la chaîne JDBC = > ODBC = > où vous pensez que vous libérez des ressources qui ne le sont pas du tout. Je n'ai aucun conseil spécifique à JDBC (car je ne l'utilise pas - je suis un programmeur Access, après tout), mais dans VBA, nous devons faire attention à la fermeture explicite de nos objets et à la libération de leurs structures de mémoire car VBA utilise le comptage des références et, parfois, il ne sait pas qu’une référence à un objet a été publiée. Par conséquent, il ne libère pas la mémoire de cet objet lorsqu’il sort de sa portée.

Donc, dans le code VBA, chaque fois que vous faites cela:

  Dim db As DAO.Database
  Dim rs As DAO.Recordset

  Set db = DBEngine(0).OpenDatabase("[database path/name]")
  Set rs = db.OpenRecordset("[SQL String]")

... après avoir fait ce que vous devez faire, vous devez en finir avec ceci:

  rs.Close         ' closes the recordset
  Set rs = Nothing ' clears the pointer to the memory formerly used by it
  db.Close
  Set db = Nothing

... et ce, même si vos variables déclarées sont hors de portée immédiatement après ce code (qui devrait libérer toute la mémoire utilisée par elles, mais ne le fait pas à 100% de manière fiable).

Maintenant, je ne dis pas que c'est ce que vous faites en Java, mais je suggère simplement que si vous rencontrez des problèmes et que vous pensez libérer toutes vos ressources, vous devez peut-être déterminer si vous ' dépend de la récupération de place pour le faire et doit plutôt le faire explicitement.

Pardonnez-moi si j'avais dit quelque chose de stupide en ce qui concerne Java et JDBC - je ne fais que signaler certains des problèmes rencontrés par les développeurs d'Access pour interagir avec Jet (via DAO, pas ODBC) et signalant les mêmes problèmes. message d'erreur que vous obtenez, dans l'espoir que notre expérience et notre pratique suggèrent une solution pour votre environnement de programmation particulier.

Autres conseils

J'ai récemment essayé UCanAccess, un pilote JDBC Java pur pour MS Access. Découvrez: http://sourceforge.net/projects/ucanaccess/ - fonctionne également sous Linux; - ) Pour charger les bibliothèques requises, il faut un peu de temps. Je ne l'ai pas encore testé à des fins de lecture seule.

Quoi qu’il en soit, j’ai rencontré les problèmes décrits ci-dessus avec le pilote sun.jdbc.odbc.JdbcOdbcDriver. Après avoir ajouté des instructions close () après la création d’objets d’instruction (et des appels à executeUpdate sur ceux-ci) ainsi que des instructions System.gc (), les messages d’erreur se sont arrêtés; -)

Il y a une chance extérieure que vous manquiez simplement de connexions réseau gratuites. Nous avons eu ce problème sur un système occupé au travail.

Il convient de noter que les connexions réseau, bien que fermées, ne peuvent pas libérer le socket avant le ramassage des ordures. Vous pouvez vérifier cela avec NETSTAT / A / N / P TCP . Si vous avez beaucoup de connexions dans l'état TIME_WAIT , vous pouvez essayer de forcer un garbage collection lors de la fermeture de la connexion ou à des intervalles réguliers.

Vous devriez également fermer votre objet Connection.

Il serait également judicieux d’envisager une solution de rechange au pilote jdbc odbc. Je n'ai aucune expérience avec une alternative moi-même, mais ce serait un bon endroit pour commencer:

Existe-t-il une alternative à sun.jdbc .odbc.JdbcOdbcDriver?

J'ai eu le même problème mais rien de ce qui précède ne fonctionnait. J'ai finalement localisé le problème. J'utilisais ceci pour lire la valeur d'un formulaire à remettre dans une source d'enregistrement de liste de recherche.

LocationCode = [Forms]![Support].[LocationCode].Column(2)
ContactCode = Forms("Support")("TakenFrom")

Modifié ci-dessous et cela fonctionne.

LocationCode = Forms("Support")("LocationCode")
ContactCode = Forms("Support")("TakenFrom")

Je sais que j'aurais dû l'écrire mieux, mais j'espère que cela aidera quelqu'un d'autre dans la même situation.

Merci Greg

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top