其实,自己写,好像也没有很好的办法。
各个jdbc driver对连接无效给出的异常没有标准。往往就是一个SQLException。让你很难区分这是一个普通的数据库异常还是因为数据库重新启动了或者fail over了。而且,也不存在一个安全的测试异常状态的函数。isClosed()不能用,因为它只表示这个connection.close()是否被调用过。

解决方案 »

  1.   

    这样实现连接池的getConnection方法:
    public Connection getConnection() throws SQLException
    {
        while(没有超时)
        {
            Connection conn = 取得一个空闲的连接
            try
            {
                用conn执行一下数据库操作(如执行一条简单的SELECT语句)
                return conn; //成功,返回该连接
            }
            catch(SQLException e)
            {
                丢弃conn
                e.printStackTrace();
            }
         }
         throw new SQLException("取连接超时");
    }虽然速度慢了一点,但可以在一定程度上解决数据库重启的问题
      

  2.   

    有时候,SQLException可能就是普通的异常,比如,试图插入重复key;网络暂时无法连接;资源timeout;资源死锁;硬盘空间耗尽;sql语法错;sql语句内部类型匹配错(比如,试图比较整数和字符串);jdbc api的类型匹配错, 等等等等。
    有些错误可以认为无法恢复(如网络失败或者硬盘耗尽),但是有些错,比如类型匹配错,资源申请暂时超时等应该可以恢复的。
    cbhyk() ,你的代码里面的“用conn执行一下数据库操作”就是随便执行一下测试语句?
    这样,不是就引入了一个来回roundtrip?感觉很不舒服,而且也有违与连接池的高效率的目的。而且,如果遇到fail over的情况,不希望简简单单地抱错,最理想的情况是,连接池可以发现问题,自动关闭放弃当前的连接,并且重新产生新的连接。所有这一切应该透明于用户,甚至最好对应用程序透明。据我想,一个可能的方案是,连接池内部纪录一个当前的池版本id,并且把这个id赋给每一个生成的Connection对象。
    一旦发生SQLException,连接池就进入一个特殊的“清理”状态。这个状态内部,把当前的版本id加一,把正在cache的连接全部关掉。遇到连接返回池的时候,检查版本id,如果不同于新的版本id,就关掉它而不是缓存。
    但是,这个方法的一个问题是:如果万一某一个连接发生了任何一个上述的SQLException,那么就意味着整个池进入这个“清理”状态。
    而连接池必须封装所有的ResultSet, Statement,Connection等 jdbc对象,一个对象也不能直接漏给程序员。这些封装的ResultSet proxy, Statement proxy, Connection proxy等需要重载所有的方法,截取所有的异常。而且这些异常里面也许有其它的合法异常,不分青红皂白地遇到SQLException就关闭Connection好象也不是很好。
    但是,这个东西做起来好象比较复杂,而且,似乎也没有连接池这么做。
      

  3.   

    truezerg,我们的系统目前没有好的办法,一旦数据库重新启动或者fail over,必须手工重新启动应用服务器。
    这个系统本来目标是24/7,看来做不到了。
      

  4.   

    我们也遇到了这个问题,是因为客户的数据库服务器定时会关毕一会。
    解决办法竟然这么简单,使用c3p0,一个开源的pool,自带连接恢复功能。
      

  5.   

    我们也使用o/r mapping 只不过我们使用的是hibernate,他本身自带的就是c3p0
      

  6.   

    这个问题困扰了我好久
    最后采用了jakarta的commons-DBCP才得以解决他们的解决方法是,定一个Query语句作为测试用
    一旦数据库发生错误,就测试Connection是否连通
    如果连不通则抛弃重新建立Conn很简单的:〉
      

  7.   

    dbcp我们也尝试过,websphere自带的数据源也试过,似乎都不好用(测试语句也太费了一点,必须定时执行)
      

  8.   

    ajoo, 想一想,应用服务器重启都做了哪些事情? 有点启发吗? 我现在没太想好这个问题。再想一想。 如果解决了“如何知道数据库重启了”,那就清一下池。这样能行吗?
      

  9.   

    我随便想到的,瞎说不知道行不行。 你们不是有源码吗?  别的地方都不改,只改一下从池中取连接的地方,在从池中取连接的时候首先判断该连接是否有效。如果没有效就在计数器上加一,同时关闭掉该连接并且不允许cache,如果计数器累加值超过5(这个数由你定,你认为有多少个连接无效可视为数据库重启了,就定到几),就意味着数据库重启了。这样你就清池。这个方法确实有点浪费时间,因为毕竟重启的可能性少,但却必需在取连接时都要检查。写到这里看来这个方法是不行了。 这个问题最关键的地方是如何知道在池中取得的连接是否有效。这个问题解决了,就可以在一定的条件下(比如我取了五次链接都是无效的)来判断数据库是否重启了。 判断出是否重启了,就可以清池然后就解决了。 要找到一个即不怎么浪费时间的做法,又要得到结果。 看来挺难又突然想到一个办法。 ajoo你不是说可能会出现很多种情况导致出现SQLException异常吗? 你可不可以这样,在出现SQLException的时候判断出现异常的connection是否有效,如果有效就说明是其它的情况出现的,如果无效就计数器加一,加到一定时候就清池。  出现SQLException的机会比每次出池时都检查相对少用了很多时间。把检查连接是否有效放在这里好像比较好一点。 (不过我不知道出现一个异常以后连接会不会自动关掉,如果是自动关掉的话,放在异常里检查就不行了。 ajoo 你试一下吧)ajoo你想到什么好点子没有?
      

  10.   

    eyeieye(魔之眼):
    谢谢。我下载了c3p0,看了一下它的代码。似乎,它也是在给用户返回一个connection的时候(当然,它也有idle时候的检查)检查这个连接的状态,通过的方法就是检查一个预先创建好的表是否还在。
    也一样是一个roundtrip的检查了(看来这必不可少)它还通过检查SQLException里面的error code,它认为state code 08001, 08001代表数据库无效,需要重新启动连接池。
    truezerg,看来不浪费时间来保证连接有效是不可能的了。只能说,做这个测试的时间,远远小于创建连接本身的时间。
    另外,连接的有效性必须在连接被返回给用户之前做,返回给用户后,再试图处理恢复就很复杂了。好在,database fail over这种东西,可以允许某些用户的连接失败,只要新用户的连接能够成功,就好了。