資料庫連線洩漏是應用開發中的噩夢。連線池耗盡將導致應用癱瘓,甚至拖垮資料庫。Spring DAO 的模板類(JdbcTemplate、HibernateTemplate 等)透過內部封裝保證連線獲取與釋放的正確性,但當整合第三方庫或遺留代碼時,若直接從 DataSource 獲取連線卻未正確歸還,洩漏便會發生。
在事務方法中調用 jdbcTemplate.getDataSource().getConnection() 會繞過 Spring 事務管理,創建獨立連線。若未在 finally 區塊顯式 close(),該連線永不釋回池中。模擬多線程併發可清晰觀察到 active 連線數單調上升,idle 不變,最終耗盡連線池。
Spring 提供 DataSourceUtils 工具類,其 getConnection(DataSource) 邏輯為:
TransactionSynchronizationManager)獲取綁定連線;替換代碼後,併發測試顯示 active 連線數始終為 1,方法結束即歸還,洩漏問題解決。
若方法未被事務增強(如移除 AOP 配置),DataSourceUtils.getConnection() 會直接從池中取連線,但不會自動綁定釋放邏輯。此時必須手動在 finally 中調用 releaseConnection(),且不可放在 try 區塊中(異常拋出前未釋放)。
Connection conn = null;
try {
conn = DataSourceUtils.getConnection(dataSource);
// 業務操作
} finally {
DataSourceUtils.releaseConnection(conn, dataSource); // 必須在 finally
}
JdbcTemplate.execute() 採用模板方法模式:統一在 try 前獲取連線,finally 中釋放,catch 異常時亦釋放。這種強制模式化流程是其免疫洩漏的根本原因。建議:盡可能使用模板類而非手動管理連線。
若不想修改代碼,可配置 TransactionAwareDataSourceProxy 代理原始 DataSource。代理後的 getConnection() 具備事務感知能力,行為等同 DataSourceUtils.getConnection()。配置關鍵在於將 JdbcTemplate 和事務管理器均引用代理後的 DataSource。
Spring 為各主流持久化框架提供了對應工具:
| 框架 | 連線工具類 | 代理類 |
|---|---|---|
| Spring JDBC | DataSourceUtils | TransactionAwareDataSourceProxy |
| Hibernate | SessionFactoryUtils | LocalSessionFactoryBean |
| JPA | EntityManagerFactoryUtils | 無 |
| JDO | PersistenceManagerFactoryUtils | TransactionAwarePersistenceManagerFactoryProxy |
連線洩漏的本質是「獲取與釋放不對稱」。核心原則:優先使用模板類;若必須手動獲取,統一用對應工具類獲取,並在 finally 中釋放;或配置事務感知代理透明化管理。掌握這套機制,可將解決方案平滑遷移至任何 Spring 支持的持久化框架。