使用JasperReport后连接池连接未释放问题排查与解决求助
从你的代码和现象来看,核心问题是数据库连接泄漏——每次生成报表时都会多占用一个连接且未正确释放,直到连接池被耗尽,导致超时或阻塞。我们来一步步拆解原因和修复方案:
直接原因:重复获取连接且未关闭
看你生成报表的核心代码片段:
connection = dataSource.getConnection(); final JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, null, dataSource.getConnection());
这里你犯了一个关键错误:
- 你先获取了一个
connection变量,但在fillReport时又调用了dataSource.getConnection(),这会从连接池新获取一个独立连接 - 你在finally块里只关闭了之前的
connection变量,但fillReport使用的那个新连接完全没有被关闭! - 每次生成报表就会泄漏一个连接,当泄漏的连接数达到连接池最大容量(Hikari默认10,DBCP默认8)时,就会出现超时或阻塞
而SimpleDriverDataSource是无池化的数据源,每次getConnection()都会创建一个全新的物理连接,用完后JVM最终会自动回收(虽然不优雅),所以不会出现池耗尽的情况,但这显然不适合生产环境。
修复方案:复用已获取的连接并确保关闭
修改代码,让fillReport复用你已经获取的connection,并确保这个连接在finally块被正确关闭:
private void generatePdfReport(final Report report) throws SQLException, JRException { final JasperReport jasperReport = JASPER_REPORT_MAP.get(report); if (jasperReport == null) { throw new UnsupportedOperationException("The report is not supported: " + report.getName()); } Connection connection = null; try { connection = dataSource.getConnection(); // 这里复用已获取的connection,而非重新调用dataSource.getConnection() final JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, null, connection); final JRPdfExporter exporter = new JRPdfExporter(); exporter.setExporterInput(new SimpleExporterInput(jasperPrint)); exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(report.getFormattedFilename() + Report.EXT_PDF)); exporter.setConfiguration(reportConfig); exporter.setConfiguration(exportConfig); exporter.exportReport(); log.info("PDF Report ({}) exported successfully!", report.getName()); } finally { if (connection != null) { log.info("about to close db connection"); try { connection.close(); } catch (SQLException e) { log.error("Failed to close database connection", e); } connection = null; } } }
额外排查与优化点
JasperReports连接自动关闭配置
可以在报表参数中添加net.sf.jasperreports.jdbc.close.connection参数并设置为true,让JasperReports在填充完报表后自动关闭传入的连接(不过更推荐自己显式管理连接,避免依赖框架行为):Map<String, Object> params = new HashMap<>(); params.put("net.sf.jasperreports.jdbc.close.connection", true); final JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, params, connection);TransactionAwareDataSourceProxy的影响
如果使用TransactionAwareDataSourceProxy,它会把连接绑定到当前事务上下文。如果你的报表生成代码在事务中执行,连接可能不会立即关闭,直到事务提交/回滚。确保报表生成逻辑不在不必要的事务中运行,或者显式控制事务边界。版本兼容性检查
某些旧版本的JasperReports可能存在连接泄漏的bug,建议升级到最新稳定版(比如6.x系列),同时确保JDBC驱动与数据库版本完全兼容。连接池监控
启用连接池的监控功能(HikariCP可以通过HikariMXBean查看连接状态),确认修复后连接是否被正确回收,没有IN_USE的连接堆积。
内容的提问来源于stack exchange,提问作者Julian

