org.springframework.transaction.CannotCreateTransactionException:
Could not open Hibernate Session for transaction;
nested exception is org.hibernate.exception.JDBCConnectionException: Could not open connection
資料庫連線在閒置一段時間後被 MySQL 伺服器主動斷開 (預設 wait_timeout=8h),連線池持有的連線變成「失效連線」。後續嘗試獲取連線開啟 Session 時,JDBC 驅動拋出 Communications link failure,Hibernate 包裝為 JDBCConnectionException,Spring 再包裝為 CannotCreateTransactionException。
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8"/>
<property name="user" value="root"/>
<property name="password" value="password"/>
<!-- 核心存活檢測參數 -->
<property name="preferredTestQuery" value="SELECT 1"/> -- 高效測試查詢
<property name="idleConnectionTestPeriod" value="300"/> -- 每 300 秒 (5 分鐘) 檢測一次閒置連線
<property name="testConnectionOnCheckin" value="true"/> -- 歸還池時檢測
<property name="testConnectionOnCheckout" value="true"/> -- 從池取出時檢測 (效能稍低但最保險)
<property name="maxIdleTime" value="1800"/> -- 閒置超過 30 分鐘銷毀,避免累積失效連線
<!-- 池大小 -->
<property name="minPoolSize" value="5"/>
<property name="maxPoolSize" value="20"/>
<property name="acquireIncrement" value="3"/>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
<!-- 核心存活檢測 -->
<property name="validationQuery" value="SELECT 1"/>
<property name="testOnBorrow" value="true"/> -- 取出時檢測
<property name="testOnReturn" value="false"/> -- 歸還時不檢測 (效能)
<property name="testWhileIdle" value="true"/> -- 閒置檢測開啟
<property name="timeBetweenEvictionRunsMillis" value="60000"/> -- 每 60 秒掃描一次
<property name="minEvictableIdleTimeMillis" value="300000"/> -- 閒置 5 分鐘以上可被驅逐
<property name="keepAlive" value="true"/> -- MySQL 專用:定時 ping 保持長連線
<!-- 池大小 -->
<property name="initialSize" value="5"/>
<property name="minIdle" value="5"/>
<property name="maxActive" value="20"/>
</bean>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
<property name="connectionTestQuery" value="SELECT 1"/>
<property name="idleTimeout" value="300000"/> -- 5 分鐘
<property name="maxLifetime" value="1800000"/> -- 30 分鐘強制換新
<property name="minimumIdle" value="5"/>
<property name="maximumPoolSize" value="20"/>
</bean>
[mysqld]
wait_timeout = 28800 # 8 小時 (預設)
interactive_timeout = 28800
# 建議將 wait_timeout 設大於連線池 maxLifetime/idleTimeout,讓池端主動管理生命週期
KILL CONNECTION_ID(); (MySQL) 或重啟 MySQL 服務。
3. 等待 idleConnectionTestPeriod / timeBetweenEvictionRunsMillis 間隔過後。
4. 發起業務請求,觀察是否自動重連成功、無異常拋出。
「Could not open Hibernate Session for transaction」本質是連線池持有失效連線。核心解法:在連線池層面配置存活檢測 (testQuery + 定期檢測 + 取出/歸還檢測),並淘汰舊版 DBCP,改用 **C3P0 / Druid / HikariCP**。Druid 在國內生產環境最成熟,HikariCP 效能最強,C3P0 配置最經典。三選一,務必正確配置上述參數,即可徹底解決閒置斷連問題。