You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ExecutorService.submit执行MySQL查询时遭遇ResultSet已关闭异常

异常原因、代码问题及解决方案

异常原因

java.sql.SQLException: Operation not allowed after ResultSet closed 异常的核心原因是:你传递到异步线程的ResultSet在异步任务执行前就已经被关闭了。

代码中的问题

  1. 资源生命周期错误
    在executeUpdate方法中,Connection、PreparedStatement和ResultSet都是通过try-with-resources声明的,这些资源会在try代码块执行完毕后自动关闭。而你返回的lambda() -> resultSet要等到异步线程执行call()方法时才会获取ResultSet,此时原方法的try块已经结束,所有相关JDBC资源都已被释放,异步线程自然无法使用已关闭的ResultSet。

  2. 误解问题本质
    你尝试用synchronized和volatile修饰来解决问题,但这个异常和线程同步无关,完全是JDBC资源的生命周期管理错误,所以这些操作毫无作用。

  3. 违反JDBC资源依赖规则
    JDBC的ResultSet依赖于创建它的PreparedStatement,而PreparedStatement又依赖于Connection。一旦PreparedStatement或Connection被关闭,对应的ResultSet也会被强制关闭,跨线程传递ResultSet本身就是错误的做法。

解决方案

最合理的做法是在JDBC资源有效时提取所需数据,再将数据传递到异步线程,而不是传递底层的JDBC资源。修改代码如下:

1. 修改executeUpdate方法,返回封装好结果的Callable<Integer>

public Callable<Integer> executeUpdate(String query, Column<?>... columns) {
    return () -> {
        try (Connection connection = getConnection();
             PreparedStatement statement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)) {
            for (int i = 0; i < columns.length; i++) {
                columns[i].setValue(statement, i + 1);
            }

            statement.executeUpdate();

            try (ResultSet resultSet = statement.getGeneratedKeys()) {
                return resultSet.next() ? resultSet.getInt(1) : -1;
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    };
}

2. 修改调用逻辑,直接获取异步返回的主键值

Callable<Integer> callable = executeUpdate(REGISTER_NEW_ACCOUNT,
        new VarcharColumn("name", name),
        new VarcharColumn("uuid", uuid.toString()),
        new VarcharColumn("rank", PlayerRank.NONE.name()),
        new VarcharColumn("packageRank", PlayerRank.NONE.name())
);
Future<Integer> future = SQL_EXECUTOR.submit(callable);
try {
    return future.get();
} catch (ExecutionException | InterruptedException e) {
    throw new RuntimeException(e);
}

关键注意事项

  • JDBC资源(Connection、Statement、ResultSet)线程不安全,禁止跨线程传递或共享。
  • try-with-resources会自动关闭所有实现AutoCloseable接口的资源,务必确保资源的使用范围在try块内部,不要返回已被关闭的资源。
  • 连接池的Connection调用close()时是归还到池中,而非真正销毁,但依赖它的Statement和ResultSet仍会被关闭,同样不能在Connection归还后使用这些资源。
  • 异步执行数据库操作时,应将完整的JDBC操作逻辑(资源创建、使用、关闭)封装到异步任务中,确保所有操作在同一个线程内完成。

内容的提问来源于stack exchange,提问作者Santiago Narvaez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 20:32:39