MySQL缺省配置下,会自动断开那些idle超过8小时的Connection,如果应用程序保持这个连接,8个小时 (wait_timeout=28800秒)后,用JDBC,再次访问数据库,会有异常抛出,据说用autoReconnect=true可以避免这个问 题,不管你信不信,反正我信了,而且好多年前,我就这么做的。
直到最近,在一个鲜有人访问的应用中发现,问题依旧,8小时候后的第一次访问,总是失败,刷新一下页面,就正常了。Google了一下,才知道MySQL不推荐使用autoReconnect=true来解决此问题,http://bugs.mysql.com/bug.php?id=5020,如下:
[12 Aug 2004 18:46] Mark Matthews
Note: Autoreconnect functionality will be depcreated and eventually removed in future releases. The reason this isn't working for your particular case is that the methodolgy for autoreconnect was changed to be safer after 3.0.11, and is related to autoCommit state, which will also cause the current 'in-flight' transaction to fail (if you attempt your transaction again _after_ the failure, the driver will reconnect). Please see the docs for the explanation on how to correctly use this feature in the 'Troubleshooting' section. In any case, there is no 100% safe way that a JDBC driver can re-connect automatically if a TCP/IP connection dies without risking corruption of the database 'state' (even _with_ transactional semantics), which is why this feature will eventually be removed. The JDBC spec does not specify that a connection is alive no matter what happens to the underlying network for this very reason. Clients of JDBC drivers are responsible for dealing with network failures, as only the application itself (really the developer of the application) 'knows' what the 'correct' response to a transaction failing due to the network going down is. 'Wait_timeout' expiring on the server is basically a 'forced' network failure by the server. You can correct this in a non-robust way by setting 'wait_timeout' higher, however, you as a developer should be handling SQL exceptions in your code and taking appropriate recovery actions, not just passing them up the call stack.
解决也很简单,记录最近一次的Connection访问时间,如果超过了8小时,就重新建立这个连接,经验证是可行的。另外也可以在建立Connection的同时,创建一个线程定时的去执行一个SELECT 1语句,前面的方法比较简单。
这里要指出的是,用Connection的isValid()方法去检测Connection是否有效是不行的,程序会死在那里(未调查原因),试 图用isClosed()去判断Connection是否断开也是行不通的,因为只有明确的调用了Connection的close()方法后,你才能用 isClosed()去判断,也就是说Connection在idle超过8小时候后,这个时候你如果去打印它的isClosed()的值,它还是显示 false,但实际上却不能使用。