Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: 当JDBC预编译参数值全为 null 时必报错:com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure #1948

Open
CodePlayer opened this issue May 6, 2024 · 7 comments
Labels
serverity: major Major functionality but can wait status: confirmed bug or feature request is confirmed type: bug Something isn't working

Comments

@CodePlayer
Copy link

CodePlayer commented May 6, 2024

Describe the bug
使用 MySQL JDBC 驱动连接 OceanBase,如果预编译参数的值全部为 null,则必定报错。

Environment

  • OS Version: CentOS 7.9 / 8.2 x86_64
  • OB Version: 4.2.2.0 社区版

Fast Reproduce Steps(Required)

Steps to reproduce the behavior:

@Test
public void findByIds() {
	// 等价于 SELECT * FROM t_user WHERE id IN ( ?, ?, ? )
	List<User> list = userDao.selectBatchIds(Arrays.asList( null, null, null )); // 传入3个 null 参数
	System.out.println(list.size());
}

目前已知的情况如下:

  • 使用的是 MySQL 官方 JDBC 驱动 mysql-connector-j 8.3.0,没有使用其他版本测试过,但结果应该是一样的。
  • 项目使用的 ORM 框架是 MyBatis Plus 3.5.6,也使用等价的原生 JDBC 代码测试过,结果一样报错。因此可以确定和框架无关。
  • 将上述3个 null 参数中的任何一个参数改为非 null 值,查询就会正常执行,并返回符合预期的结果。也就是说,只有 全部 的预编译参数值都为null,才会报错。
  • 使用原生JDBC代码执行 SELECT * FROM t_user WHERE id = ?,哪怕只有一个预编译参数,只要参数值为null,就会报同样的错。
  • 使用 MySQL 数据库(不使用 OceanBase),就不会报错。
  • 此问题必现

Expected behavior
期望正确返回,而不是报错。

Actual Behavior

The last packet successfully received from the server was 13 milliseconds ago. The last packet sent successfully to the server was 17 milliseconds ago.
	at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:174)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64)
	at com.mysql.cj.jdbc.ServerPreparedStatement.serverExecute(ServerPreparedStatement.java:555)
	at com.mysql.cj.jdbc.ServerPreparedStatement.executeInternal(ServerPreparedStatement.java:339)
	at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:354)
	at com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44)
	at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.execute(HikariProxyPreparedStatement.java)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke(PreparedStatementLogger.java:58)
	at jdk.proxy3/jdk.proxy3.$Proxy361.execute(Unknown Source)
	at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:65)
	at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:80)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61)
	at jdk.proxy2/jdk.proxy2.$Proxy360.query(Unknown Source)
	at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:65)
	at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:336)
	at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:158)
	at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:110)
	at com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor.intercept(MybatisPlusInterceptor.java:81)
	at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:59)
	at jdk.proxy2/jdk.proxy2.$Proxy359.query(Unknown Source)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:154)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:147)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:142)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:425)
	... 91 more
Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure

The last packet successfully received from the server was 13 milliseconds ago. The last packet sent successfully to the server was 17 milliseconds ago.
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:104)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:149)
	at com.mysql.cj.exceptions.ExceptionFactory.createCommunicationsException(ExceptionFactory.java:165)
	at com.mysql.cj.protocol.a.NativeProtocol.readMessage(NativeProtocol.java:582)
	at com.mysql.cj.protocol.a.NativeProtocol.checkErrorMessage(NativeProtocol.java:762)
	at com.mysql.cj.protocol.a.NativeProtocol.sendCommand(NativeProtocol.java:701)
	at com.mysql.cj.ServerPreparedQuery.sendExecutePacket(ServerPreparedQuery.java:275)
	at com.mysql.cj.ServerPreparedQuery.serverExecute(ServerPreparedQuery.java:198)
	at com.mysql.cj.jdbc.ServerPreparedStatement.serverExecute(ServerPreparedStatement.java:553)
	... 124 more
Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
	at com.mysql.cj.protocol.FullReadInputStream.readFully(FullReadInputStream.java:67)
	at com.mysql.cj.protocol.a.SimplePacketReader.readHeaderLocal(SimplePacketReader.java:81)
	at com.mysql.cj.protocol.a.SimplePacketReader.readHeader(SimplePacketReader.java:63)
	at com.mysql.cj.protocol.a.SimplePacketReader.readHeader(SimplePacketReader.java:45)
	at com.mysql.cj.protocol.a.TimeTrackingPacketReader.readHeader(TimeTrackingPacketReader.java:52)
	at com.mysql.cj.protocol.a.TimeTrackingPacketReader.readHeader(TimeTrackingPacketReader.java:41)
	at com.mysql.cj.protocol.a.MultiPacketReader.readHeader(MultiPacketReader.java:54)
	at com.mysql.cj.protocol.a.MultiPacketReader.readHeader(MultiPacketReader.java:44)
	at com.mysql.cj.protocol.a.NativeProtocol.readMessage(NativeProtocol.java:576)
	... 129 more
@CodePlayer CodePlayer added the type: bug Something isn't working label May 6, 2024
@hnwyllmm
Copy link
Contributor

hnwyllmm commented May 6, 2024

收到,感谢反馈,我们重现一下看。

@hnwyllmm
Copy link
Contributor

hnwyllmm commented May 7, 2024

抱歉使用setNull的方式没有重现。
我看到你的描述:“使用原生JDBC代码执行 SELECT * FROM t_user WHERE id = ?,哪怕只有一个预编译参数,只要参数值为null,就会报同样的错。”
看起来已经可以使用JDBC代码来重现,发一下JDBC重现的代码?多谢

@hnwyllmm
Copy link
Contributor

hnwyllmm commented May 7, 2024

我们使用mysql-connector-java-8.0.30 重现了这个问题。
可以改用 mysql-connector-java-8.0.20 没有这个问题,可以替换使用这个驱动。
或者使用ob提供的jdbc,我们使用 2410 版本没有问题

@CodePlayer
Copy link
Author

CodePlayer commented May 7, 2024

抱歉使用setNull的方式没有重现。 我看到你的描述:“使用原生JDBC代码执行 SELECT * FROM t_user WHERE id = ?,哪怕只有一个预编译参数,只要参数值为null,就会报同样的错。” 看起来已经可以使用JDBC代码来重现,发一下JDBC重现的代码?多谢

如下所示,原生 JDBC 代码很简单。不过我们使用的不是 setNull,而是 setObject( index, null )

Connection conn = dataSource.getConnection();
try {
	PreparedStatement ps = conn.prepareStatement("SELECT id FROM t_user WHERE id = ?");
	ps.setObject(1, null); // ps.setNull(1, Types.BIGINT);
	ps.execute();
} catch (SQLException e) {
	throw new RuntimeException(e);
}

更换驱动版本,多次测试,初步验证出如下结论:

  • mysql-connector-java-8.0.28 及以下版本不会报错,以上版本就会报错(仅测试了 8.0.20 ~ 8.0.33 之间的版本)。
  • 不管是使用 setNull 还是 setObject,结果都是一样的。
  • 不过,如果是连接 MySQL 数据库,不管哪个版本,都不会报错。

@hnwyllmm hnwyllmm added the status: confirmed bug or feature request is confirmed label May 7, 2024
@CodePlayer
Copy link
Author

目前我们有多个环境,并未全部更换为 OceanBase,因此需要在多个环境的 OceanBase 和 MySQL 之间来回测试,以验证兼容性。
所以,目前还是打算使用 MySQL 提供的 JDBC 驱动,暂时还不宜使用 OceanBase 官方提供的 驱动包。

请问,如果使用 OceanBase 官方驱动包,除了更好的兼容性外,在性能方面会有更好的表现么 ?

BTW, 不知道 OceanBase 4.3 大概什么时候能够发布正式版 ?貌似该版本加了不少 OLAP 的新特性,比较眼馋~

@hnwyllmm
Copy link
Contributor

hnwyllmm commented May 7, 2024

使用MySQL原生驱动没有问题,现在看来不着急升级驱动可以正常运行。
4.3,预估在9月底发布GA(一般可用(General availability, 缩写GA))版本。感谢关注。

@hnwyllmm
Copy link
Contributor

hnwyllmm commented May 9, 2024

原因已找到,正在修复。

@hnwyllmm hnwyllmm added the serverity: major Major functionality but can wait label May 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
serverity: major Major functionality but can wait status: confirmed bug or feature request is confirmed type: bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants