Java ResultSetTimepl.checkColumnBounds或ResultSetTimepl.getStringInternal中偶尔出现NullPointerException
首先,请不要将此关闭为与Java ResultSetTimepl.checkColumnBounds或ResultSetTimepl.getStringInternal中偶尔出现NullPointerException,java,mysql,jdbc,Java,Mysql,Jdbc,首先,请不要将此关闭为与NullPointerException及其修复方法重复的内容。我知道什么是NullPointerException,我知道如何在我自己的代码中解决它,但当mysql-connector-java-5.1.36-bin.jar抛出它时就不知道了,我无法控制它 我们在mySQL数据库上运行公共数据库查询时遇到了异常,该查询大部分时间都有效。我们在部署新版本后开始看到此异常,但发生此异常的查询在很长时间内没有更改 下面是查询的样子(经过一些必要的简化)。我用前后执行的一些逻辑
NullPointerException
及其修复方法重复的内容。我知道什么是NullPointerException
,我知道如何在我自己的代码中解决它,但当mysql-connector-java-5.1.36-bin.jar抛出它时就不知道了,我无法控制它
我们在mySQL数据库上运行公共数据库查询时遇到了异常,该查询大部分时间都有效。我们在部署新版本后开始看到此异常,但发生此异常的查询在很长时间内没有更改
下面是查询的样子(经过一些必要的简化)。我用前后执行的一些逻辑来包围它。实际的代码并不都在一个方法中,但我将其放在一个块中以使其更易于理解
Connection conn = ... // the connection is open
...
for (String someID : someIDs) {
SomeClass sc = null;
PreparedStatement
stmt = conn.prepareStatement ("SELECT A, B, C, D, E, F, G, H FROM T WHERE A = ?");
stmt.setString (1, "someID");
ResultSet res = stmt.executeQuery ();
if (res.next ()) {
sc = new SomeClass ();
sc.setA (res.getString (1));
sc.setB (res.getString (2));
sc.setC (res.getString (3));
sc.setD (res.getString (4));
sc.setE (res.getString (5));
sc.setF (res.getInt (6));
sc.setG (res.getString (7));
sc.setH (res.getByte (8)); // the exception is thrown here
}
stmt.close ();
conn.commit ();
if (sc != null) {
// do some processing that involves loading other records from the
// DB using the same connection
}
}
conn.close();
res.getByte(8)
导致具有以下调用堆栈的NullPointerException
:
com.mysql.jdbc.ResultSetImpl.checkColumnBounds(ResultSetImpl.java:763)
com.mysql.jdbc.ResultSetImpl.getStringInternal(ResultSetImpl.java:5251)
com.mysql.jdbc.ResultSetImpl.getString(ResultSetImpl.java:5173)
com.mysql.jdbc.ResultSetImpl.getByte(ResultSetImpl.java:1650)
org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet.getByte(DelegatingResultSet.java:206)
org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet.getByte(DelegatingResultSet.java:206)
我搜索了相关mysql连接器版本的源代码,发现了以下内容(摘自):
这意味着this.fields
不知何故变成了null
我能找到的最接近的东西是,它没有答案
我怀疑问题不在我发布的查询中。可能是由于我们在同一连接上运行的一些其他语句,连接
实例出了问题。我所能说的是,我们在执行每个语句之后立即关闭它,并从它的结果集
读取数据
编辑(2017年1月19日):
我无法在我的开发环境中重新创建错误。我想可能是长时间使用同一个连接时触发了mysql连接器错误。我将上述循环限制为一次最多加载6个元素。此外,我们将mysql连接器版本升级到5.1.40
我们仍然在ResultSetImpl
中看到NullPointerException
s,但这次是在不同的位置
堆栈跟踪是:
com.mysql.jdbc.ResultSetImpl.getStringInternal(ResultSetImpl.java:5294)
com.mysql.jdbc.ResultSetImpl.getString(ResultSetImpl.java:5151)
org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet.getString(DelegatingResultSet.java:198)
org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet.getString(DelegatingResultSet.java:198)
这意味着这次异常是由于我们的一个res.getString()
调用引发的(我不知道是哪一个)
我找到了mysql-connector-java-5.1.40-bin.jar的源代码,相关代码是:
5292 // Handles timezone conversion and zero-date behavior
5293
5294 if (checkDateTypes && !this.connection.getNoDatetimeStringSync()) {
5295 switch (metadata.getSQLType()) {
这意味着此连接为空this.connection
是类型为MySQLConnection
的实例变量,它在ResultSetImpl
的构造函数中初始化,只有在调用public void realClose(boolean calledeplicity)
时才设置为null(由public void close()调用)
,根据源代码中的文档,在调用ResultSet.close()
时调用它)。我们绝对不会在读取所有数据之前关闭结果集
有什么想法可以继续吗?您可以重写代码以使用一个try-catch块吗?
然后,您将在方法中拥有该连接,其他人无法关闭该连接
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class StackProblem1
{
void dbQuery(final String[] someIDs)
{
try (final Connection conn = getConnection();
final PreparedStatement stmt = conn.prepareStatement(
"SELECT A, B, C, D, E, F, G, H FROM T WHERE A = ?"))
{
for (final String someID : someIDs)
{
stmt.setString(1, "someID");
final ResultSet res = stmt.executeQuery();
if (res.next())
{
final SomeClass sc = new SomeClass();
sc.set(res.getString(1));
sc.set(res.getString(2));
sc.set(res.getString(3));
sc.set(res.getString(4));
sc.set(res.getString(5));
sc.set(res.getInt(6));
sc.set(res.getString(7));
sc.set(res.getByte(8)); // the exception is thrown here
// do some processing that involves loading other records from the
// DB using the same connection
}
}
}
catch (final SQLException sqlEx)
{
sqlEx.printStackTrace();
}
}
/**
* @return the connection is open
*/
private Connection getConnection()
{
return null;//FIXME return connection
}
}
我无法证明这是正确的解释,但即使不是,它也可能指向一些好的方向
归根结底,我们要弄清楚字段
受保护的成员变量是如何取null
值的
如果我正确地跟踪了堆栈跟踪,那么ResultSetImpl将通过调用static来实例化,static由which调用,which由which调用
在getResultSet()中,我们发现第一个字段初始化为null
Field[] fields = null;
然后就有了
if (metadataFromCache == null) {
fields = new Field[(int) columnCount];
...
} else {
for (int i = 0; i < columnCount; i++) {
skipPacket();
}
}
是true
和usingCursor
也是true
,也就是说versionmeetsmimum(5,0,5)&&(this.serverStatus&SERVER\u STATUS\u CURSOR\u存在)!=0
也是true
那么打电话的时候
ResultSetImpl rs = buildResultSetWithRows(callingStatement, catalog, fields, rows, resultSetType, resultSetConcurrency, isBinaryEncoded)
字段将为null
这可能导致ResultSetImpl
在其字段中获得null
值,并且与从5.0.5之前的版本升级到5.1.36之后发生的情况兼容
坏消息是,我看不出isbinarycoded
如何将值设为true,这是使用null
字段创建ResultSetImpl
实例的另一个必要条件
如果这个推测是正确的,那么这个bug是一个缺失的(metadataFromCache==null)?字段:metadataFromCache
位于MysqlIO.getResultSet()
并将ResultSet
类型从type\u FORWARD\u ONLY
更改为type\u SCROLL\u Unsensitive
可能会阻止NullPointerException
就我所知,这一切发生的唯一途径是
有人/某事正在调用ResultSet.close()
已调用ResultSet.getString(),但尚未返回
这可能是您的应用程序(它是多线程的,还是有
一个棘手的处理路径),或者你可能会失去连接
在这段时间的某个时候,这会导致驾驶员关闭
隐式连接,关闭所有打开的语句,关闭
全部开放结果se
if (metadataFromCache == null) {
fields = new Field[(int) columnCount];
...
} else {
for (int i = 0; i < columnCount; i++) {
skipPacket();
}
}
this.connection.versionMeetsMinimum(5, 0, 2) &&
this.connection.getUseCursorFetch() &&
isBinaryEncoded &&
callingStatement != null &&
callingStatement.getFetchSize() != 0 &&
callingStatement.getResultSetType() == ResultSet.TYPE_FORWARD_ONLY
ResultSetImpl rs = buildResultSetWithRows(callingStatement, catalog, fields, rows, resultSetType, resultSetConcurrency, isBinaryEncoded)
Connection conn = ... // the connection is open
...
SomeClass sc = null;
PreparedStatement
stmt = conn.prepareStatement ("SELECT A, B, C, D, E, F, G, H FROM T WHERE A in (?)");
//here you need to concate String to form like ('a','b')
stmt.setString (1, someID);
ResultSet res = stmt.executeQuery ();
if (res.next ()) {
sc = new SomeClass ();
sc.setA (res.getString (1));
sc.setB (res.getString (2));
sc.setC (res.getString (3));
sc.setD (res.getString (4));
sc.setE (res.getString (5));
sc.setF (res.getInt (6));
sc.setG (res.getString (7));
sc.setH (res.getByte (8));
}
stmt.close ();
conn.commit ();
if (sc != null) {
// do some processing that involves loading other records from the
// DB using the same connection
}
conn.close();
Connection conn = ... // the connection is open
...
for (String someID : someIDs) {
SomeClass sc = null;
PreparedStatement
stmt = conn.prepareStatement ("SELECT A, B, C, D, E, F, G, H FROM T WHERE A = ?");
stmt.setString (1, "someID");
ResultSet res = stmt.executeQuery ();
if (res.next ()) {
sc = new SomeClass ();
sc.setA (res.getString (1));
sc.setB (res.getString (2));
sc.setC (res.getString (3));
sc.setD (res.getString (4));
sc.setE (res.getString (5));
sc.setF (res.getInt (6));
sc.setG (res.getString (7));
sc.setH (res.getByte (8)); // the exception is thrown here
}
stmt.close ();
conn.commit ();
if (sc != null) {
// do some processing that involves loading other records from the
// DB using the same connection
}
}
conn.close();
if (sc != null) {
...
someMethod (conn);
...
}
public void someMethod (Connection conn)
{
...
SomeOtherClass instance = new SomeOtherClass (conn);
...
}
public class SomeOtherClass
{
Connection conn;
public SomeOtherClass (Connection conn)
{
this.conn = conn;
}
protected void finalize() throws Throwable
{
if (this.conn != null)
conn.close();
}
}