27

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

目前,我只是在函数中打开一个连接init(),然后在destroy().

但是,我担心“永久”保持数据库连接可能是一件坏事。

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

编辑:提供更多说明:我尝试简单地为每个请求打开/关闭一个新连接,但是通过测试我发现由于创建太多连接而导致性能问题。

通过多个请求共享连接有什么价值吗?对这个应用程序的请求几乎都是“只读的”并且来得相当快(尽管请求的数据相当小)。

4

9 回答 9

22

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

您的解决方案有什么问题

我知道这一点,因为从前我也认为这是一个好主意。问题有两个方面:

  1. 所有线程(每个 servlet 请求由一个线程提供服务)将共享相同的连接。因此,请求将一次处理一个。即使您只是坐在单个浏览器中并靠在 F5 键上,这也很慢。试试看:这些东西听起来很高级和抽象,但它是经验性的和可测试的。
  2. 如果连接因任何原因中断,则不会再次调用 init 方法(因为 servlet 不会停止服务)。不要尝试通过在 doGet 或 doPost 中放置 try-catch 来解决此问题,因为那样您将陷入困境(有点像在不被询问的情况下编写应用程序服务器)。
  3. 与人们可能想的相反,事务不会有问题,因为事务开始与线程相关联,而不仅仅是连接。我可能是错的,但由于无论如何这是一个糟糕的解决方案,请不要担心。

为什么连接池

连接池为您提供了一大堆优势,但最重要的是它们解决了以下问题

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

何时获得连接

在您的服务委托(doPost、doGet、doDisco 等)中启动的调用堆栈中的某个地方,您应该获得一个连接,然后您应该做正确的事情并在 finally 块中返回它。我应该提一下,C# 的主要架构师曾经说过,你应该使用finally块 100 倍于catch块。真实的话从来没有说过...

哪个连接池

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

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

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

于 2009-05-25T16:27:51.203 回答
14

我实际上不同意使用 Commons DBCP。您应该真正遵从容器来为您管理连接池。

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

于 2008-10-27T22:57:02.213 回答
4

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

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

DBCP 可以管理连接超时并从中恢复。如果您的数据库在任何时间段内出现故障,您当前的处理方式将不得不重新启动您的应用程序。

于 2008-10-27T21:40:31.057 回答
2

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

一旦这不碍事,只要它需要,只要保持连接打开,就像约翰建议的那样。

于 2008-10-27T21:35:02.177 回答
2

最好的方法,我目前正在通过谷歌寻找更好的参考表,是使用池。

在初始化时,您创建一个包含 X 个数据库的 SQL 连接对象的池。将这些对象存储在某种列表中,例如 ArrayList。这些对象中的每一个都有一个用于“isLeased”的私有布尔值,一个表示上次使用时间的 long 和一个 Connection。每当您需要连接时,您都可以从池中请求一个。池要么为您提供第一个可用连接,检查 isLeased 变量,要么创建一个新连接并将其添加到池中。确保设置时间戳。完成连接后,只需将其返回到池中,这会将 isLeased 设置为 false。

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

使用它的好处是,您无需等待 Connection 对象连接到数据库的时间很长。您已经建立的连接可以尽可能多地重复使用。您将能够根据您认为应用程序的繁忙程度来设置连接数。

于 2008-10-27T21:35:23.173 回答
1

您应该只在需要时保持数据库连接打开,这取决于您正在做的事情可能在您的doGet/doPost方法范围内。

于 2008-10-27T21:31:30.500 回答
1

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

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

BR,
~A

于 2008-10-27T22:15:53.873 回答
1

汇集它。

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

在此处查看一些示例:

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

于 2008-10-27T23:35:04.617 回答
0

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

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

于 2008-10-27T21:35:49.977 回答