2

我有一个使用 JDBC-OBDC 的 Java 小应用程序的奇怪情况。我正在使用 DatabaseMetaData 类检查数据库。当我执行程序时,一切正常。但是,当我想调试以查看包含 DatabaseMetaData 的 Resulset 中的值时,仅当我在其中放置断点时才会抛出 java.sql.SQLException。这是我的代码:

DatabaseMetaData patrol = con.getMetaData();
ResultSet answer = patrol.getTables(null, null, null, null);
        while(answer.next()) {
            if (answer.wasNull() == false) {
                tableNamesAsOne = tableNamesAsOne + answer.getString("TABLE_NAME") + " ";
            }
        }
        answer.close();

为什么我不能在这部分代码中放置断点?

这是 printStackTrace。

Exception in thread "main" java.sql.SQLException: No data found
    at sun.jdbc.odbc.JdbcOdbc.standardError(Unknown Source)
    at sun.jdbc.odbc.JdbcOdbc.SQLGetDataString(Unknown Source)
    at sun.jdbc.odbc.JdbcOdbcResultSet.getDataString(Unknown Source)
    at sun.jdbc.odbc.JdbcOdbcResultSet.getString(Unknown Source)
    at sun.jdbc.odbc.JdbcOdbcResultSet.getString(Unknown Source)
    at Snooper.inspect(Snooper.java:56)
    at Snooper.<init>(Snooper.java:26)
    at Snooper.createAndShowGUI(Snooper.java:112)
    at Snooper.main(Snooper.java:125)

我的代码中的 Line Snooper.java:56 指

tableNamesAsOne = tableNamesAsOne + answer.getString("TABLE_NAME") + " ";

谢谢。

4

2 回答 2

3

我已经安装了 SQL Server 来重现您的问题并进行验证。

简短说明

您必须仅读取一次值,并且在它们出现在 SELECT中的ORDER中。JdbcOdbc 很烂。在调试时,您会多次阅读它们。

长解释

您正在做的是检查调试器中的有状态对象,这会导致动态结果。

在这种情况下,它是多次sun.jdbc.odbc.JdbcOdbcResultSet执行表达式。resultSet.getString(...)第一次,它将起作用(如果您的断点在询问 resultSet 之前暂停线程)。然后,您(或您的调试器)第二次再次检查表达式的值时,再次调用getString()该方法并且该方法更改对象的内部状态。ResultSet

尽管该方法的名称表明它是一个简单的 getter,但事实并非如此。它的作用不止于此。它实际上可能从数据库中检索数据,更改其内部位置计数器等。您不能多次执行 getter 方法。

ODBC 驱动程序是一个非常糟糕且质量低下的东西。期待奇怪的行为和其他龙。您可以通过启用 JdbcOdbc Tracing 来获取一些调试信息。这是通过LogWriter在 DriverManager 上设置 a 在激活 JdbcOdbc-Bridge 之前完成的:

java.sql.DriverManager.setLogWriter(new PrintWriter(System.out));

然后,您将获得 JdbcOdbc-Driver 的详细调试输出,如下所示。它可以帮助您调试您遇到的问题。调试时,只需确保将从 ResultSet 对象中检索到的数据存储在本地对象中,这样您就可以在调试器中多次检查它们。

DriverManager.getConnection("jdbc:odbc:testdbodbc")
JdbcOdbcDriver class loaded
registerDriver: driver[className=sun.jdbc.odbc.JdbcOdbcDriver,sun.jdbc.odbc.JdbcOdbcDriver@7b479feb]
DriverManager.initialize: jdbc.drivers = null
JDBC DriverManager initialized
    trying driver[className=sun.jdbc.odbc.JdbcOdbcDriver,sun.jdbc.odbc.JdbcOdbcDriver@7b479feb]
*Driver.connect (jdbc:odbc:testdbodbc)
JDBC to ODBC Bridge: Checking security
No SecurityManager present, assuming trusted application/applet
JDBC to ODBC Bridge 2.0001
Current Date/Time: Wed Jan 26 00:31:27 CET 2011
Loading JdbcOdbc library
Allocating Environment handle (SQLAllocEnv)
hEnv=115724512
Allocating Connection handle (SQLAllocConnect)
hDbc=116219184
Connecting (SQLDriverConnect), hDbc=116219184, szConnStrIn=DSN=testdbodbc
RETCODE = 1
WARNING - Generating SQLWarning...
SQLWarning: reason([Microsoft][ODBC SQL Server Driver][SQL Server]Changed database context to 'master'.) SQLState(01000) vendor code(5701)
SQLWarning: reason([Microsoft][ODBC SQL Server Driver][SQL Server]Changed language setting to us_english.) SQLState(01000) vendor code(5703)
*Connection.getMetaData
*DatabaseMetaData.getDriverName
Get connection info string (SQLGetInfo), hDbc=116219184, fInfoType=6, len=300
SQLSRV32.DLL
*DatabaseMetaData.getDriverVersion
Get connection info string (SQLGetInfo), hDbc=116219184, fInfoType=7, len=300
06.01.7600
*DatabaseMetaData.getDriverName
Get connection info string (SQLGetInfo), hDbc=116219184, fInfoType=6, len=300
SQLSRV32.DLL
Driver name:    JDBC-ODBC Bridge (SQLSRV32.DLL)
*DatabaseMetaData.getDriverVersion

PS 这是复制的异常,包括 JDK 1.6.0_22 的 Sun 代码的行号。正如您在第一行中看到的,这是我检查该getString()方法时在控制台上打印的内容。

Get string data (SQLGetData), hStmt=108067024, column=3, maxLen=257
RETCODE = 100
ERROR - No data found
java.sql.SQLException: No data found
at sun.jdbc.odbc.JdbcOdbc.standardError(JdbcOdbc.java:7138)
at sun.jdbc.odbc.JdbcOdbc.SQLGetDataString(JdbcOdbc.java:3907)
at sun.jdbc.odbc.JdbcOdbcResultSet.getDataString(JdbcOdbcResultSet.java:5698)
at sun.jdbc.odbc.JdbcOdbcResultSet.getString(JdbcOdbcResultSet.java:354)
at sun.jdbc.odbc.JdbcOdbcResultSet.getString(JdbcOdbcResultSet.java:411)
at sandbox.DatabaseMetadataTest.testDBMetadata(DatabaseMetadataTest.java:27)
于 2011-01-25T23:37:43.933 回答
1

是的,调试器在与...获得的元数据不同的线程中运行,con.getMetaData();所以,你知道,这是一个具有不同元数据的不同事务。

好吧,这将是我的第一个猜测。我还没有查看 Obdc 驱动程序源代码来确认。


编辑:感谢 mhaller 出色的评论,第二次看/猜测:你过早地调用 wasNull(),它在对 ResultSet 进行一些 get 操作后有意义。从 javadoc 复制

 * Note that you must first call one of the getter methods
 * on a column to try to read its value and then call
 * the method <code>wasNull</code> to see if the value read was
 * SQL <code>NULL</code>.

呸,今晚我真烂!

于 2011-01-25T18:52:17.227 回答