在工作中我们必须处理几个MS Access mdb文件,因此我们使用Sun JVM附带的默认JdbcOdbcBridge驱动程序,并且在大多数情况下,它运行良好。
问题在于,当我们必须处理一些较大的文件时,我们会遇到几次异常,并显示“无法打开更多表”的消息。我们怎么能避免这种情况?
我们已经关闭了PreparedStatements和RecordSets的所有实例,甚至将它们的变量设置为null,但即便如此,这种异常仍会继续发生。我们应该做什么?我们怎样才能避免这些讨厌的例外?这里有人知道怎么做?
Windows上的ODBC驱动程序是否有任何其他配置我们可以更改以避免此问题?
答案 0 :(得分:10)
“无法打开任何更多的表”是比“无法打开更多数据库”更好的错误消息,这在我的经验中更常见。事实上,后一条消息几乎总是掩盖前者。
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)交互时遇到的一些问题您收到的错误消息,希望我们的经验和实践可能为您的特定编程环境提供解决方案。
答案 1 :(得分:3)
最近我尝试了UCanAccess - 一个用于MS Access的纯Java JDBC驱动程序。退房:http://sourceforge.net/projects/ucanaccess/ - 适用于Linux ;-)要加载所需的库,需要一些时间。我还没有测试它只是为了只读目的。
无论如何,我遇到了如上所述的sun.jdbc.odbc.JdbcOdbcDriver问题。在创建语句对象(以及对那些语句调用executeUpdate)以及System.gc()语句之后添加close()语句后,错误消息停止; - )
答案 2 :(得分:1)
你有可能只是用完了免费的网络连接。我们在工作繁忙的系统上遇到了这个问题。
需要注意的是,网络连接虽然已关闭,但在垃圾收集时间之前可能无法释放套接字。您可以使用NETSTAT /A /N /P TCP
进行检查。如果您在TIME_WAIT
状态中有很多连接,则可以尝试在连接关闭或定期间隔时强制执行垃圾收集。
答案 3 :(得分:0)
您还应该关闭Connection对象。
寻找jdbc odbc驱动程序的替代方案也是个好主意。我自己没有任何替代经验,但这将是一个很好的起点:
Is there an alternative to using sun.jdbc.odbc.JdbcOdbcDriver?
答案 4 :(得分:0)
我遇到了同样的问题,但上述情况都没有奏效。我最终找到了这个问题。 我正在使用它来读取表单的值以放回查找列表记录源。
LocationCode = [Forms]![Support].[LocationCode].Column(2)
ContactCode = Forms("Support")("TakenFrom")
将其更改为以下内容并且有效。
LocationCode = Forms("Support")("LocationCode")
ContactCode = Forms("Support")("TakenFrom")
我知道我应该把它写得更好,但我希望这能帮助处于同样情况的其他人。
由于 格雷格