管理Java servlet的数据库连接的最佳方法

时间:2008-10-27 21:24:58

标签: java database servlets connection

在Java servlet中管理数据库连接的最佳方法是什么?

目前,我只是在init()函数中打开一个连接,然后在destroy()中将其关闭。

然而,我担心“永久”持有数据库连接可能是件坏事。

这是处理此问题的正确方法吗?如果没有,有什么更好的选择?

编辑:进一步澄清一下:我尝试过为每个请求打开/关闭一个新连接,但是通过测试我发现由于创建了太多连接而导致性能问题。

在多个请求中共享连接有什么价值吗?对此应用程序的请求几乎都是“只读”并且速度非常快(尽管请求的数据相当小)。

9 个答案:

答案 0 :(得分:22)

正如大家所说,你需要使用连接池。为什么?怎么了?等

您的解决方案有什么问题

我知道这一点,因为我以前认为这是一个好主意。问题是双重的:

  1. 所有线程(servlet请求每个服务器都有一个线程)将共享同一个连接。因此,将一次处理一个请求。这非常慢,即使您只是坐在一个浏览器中并依靠F5键。尝试一下:这些东西听起来很高级,很抽象,但它是经验和可测试的。
  2. 如果连接因任何原因而中断,则不会再次调用init方法(因为servlet不会停止服务)。不要试图通过在doGet或doPost中放置一个try-catch来处理这个问题,因为那样你就会陷入困境(有点像编写app服务器而不被问到)。
  3. 与人们的想法相反,您不会遇到事务问题,因为事务开始与线程相关联而不仅仅是连接。我可能错了,但是因为这是一个糟糕的解决方案,所以不要冒汗。
  4. 为什么选择连接池

    连接池为您提供了许多优势,但最重要的是它们解决了

    的问题
    1. 建立真正的数据库连接成本很高。连接池总是有一些额外的连接,并为您提供其中一个。
    2. 如果连接失败,连接池将知道如何打开新连接
    3. 非常重要:每个线程都有自己的连接。这意味着线程应该在以下位置处理:在DB级别。 DB非常高效,可以轻松处理并发请求。
    4. 其他东西(比如集中JDBC连接字符串的位置等),但是这里有数百万篇文章,书籍等
    5. 何时获取连接

      在您的服务委托(doPost,doGet,doDisco,无论如何)中启动的调用堆栈中的某个地方,您应该获得连接,然后您应该做正确的事情并将其返回到finally块中。我应该提一下,C#主建筑师老兄曾说过,你应该使用finally块比catch块多100倍。真正的话从未说过......

      哪个连接池

      您在servlet中,因此您应该使用容器提供的连接池。除了获得连接的方式之外,您的JNDI代码将完全正常。据我所知,所有servlet容器都有连接池。

      上述答案的一些评论建议使用特定的连接池API。您的WAR应该是可移植的并且“只是部署”。我认为这基本上是错误的。如果您使用容器提供的连接池,您的应用程序将可以部署在跨多台计算机的容器上,以及Java EE规范提供的所有那些花哨的东西。是的,必须编写特定于容器的部署描述符,但这是EE方式,mon。

      一位评论者提到某些容器提供的连接池不能与JDBC驱动程序一起使用(他/她提到了Websphere)。这听起来完全是牵强附会和荒谬的,所以它可能是真的。当这样的事情发生时,把你应该做的所有事情扔进垃圾桶,尽你所能。这就是我们得到的报酬,有时候:)

答案 1 :(得分:14)

我实际上不同意使用Commons DBCP。您应该真的按照容器来管理连接池。

由于您正在使用Java Servlet,这意味着在Servlet容器中运行,并且我熟悉的所有主要Servlet容器都提供连接池管理(Java EE规范甚至可能需要它)。如果你的容器碰巧使用DBCP(就像Tomcat那样),那么,不管怎么说,只需使用容器提供的任何内容。

答案 2 :(得分:4)

我会使用Commons DBCP。这是一个Apache项目,可以为您管理连接池。

您只需在doGet中获取连接,或者doPost运行查询,然后在finally块中关闭连接。 (con.close()只是将它返回到池中,它实际上并不关闭它。)

DBCP可以管理连接超时并从中恢复。如果您的数据库在任何时间段内出现故障,您当前正在做的事情就是您必须重新启动应用程序。

答案 3 :(得分:2)

你在汇集你的联系吗?如果没有,您可能应该减少打开和关闭连接的开销。

一旦完成,只要按照需要保持连接打开,就像John建议的那样。

答案 4 :(得分:2)

最好的方法,也就是我目前正在通过Google寻找更好的参考表,就是使用游泳池。

在初始化时,您将创建一个池,其中包含数据库的X个SQL连接对象。将这些对象存储在某种List中,例如ArrayList。这些对象中的每一个都有一个'isLeased'的私有布尔值,上次使用的时间很长,还有一个Connection。无论何时需要连接,都可以从池中请求连接。池将为您提供第一个可用连接,检查isLeased变量,或者它将创建一个新连接并将其添加到池中。确保设置时间戳。完成连接后,只需将其返回到池中,池将isLeased设置为false。

为了避免不断地连接数据库,您可以创建一个工作线程,该线程偶尔会通过池并查看上次使用连接的时间。如果它足够长,它可以关闭该连接并将其从池中删除。

使用它的好处是,您没有等待连接对象连接到数据库的漫长等待时间。您已建立的连接可以根据需要重复使用。并且您将能够根据您认为应用程序的繁忙程度来设置连接数。

答案 5 :(得分:1)

您应该只在需要时保持数据库连接处于打开状态,这取决于您正在执行的操作可能属于doGet/doPost方法的范围。

答案 6 :(得分:1)

与数据源关联的连接池应该可以解决问题。您可以在servlet请求方法(doget / dopost等)中获取dataSource的连接。

dbcp,c3p0和许多其他连接池可以满足您的需求。在汇集连接时,您可能希望汇总语句和PreparedStatements;此外,如果您指的是READ HEAVY环境,您可能希望使用ehcache之类的东西来缓存一些结果。

BR,
〜A

答案 7 :(得分:1)

汇集它。

此外,如果您正在使用原始JDBC,您可以查看一些可以帮助您管理Connection,PreparedStatement等的内容。除非您有非常严格的“轻量级”要求,否则使用Spring的JDBC支持将会简化你的代码很多 - 你不会被迫使用Spring的任何其他部分。

请参阅此处的一些示例:

http://static.springframework.org/spring/docs/2.5.x/reference/jdbc.html

答案 8 :(得分:0)

通常,您会发现每个请求的开放连接更易于管理。这意味着在servlet的doPost()或doGet()方法中。

在init()中打开它使所有请求都可用,并且当你有并发请求时会发生什么?