MS Access - 无法打开更多表

时间:2009-11-27 10:25:22

标签: java ms-access jdbc jdbc-odbc

在工作中我们必须处理几个MS Access mdb文件,因此我们使用Sun JVM附带的默认JdbcOdbcBridge驱动程序,并且在大多数情况下,它运行良好。

问题在于,当我们必须处理一些较大的文件时,我们会遇到几次异常,并显示“无法打开更多表”的消息。我们怎么能避免这种情况?

我们已经关闭了PreparedStatements和RecordSets的所有实例,甚至将它们的变量设置为null,但即便如此,这种异常仍会继续发生。我们应该做什么?我们怎样才能避免这些讨厌的例外?这里有人知道怎么做?

Windows上的ODBC驱动程序是否有任何其他配置我们可以更改以避免此问题?

5 个答案:

答案 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来预先组装我在其中使用的标准记录集很多地方(就像你在任何服务器数据库上查看一样)。解决问题的原因是:

  1. 仅在显示子表单时加载它们。

  2. 仅在屏幕上加载组合框和列表框的行源时才会加载。

  3. 尽可能删除所有已保存的QueryDef并使用加入原始表的SQL语句。

  4. 这使我能够在伦敦办公室部署该应用仅比计划晚一周。当Jet SP2出现时,它将表句数量增加了一倍,这就是我们在Jet 4中所拥有的(我认为是ACE)。

    关于通过ODBC从Java使用Jet,我认为关键点是:

    1. 在整个应用中使用单个连接,而不是根据需要打开和关闭它们(这会使您无法关闭它们。)

    2. 仅在您需要时打开记录集,并在完成后清理并释放其资源。

    3. 现在,可能是在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")

我知道我应该把它写得更好,但我希望这能帮助处于同样情况的其他人。

由于 格雷格