为什么我无法调试 DatabaseMetaData?

发布于 2024-10-14 19:24:32 字数 1322 浏览 4 评论 0原文

我有一个使用 JDBC-OBDC 的 Java 小应用程序,遇到了一个奇怪的情况。我正在使用 DatabaseMetaData 类检查数据库。当我执行该程序时,一切正常,没有任何问题。但是,当我想调试以查看包含 DatabaseMetaData 的结果集中的值时,仅当我在一段时间内放置断点时才会引发 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)

我的代码中的 Snooper.java:56 行指的是

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

谢谢。

I've a strange situation with a little application in Java using a JDBC-OBDC. I'm inspecting a Database using the DatabaseMetaData Class. When I execute the program, everything works without anyproblem. But when I want to debug to see the values inside the Resulset containing the DatabaseMetaData a java.sql.SQLException is thrown only if I put a breakpoint within the while. Here's my code:

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();

Why I cannot put my breakpoint in this section of code??

This is the 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 in my code refers to

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

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

尐籹人 2024-10-21 19:24:32

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

简短说明

您必须读取值ONLY ONCE,并且在 ORDER 中它们出现在 SELECT 中。 JdbcOdbc 很糟糕。在调试时,您会多次阅读它们。

长解释

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

在本例中,它是 sun.jdbc.odbc.JdbcOdbcResultSet 并多次执行表达式 resultSet.getString(...)。第一次,它会起作用(如果你的断点在询问结果集之前挂起线程)。然后,当您(或您的调试器)再次检查表达式的值时,将再次调用 getString() 方法,并且此方法会更改内部ResultSet 对象的状态。

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

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

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)

I have installed SQL Server to reproduce your problem and verify it.

Short explanation

You must read the values ONLY ONCE and in the ORDER they appear in the SELECT. JdbcOdbc sucks. While debugging, you're reading them multiple times.

Long explanation

What you are doing is inspecting a stateful object in the debugger, which leads to dynamic results.

In this case it's the sun.jdbc.odbc.JdbcOdbcResultSet and executing the expression resultSet.getString(...) multiple times. The first time, it will work (in case your breakpoint suspends the Thread before the resultSet is asked). Then, the second time you (or your debugger) inspects the value of the expression again, the getString() method is called again and this method changes the internal state of the ResultSet object.

Although the method's name suggests it's a simple getter, it's not. It does more than that. It may actually retrieve the data from the database, change its internal position counters etc. You cannot execute the getter methods multiple times.

The ODBC Driver is a very bad thing and of low quality. Expect strange behavior and other dragons. You can get some debug information by enabling JdbcOdbc Tracing. That is done by setting a LogWriter on the DriverManager, before the JdbcOdbc-Bridge is activated:

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

Then, you will get verbose debugging output of the JdbcOdbc-Driver like the following. It may help you to debug the problem you have. When debugging, just ensure to store the data retrieved from the ResultSet objects in local objects, so you can inspect them multiple times in the debugger.

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

P.S. And this was the reproduced exception, including line numbers of the Sun code for JDK 1.6.0_22. As you can see in the first line, this is what is printed out on the console when I inspected the getString() method.

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)
尤怨 2024-10-21 19:24:32

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

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


编辑:
感谢 mhaller 的精彩评论,我进行了第二次查看/猜测:您过早地调用了 wasNull(),它在对 ResultSet 进行一些获取操作后才有意义。从 javadoc 复制

 * 注意,必须先调用其中一个getter方法
 * 在列上尝试读取其值然后调用
 * 方法wasNull;查看读取的值是否为
 * SQL <代码>NULL。

唷,我今晚真糟糕!

Yeah, the debugger runs in a different thread than the metadata obtained by con.getMetaData();... so, you know, it's a different transaction with a different metadata.

Well, ok, that would be my 1st guess. I have not looked into Obdc driver source code to confirm.


Edit:
thanks for mhaller excellent remark a made a 2nd look/guess: you call wasNull() prematurely, it has meaning after some get operation of the ResultSet. Copy from 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>.

phew, me sucks tonight!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文