如何计算指定项目任务完成率并解决JDBC ResultSet关闭异常?
问题分析与解决方案
核心问题
- ResultSet关闭异常原因:同一个
Statement对象执行第二个executeQuery时,会自动关闭之前打开的ResultSet,导致后续访问第一个ResultSet时抛出"The result set is closed"异常。 - SQL冗余与硬编码:原代码用两次查询获取数据,效率低且
sql2中硬编码了p_id=2,没有使用传入的动态id。 - SQL注入风险:直接通过字符串拼接
id到SQL语句中,存在SQL注入漏洞。
优化方案
1. 合并SQL查询(一次获取完成数与总数)
用单条SQL语句同时统计指定项目的已完成任务数和总任务数,避免多次查询:
SELECT SUM(CASE WHEN state = 1 THEN 1 ELSE 0 END) AS finishedTasks, COUNT(*) AS totalTasks FROM tasks WHERE p_id = ?
SUM(CASE...):统计state=1的任务数量(已完成)COUNT(*):统计该项目的总任务数?是参数占位符,用于后续传入动态p_id,避免SQL注入
2. 修改Java代码(修复异常+安全查询)
使用PreparedStatement替代Statement,同时利用try-with-resources自动关闭连接、语句和结果集,避免资源泄漏:
修改后的projectProgress方法
public void projectProgress() throws SQLException, ClassNotFoundException { int id = Integer.parseInt(PMprogressFrame.Pnum.getText()); String sql = "SELECT SUM(CASE WHEN state = 1 THEN 1 ELSE 0 END) AS finishedTasks, COUNT(*) AS totalTasks FROM tasks WHERE p_id = ?"; // 使用try-with-resources自动关闭资源 try (SqlConnection DB = new SqlConnection(); PreparedStatement pstmt = DB.conn.prepareStatement(sql)) { pstmt.setInt(1, id); // 设置参数,避免SQL注入 ResultSet result = pstmt.executeQuery(); if (result.next()) { float finished = result.getFloat("finishedTasks"); float total = result.getFloat("totalTasks"); float progress = 0.0f; // 处理总任务数为0的情况,避免除以0错误 if (total > 0) { progress = (finished / total) * 100; } PMprogressFrame.progress.setText(String.format("%.2f%%", progress)); // 保留两位小数更友好 } } }
调整SqlConnection类(可选优化)
为了更规范,建议将Statement改为局部变量,避免成员变量复用导致的问题:
public class SqlConnection { public Connection conn; public SqlConnection() throws SQLException, ClassNotFoundException { Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); String connectionUrl = "jdbc:sqlserver://MEMENTOMORI:1433;databaseName=PMS;user=sa;password=12345;encrypt=false"; conn = DriverManager.getConnection(connectionUrl); } }
- 移除了成员变量
st和rs,避免复用带来的资源冲突 - 不再需要
allowMultiQueries=true,因为已经合并为单条查询
关键注意事项
- 资源管理:必须确保
Connection、PreparedStatement、ResultSet在使用后关闭,try-with-resources会自动处理这些操作,避免内存泄漏。 - 除数为0处理:当项目总任务数为0时,直接计算会抛出算术异常,需要先判断
total > 0再计算完成率。 - SQL注入防护:始终使用
PreparedStatement的参数占位符传递动态值,禁止直接拼接字符串到SQL语句中。
内容的提问来源于stack exchange,提问作者Ahmed
相关产品推荐
相关产品推荐

