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

jOOQ中ExecuteListener.end事件查询数据库遇连接关闭问题求助

解决方案

1. 切换到executeEnd()方法执行检查逻辑

问题核心在于end()方法是jOOQ执行流程的最后阶段,此时连接已被ConnectionProvider归还到连接池并关闭。而executeEnd()方法会在SQL语句执行完成后、连接归还前触发,此时连接仍处于可用状态,完全满足insert后的检查需求。

代码示例:

import org.jooq.ExecuteContext;
import org.jooq.ExecuteType;
import org.jooq.impl.DefaultExecuteListener;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class PostInsertCheckListener extends DefaultExecuteListener {
    @Override
    public void executeEnd(ExecuteContext ctx) {
        // 仅对INSERT语句执行检查
        if (ctx.type() != ExecuteType.INSERT) {
            return;
        }

        try (PreparedStatement stmt = ctx.connection().prepareStatement(
            "SELECT COUNT(*) FROM your_table WHERE id = ?"
        )) {
            // 可通过ctx获取插入语句的参数,示例中假设插入的ID是第一个参数
            stmt.setObject(1, ctx.query().getBindValues().get(0));
            
            try (ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) {
                    int count = rs.getInt(1);
                    // 根据检查结果处理逻辑,比如抛出异常或记录日志
                    if (count != 1) {
                        ctx.exception(new SQLException("Insert check failed: unexpected record count"));
                    }
                }
            }
        } catch (SQLException e) {
            // 将异常传递给jOOQ处理
            ctx.exception(e);
        }
    }
}

2. 若需事务提交后检查,使用TransactionListener

如果你的检查逻辑必须在整个事务提交后执行(而非单条insert语句执行后),可以通过jOOQ的TransactionListener监听事务提交事件,此时需手动获取新连接执行检查(确保不依赖已关闭的事务连接)。

代码示例:

import org.jooq.TransactionContext;
import org.jooq.ConnectionProvider;
import org.jooq.impl.DefaultTransactionListener;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class PostCommitCheckListener extends DefaultTransactionListener {
    @Override
    public void commitEnd(TransactionContext ctx) {
        ConnectionProvider provider = ctx.configuration().connectionProvider();
        Connection conn = null;

        try {
            // 获取新的可用连接
            conn = provider.getConnection();
            
            try (Statement stmt = conn.createStatement()) {
                // 执行事务提交后的检查逻辑,示例:验证事务内所有插入的记录状态
                String checkSql = "SELECT COUNT(*) FROM your_table WHERE transaction_id = 'xxx'";
                try (ResultSet rs = stmt.executeQuery(checkSql)) {
                    if (rs.next()) {
                        int count = rs.getInt(1);
                        // 处理检查结果
                    }
                }
            }
        } catch (SQLException e) {
            ctx.exception(e);
        } finally {
            // 手动归还连接到连接池
            if (conn != null) {
                try {
                    provider.releaseConnection(conn);
                } catch (SQLException e) {
                    // 记录归还连接时的异常
                }
            }
        }
    }
}

3. 确保手动管理连接的获取与释放(若必须用end()方法)

如果因特殊需求必须在end()方法中执行逻辑,需注意手动从ConnectionProvider获取新连接,并在使用后调用releaseConnection()归还到池,避免连接泄漏或拿到已关闭的连接:

import org.jooq.ExecuteContext;
import org.jooq.ExecuteType;
import org.jooq.ConnectionProvider;
import org.jooq.impl.DefaultExecuteListener;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.SQLException;

public class PostInsertEndListener extends DefaultExecuteListener {
    @Override
    public void end(ExecuteContext ctx) {
        if (ctx.type() != ExecuteType.INSERT) {
            return;
        }

        ConnectionProvider provider = ctx.configuration().connectionProvider();
        Connection conn = null;

        try {
            conn = provider.getConnection();
            // 执行检查逻辑,例如查询元数据
            try (Statement stmt = conn.createStatement()) {
                stmt.executeQuery("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'");
                // 处理查询结果
            }
        } catch (SQLException e) {
            ctx.exception(e);
        } finally {
            if (conn != null) {
                try {
                    provider.releaseConnection(conn);
                } catch (SQLException e) {
                    // 处理异常
                }
            }
        }
    }
}

兼容性说明

以上方案均基于jOOQ标准API实现,未依赖Quarkus的AgroalDataSource或其他特定环境类,可直接兼容纯jOOQ+H2环境与Quarkus+PostgreSQL环境。只需将自定义Listener配置到jOOQ的Configuration中即可:

  • 纯jOOQ环境:通过DefaultConfiguration().setExecuteListener()或setTransactionListener()添加
  • Quarkus环境:通过CDI将自定义Listener注册为Bean,Quarkus会自动将其注入到jOOQ的配置中

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 12:55:21