“无法打开更多的表”是比“无法打开更多的数据库”更好的错误消息,在我的经验中更常见。事实上,后者的信息几乎总是掩盖前者。
Jet 4 数据库引擎有 2048 个表句柄的限制。我不完全清楚这在连接的生命周期内是同时发生的还是累积的。我一直认为它是累积的,因为在实践中一次打开更少的记录集似乎可以避免这个问题。
问题是“表句柄”不仅仅指表句柄,而是更多的东西。
考虑使用此 SQL 保存的 QueryDef:
SELECT tblInventory.* From tblInventory;
运行 QueryDef 使用两个表句柄。
什么?,你可能会问?它只使用一张桌子!但是 Jet 为表使用表句柄,为保存的 QueryDef 使用表句柄。
因此,如果您有这样的 QueryDef:
SELECT qryInventory.InventoryID, qryAuthor.AuthorName
FROM qryInventory JOIN qryAuthor ON qryInventory.AuthorID = qryAuthor.AuthorID
...如果您的每个源查询中都有两个表,则您正在使用这些表句柄,每个句柄一个:
Table 1 in qryInventory
Table 2 in qryInventory
qryInventory
Table 1 in qryAuthor
Table 2 in qryAuthor
qryAuthor
the top-level QueryDef
因此,您可能认为只涉及四个表(因为只有四个基表),但实际上您将使用 7 个表句柄来使用这 4 个基表。
如果在记录集中,然后使用使用 7 个表句柄的已保存 QueryDef,则您已经用完了另一个表句柄,总共 8 个。
回到 Jet 3.5 天,原始表处理限制为 1024,我在设计工作应用程序后复制数据文件时遇到了它。问题在于,某些复制表始终处于打开状态(可能是针对每个记录集?),而这仅使用了足够多的表句柄来将应用程序置于顶部。
在该应用程序的原始设计中,我打开了一堆重量级的表单,其中包含许多子表单、组合框和列表框,当时我使用了很多保存的 QueryDef 来预组装我会在很多地方使用的标准记录集(就像您对任何服务器数据库的视图一样)。解决问题的是:
仅在显示子窗体时才加载它们。
仅当它们在屏幕上时才加载组合框和列表框的行源。
尽可能删除所有保存的 QueryDef 并使用连接原始表的 SQL 语句。
这使我能够比计划只晚一周在伦敦办公室部署该应用程序。当 Jet SP2 出现时,它的桌面句柄数量增加了一倍,这就是我们在 Jet 4 中仍然拥有的(我猜是 ACE)。
在通过 ODBC 从 Java 使用 Jet 方面,我认为关键是:
在整个应用程序中使用单个连接,而不是根据需要打开和关闭它们(这会使您面临无法关闭它们的危险)。
仅在需要时打开记录集,并在完成后清理并释放它们的资源。
现在,可能是 JDBC=>ODBC=>Jet 链中的某个地方存在内存泄漏,您认为您正在释放资源,而它们根本没有被释放。我没有针对 JDBC 的任何建议(因为我不使用它——毕竟我是 Access 程序员),但在 VBA 中,我们必须小心明确地关闭我们的对象并释放它们的内存结构,因为VBA 使用引用计数,有时它不知道对对象的引用已被释放,因此当对象超出范围时它不会释放该对象的内存。
所以,在 VBA 代码中,任何时候你这样做:
Dim db As DAO.Database
Dim rs As DAO.Recordset
Set db = DBEngine(0).OpenDatabase("[database path/name]")
Set rs = db.OpenRecordset("[SQL String]")
...完成您需要做的事情后,您必须完成以下操作:
rs.Close ' closes the recordset
Set rs = Nothing ' clears the pointer to the memory formerly used by it
db.Close
Set db = Nothing
...即使您声明的变量在该代码之后立即超出范围(这应该释放它们使用的所有内存,但不会 100% 可靠地这样做)。
现在,我并不是说这就是你在 Java 中所做的,但我只是建议如果你遇到问题并且你认为你正在释放所有资源,也许你需要确定你是否依赖于垃圾收集这样做,而是需要明确地这样做。
如果我在 Java 和 JDBC 方面说了一些愚蠢的话,请原谅我——我只是在报告 Access 开发人员在与 Jet 交互(通过 DAO,而不是 ODBC)时遇到的一些问题,这些问题报告了相同的错误消息您正在获得,希望我们的经验和实践可以为您的特定编程环境提供解决方案。