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

Java插入ArrayList到数据库时遇SQLNonTransientConnectionException错误求助

问题分析与修复方案

核心问题原因

  1. 静态连接复用导致的失效问题
    你的db类使用了静态connection变量,第一次调用getConnection()会创建有效连接,但业务代码的finally块执行con.close()后,这个静态变量并未被置为null。后续调用getConnection()时,因connection != null会直接返回已关闭的连接,执行数据库操作就会触发No operations allowed after connection closed错误。

  2. 资源泄漏风险
    循环中每次创建的PreparedStatement未被关闭,会持续占用数据库资源,长期运行可能引发连接耗尽等问题。

  3. SQL注入漏洞
    直接将变量拼接进SQL语句,不仅存在SQL注入风险,还可能因特殊字符导致语法错误。

修复步骤

1. 修复数据库连接类

移除静态连接的复用逻辑,改为每次获取新连接(简单场景推荐),或增加连接有效性检查:

方案一:每次创建新连接

static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost:3306/tes";
static final String USER = "root";
static final String PASS = "";

// 移除静态connection变量,避免复用已关闭连接
public static Connection getConnection() throws SQLException, ClassNotFoundException {
    Class.forName("com.mysql.cj.jdbc.Driver");
    return DriverManager.getConnection(DB_URL, USER, PASS);
}

方案二:保留复用并检查有效性

private static Connection connection;
public static Connection getConnection() throws SQLException, ClassNotFoundException {
    // 检查连接是否为空或已关闭,无效则重建
    if (connection == null || connection.isClosed()) {
        connection = createConnection();
    }
    return connection;
}

private static Connection createConnection() throws SQLException, ClassNotFoundException {
    Class.forName("com.mysql.cj.jdbc.Driver");
    return DriverManager.getConnection(DB_URL, USER, PASS);
}

2. 修复业务代码

使用参数化查询避免注入,正确释放资源,优化循环逻辑:

Connection con = db.getConnection();
PreparedStatement p1 = null;
try{
    // 预编译SQL,使用占位符避免注入
    String sql = "INSERT INTO history (id_transaction, kode_saham, lot, harga, username, status, lot_sell_check) VALUES(NULL, ?, ?, ?, ?, 'Buy', ?)";
    p1 = con.prepareStatement(sql);
    
    for(Stock element: listStock){
        if(element.getKodeSaham().equals(getKode_saham())){
            int hargaSaham = element.getHarga();
            int harga = (lot*100) * hargaSaham;
            
            // 空列表直接跳过,避免无效循环
            if(listTransaksi.isEmpty()) continue;
            
            // 一次性设置参数,循环执行插入
            p1.setString(1, getKode_saham());
            p1.setInt(2, getLot());
            p1.setInt(3, harga);
            p1.setString(4, getUsername());
            p1.setInt(5, getLot());
            
            for(Transaction trans : listTransaksi){
                p1.executeUpdate();
            }
        }
    }
} finally {
    // 按顺序关闭资源:先关闭PreparedStatement,再关闭连接
    if(p1 != null){
        try{
            p1.close();
        } catch(SQLException e){
            e.printStackTrace();
        }
    }
    if(con != null){
        try{
            con.close();
        } catch(SQLException e){
            e.printStackTrace();
        }
    }
}

额外建议

  • 生产环境优先使用数据库连接池(如HikariCP),比手动管理连接更高效稳定
  • 将数据库配置(URL、账号、密码)移至配置文件,避免硬编码
  • 异常处理不要仅打印日志,需结合业务场景做回滚、用户提示等操作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 14:16:23