Open qzeng2490 opened 4 years ago
我也遇到了这个问题;
主要原因是 iterator.hasNext() 在到达最后一条时,会主动关闭 Cursor,然后连锁反应最终导致关闭查询对应的PreparedStatement;
然而 mybatis 的 session.close() 的时候,最终会调到 com.alibaba.druid.pool.DruidConnectionHolder.reset 对跟踪的statement进行重置操作,如果Mapper方法重新定义了fetchSize属性,并且与defaultFetchSize不同,则会调用 setFetchSize 重置为默认值;而此时的Statement已经被hasNext触发关闭了,所以会报这个错。
检查了下源码,Mapper 方法中定义的 maxFieldSize / maxRows / queryTimeout / fetchDirection / fetchSize 如果与默认值不同,在使用 Druid 和 Cursor时都会触发这个问题。
虽然Issues提在这里,但我认为这是mybatis的问题。Cursor应该交给用户主动关闭,而不是隐藏在 hasNext 里
我现在也遇到了这个问题 现在 是 cursor 正常 执行完毕 hasNext 把游标关闭 了 但是 在我调用 finally 的 sqlSession#close()时候就会触发这个问题 。。。。。。
This probably occurs only when pool-prepared-statements
is enabled.
MyBatis' Cursor implementation uses [java.sql.Statement#closeOnCompletion
](https://docs.oracle.com/en/java/javase/16/docs/api/java.sql/java/sql/Statement.html#closeOnCompletion()) (since 3.5.0) which implicitly closes a Statement
when all ResultSet
s associated with it are closed.
It seems that Druid logs the exception, but does not actually throw it , so it should not be a problem if you ignore the log output.
If you don't want to see the stack trace, you may have to disable
[EDIT] It might be a problem if the closed statement is not removed from the pool. Then you may have to disable pool-prepared-statements
for now.pool-prepared-statements
when using MyBatis' Cursor.
A long term solution would be to improve Druid to support closeOnCompletion()
properly.
The following test should pass (with no stack trace in the log), basically.
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.Assert;
import com.alibaba.druid.pool.DruidDataSource;
import junit.framework.TestCase;
public class CloseOnCompletionTest extends TestCase {
public void test_closeOnCompletion_druid() throws Exception {
try (DruidDataSource dataSource = new DruidDataSource()) {
dataSource
.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver");
dataSource.setUrl(
"jdbc:derby:memory:closeoncompletiondruid;create=true");
dataSource.setUsername("APP");
dataSource.setPassword("APP");
dataSource.setPoolPreparedStatements(true);
dataSource.init();
try (Connection connection = dataSource.getConnection()) {
try (Statement statement = connection.createStatement()) {
statement.execute(
"create table test (id int, name varchar(10))");
statement.execute(
"insert into test (id, name) values (1, 'Name1')");
statement.execute(
"insert into test (id, name) values (2, 'Name2')");
}
PreparedStatement statement = connection
.prepareStatement("select name from test order by id");
statement.setFetchSize(5);
statement.closeOnCompletion();
ResultSet resultSet = statement.executeQuery();
Assert.assertTrue(resultSet.next());
Assert.assertEquals("Name1", resultSet.getString("name"));
Assert.assertTrue(resultSet.next());
Assert.assertEquals("Name2", resultSet.getString("name"));
Assert.assertFalse(resultSet.next());
resultSet.close();
Assert.assertTrue(statement.isClosed());
}
}
}
}
我在使用MyBatisCursorItemReader时也遇到了这个问题,请教各位大佬,都是怎么解决的?
原因是druid尝试关闭一个已经被关闭的 Statement,不过这其实只是一条debug日志,不会影响什么(当然,确实不太好看)
遇到了这个问题+1,只能通过spring.datasource.druid.poolPreparedStatements=false规避
spring.datasource.druid.poolPreparedStatements=false spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize=0 或者两个都删除
同遇到,求解决,改数据源配置不是长久之道,全局关闭PreparedStatement 不是很好。
手动开数据源连接又显得不是很优雅。
同遇到,求解决,改数据源配置不是长久之道,全局关闭PreparedStatement 不是很好。
可以让 加一个 Mybatis Interceptor,然后在 StatementHandler 中对 prepare 方法进行处理返回一个改写的 Statement,然后该 Statement 返回 ResultSet 时,返回一个 ResultSet 代理对象,当关闭这个 ResultSet 时,也关闭 Statement。
绕一圈...还是直接弃用 druid 吧,2000+ issues 呢,而这已经是 3 年前的问题了。
另外,1.2.10 里,通过调整级别 https://github.com/alibaba/druid/commit/500f8ac2932e58533eb42617e761e7e43b3df952 ,已经是 debug 不是 error 了。眼不见心净。
感谢你的回复,确实主要是看着报error有点不好,虽然不影响功能,我还是升下版本吧。
感谢你的回复,确实主要是看着报error有点不好,虽然不影响功能,我还是升下版本吧。
这个日志,只有底层使用 SLF4J 才有。而且,即使只是 debug 日志,也就是见不到,但其实已经是一个异常。
而最近发现的另外一个,简直无法理解:https://github.com/alibaba/druid/blob/1.2.12/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java#L1495-L1517
已经 valid,然后还要进一步判断。也就是说,连接明明能用,但是还是要断掉。这个我个人理解,纯 alibaba 私货行为:https://github.com/alibaba/druid/search?q=usePingMethod
我们暂时直接停用 ping 检查:-Ddruid.mysql.usePingMethod=false
druid-spring-boot-starter 版本是1.1.21 spring-boot-starter-parent 版本2.2.1.RELEASE mybatis-spring-boot-starter 版本2.1.1 mysql-connector-java 版本8.0.18
主要的代码如下
报错如下