Java插入ArrayList到数据库时遇SQLNonTransientConnectionException错误求助
问题分析与修复方案
核心问题原因
静态连接复用导致的失效问题
你的db类使用了静态connection变量,第一次调用getConnection()会创建有效连接,但业务代码的finally块执行con.close()后,这个静态变量并未被置为null。后续调用getConnection()时,因connection != null会直接返回已关闭的连接,执行数据库操作就会触发No operations allowed after connection closed错误。资源泄漏风险
循环中每次创建的PreparedStatement未被关闭,会持续占用数据库资源,长期运行可能引发连接耗尽等问题。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
相关产品推荐
相关产品推荐

