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

Java操作PostgreSQL数据库commit提交失败问题排查

问题分析:PostgreSQL JDBC连接关闭导致commit失败

问题回顾

你在Java操作PostgreSQL时遇到了提交数据失败的问题,控制台抛出:

org.postgresql.util.PSQLException: This connection has been closed

堆栈追踪明确指向addToTable方法里的c.commit();行,而你确认getId(t.getName())和参数i的输出都符合预期,其他类似插入逻辑也能正常运行。

核心原因:类成员变量被意外覆盖并关闭

问题出在类成员变量c(Connection)的复用冲突上!

看你的addToTable方法流程:

  1. 创建新连接并赋值给类成员c
  2. 调用getId(t.getName())方法
  3. 尝试对c执行commit操作

而你的getId(对应类里的getTableId或getDrinkId)方法内部,又重新给类成员c赋值了一个新的连接,并且在方法结束前调用了c.close()关闭这个新连接。

这就导致:当getId执行完毕回到addToTable时,类成员c已经被替换成了那个被关闭的连接,此时再调用c.commit()自然会抛出连接已关闭的异常。

简单梳理一下时序:

  • addToTable:创建连接A → 赋值给类成员c
  • getId:创建连接B → 覆盖类成员c → 查询完成后关闭连接B
  • addToTable:此时c是已关闭的连接B → 执行c.commit() → 报错

解决方案

1. 优先推荐:使用局部变量存储连接和Statement

把Connection和Statement从类成员改成方法内的局部变量,让每个方法的连接完全独立,避免互相干扰。

修改后的addToTable方法:

@Override
public void addToTable(T t, int i) {
    try {
        Class.forName("org.postgresql.Driver");
        // 用局部变量存储连接,不再使用类成员c
        Connection conn = DriverManager.getConnection(connectionUrl, connectionUser, connectionPwd);
        conn.setAutoCommit(false);
        System.out.println("Opened database successfully");
        Statement stmt = conn.createStatement();
        int id = this.getId(t.getName());
        String sql = "INSERT INTO app.my_table_name VALUES ('" + i + "', '" + id + "');";
        stmt.executeUpdate(sql);
        stmt.close();
        conn.commit();
        conn.close();
    } catch (Exception e) {
        System.err.println( e.getClass().getName()+": "+ e.getMessage());
        for (StackTraceElement error : e.getStackTrace()) {
            System.out.println(error);
        }
        System.exit(0);
    }
    System.out.println("Row inserted successfully");
}

同步修改getTableId方法(同理getDrinkId):

@Override
public int getTableId(String tableName) {
    try {
        Class.forName("org.postgresql.Driver");
        // 局部变量存储连接
        Connection conn = DriverManager.getConnection(connectionUrl, connectionUser, connectionPwd);
        conn.setAutoCommit(false);
        System.out.println("Opened database successfully");
        Statement stmtQuery = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY );
        int tableId = 0;
        String sql = "SELECT tl.id FROM app.tables tl WHERE tl.table_name = '" + tableName + "' LIMIT 1;";
        ResultSet rs = stmtQuery.executeQuery(sql);
        while (rs.next()) {
            tableId = rs.getInt("id");
        }
        conn.commit();
        stmtQuery.close();
        conn.close();
        return tableId;
    } catch (Exception e) {
        System.err.println( e.getClass().getName()+": "+ e.getMessage() );
        System.exit(0);
    }
    System.out.println("Row inserted successfully");
    return 0;
}

2. 进阶优化:使用数据库连接池

如果频繁创建关闭连接影响性能,可以引入连接池(比如HikariCP),它能帮你管理连接的复用,避免重复创建销毁连接的开销,同时也能避免此类成员变量冲突问题。

3. 不推荐:临时保存并恢复类成员连接

如果非要保留类成员变量的方式,可以在getId方法里先保存原有连接,执行完后恢复,但这种方式极易出错,不建议使用:

@Override
public int getTableId(String tableName) {
    Connection originalConn = c; // 保存原有连接
    try {
        Class.forName("org.postgresql.Driver");
        c = DriverManager.getConnection(connectionUrl, connectionUser, connectionPwd);
        c.setAutoCommit(false);
        // ... 原有查询逻辑
        c.close();
        c = originalConn; // 恢复原有连接
        return tableId;
    } catch (Exception e) {
        // ... 异常处理
    }
    return 0;
}

额外提醒:避免SQL注入风险

你的代码目前直接拼接字符串生成SQL,存在严重的SQL注入风险,建议改用PreparedStatement:

String sql = "INSERT INTO app.my_table_name VALUES (?, ?);";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, i);
pstmt.setInt(2, id);
pstmt.executeUpdate();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 20:17:55