在 Java servlet 中管理数据库连接的最佳方法是什么?
目前,我只是在函数中打开一个连接init()
,然后在destroy()
.
但是,我担心“永久”保持数据库连接可能是一件坏事。
这是处理这个问题的正确方法吗?如果没有,有什么更好的选择?
编辑:提供更多说明:我尝试简单地为每个请求打开/关闭一个新连接,但是通过测试我发现由于创建太多连接而导致性能问题。
通过多个请求共享连接有什么价值吗?对这个应用程序的请求几乎都是“只读的”并且来得相当快(尽管请求的数据相当小)。
在 Java servlet 中管理数据库连接的最佳方法是什么?
目前,我只是在函数中打开一个连接init()
,然后在destroy()
.
但是,我担心“永久”保持数据库连接可能是一件坏事。
这是处理这个问题的正确方法吗?如果没有,有什么更好的选择?
编辑:提供更多说明:我尝试简单地为每个请求打开/关闭一个新连接,但是通过测试我发现由于创建太多连接而导致性能问题。
通过多个请求共享连接有什么价值吗?对这个应用程序的请求几乎都是“只读的”并且来得相当快(尽管请求的数据相当小)。
正如大家所说,您需要使用连接池。为什么?怎么了?等等。
您的解决方案有什么问题
我知道这一点,因为从前我也认为这是一个好主意。问题有两个方面:
为什么连接池
连接池为您提供了一大堆优势,但最重要的是它们解决了以下问题
何时获得连接
在您的服务委托(doPost、doGet、doDisco 等)中启动的调用堆栈中的某个地方,您应该获得一个连接,然后您应该做正确的事情并在 finally 块中返回它。我应该提一下,C# 的主要架构师曾经说过,你应该使用finally
块 100 倍于catch
块。真实的话从来没有说过...
哪个连接池
您在一个 servlet 中,因此您应该使用容器提供的连接池。除了获取连接的方式之外,您的 JNDI 代码将完全正常。据我所知,所有 servlet 容器都有连接池。
对上述答案的一些评论建议使用特定的连接池 API。您的 WAR 应该是可移植的并且“只需部署”。我认为这基本上是错误的。如果您使用容器提供的连接池,您的应用程序将可部署在跨多台机器的容器以及 Java EE 规范提供的所有花哨的东西上。是的,必须编写容器特定的部署描述符,但这是 EE 方式,mon。
一位评论者提到某些容器提供的连接池不适用于 JDBC 驱动程序(他/她提到了 Websphere)。这听起来完全牵强和荒谬,所以它可能是真的。当这样的事情发生时,把你“应该做”的一切都扔进垃圾箱,尽你所能。这就是我们得到报酬的原因,有时:)
我实际上不同意使用 Commons DBCP。您应该真正遵从容器来为您管理连接池。
由于您使用的是 Java Servlet,这意味着在 Servlet 容器中运行,并且我熟悉的所有主要 Servlet 容器都提供连接池管理(Java EE 规范甚至可能需要它)。如果您的容器碰巧使用 DBCP(就像 Tomcat 一样),那很好,否则,只需使用您的容器提供的任何内容。
我会使用Commons DBCP。这是一个为您管理连接池的 Apache 项目。
您只需在 doGet 或 doPost 中运行您的查询,然后在 finally 块中关闭连接。(con.close() 只是将它返回到池中,实际上并没有关闭它)。
DBCP 可以管理连接超时并从中恢复。如果您的数据库在任何时间段内出现故障,您当前的处理方式将不得不重新启动您的应用程序。
你在汇集你的连接吗?如果没有,您可能应该减少打开和关闭连接的开销。
一旦这不碍事,只要它需要,只要保持连接打开,就像约翰建议的那样。
最好的方法,我目前正在通过谷歌寻找更好的参考表,是使用池。
在初始化时,您创建一个包含 X 个数据库的 SQL 连接对象的池。将这些对象存储在某种列表中,例如 ArrayList。这些对象中的每一个都有一个用于“isLeased”的私有布尔值,一个表示上次使用时间的 long 和一个 Connection。每当您需要连接时,您都可以从池中请求一个。池要么为您提供第一个可用连接,检查 isLeased 变量,要么创建一个新连接并将其添加到池中。确保设置时间戳。完成连接后,只需将其返回到池中,这会将 isLeased 设置为 false。
为了避免经常有连接占用数据库,您可以创建一个工作线程,它偶尔会通过池并查看上次使用连接的时间。如果它已经足够长,它可以关闭该连接并将其从池中删除。
使用它的好处是,您无需等待 Connection 对象连接到数据库的时间很长。您已经建立的连接可以尽可能多地重复使用。您将能够根据您认为应用程序的繁忙程度来设置连接数。
您应该只在需要时保持数据库连接打开,这取决于您正在做的事情可能在您的doGet/doPost
方法范围内。
与数据源关联的连接池应该可以解决问题。doget
您可以在 servlet 请求方法( /dopost
等)中从 dataSource 获取连接。
dbcp、c3p0 和许多其他连接池可以满足您的需求。在合并连接时,您可能希望合并 Statements 和 PreparedStatements;此外,如果您是如您所指示的 READ HEAVY 环境,您可能希望使用 ehcache 之类的东西缓存一些结果。
BR,
~A
汇集它。
此外,如果您正在使用原始 JDBC,您可以查看一些可以帮助您管理 Connection、PreparedStatement 等的东西。除非您有非常严格的“轻量级”要求,例如,使用 Spring 的 JDBC 支持将简化您的代码很多-并且您不会被迫使用 Spring 的任何其他部分。
在此处查看一些示例:
http://static.springframework.org/spring/docs/2.5.x/reference/jdbc.html
通常你会发现每个请求打开连接更容易管理。这意味着在您的 servlet 的 doPost() 或 doGet() 方法中。
在 init() 中打开它使其可用于所有请求,当您有并发请求时会发生什么?