用连接池提高Servlet访问数据库的效率(2)

网络编程 2025-04-24 23:24www.168986.cn编程入门

三、DBConnectionPool:连接池的管理者

在数据库操作的背后,隐藏着一种强大的机制——连接池。这种机制由DBConnectionPool类实现,位于代码的第209至345行。该类代表了一个指向特定数据库的连接池,这个数据库通过JDBC URL进行标识。JDBC URL结构独特,由三部分组成:协议标识(总是为jdbc)、驱动程序标识(如odbc、idb、oracle等),以及数据库标识(格式依赖于驱动程序)。例如,“jdbc:odbc:demo”就是一个指向demo数据库的JDBC URL,访问该数据库使用的是JDBC-ODBC驱动程序。

每个连接池都拥有一个名字,供客户程序使用,同时还有可选的用户账号、密码以及最大连接数限制。想象一下,如果你的Web应用程序支持多种数据库操作,有些操作对所有用户开放,而有些则需要特定权限,你就可以为这两类操作创建不同的连接池,共享同一个JDBC URL但使用不同的账号和密码。

DBConnectionPool类的构造函数需要上述所有数据作为参数。这些数据被妥善保存为实例变量(如第252至283行、285至305行所示)。客户程序可以通过DBConnectionPool类的两个方法获取可用的连接。如果连接池中有空闲连接,这两个方法都会直接返回;否则,它们会创建新的连接并返回。

其中,第一个getConnection()方法在返回连接前会验证其有效性。如果连接已关闭或出现异常,它会递归调用自己以尝试获取其他可用连接。创建新连接的任务由newConnection()方法实现(如第325至345行所示),这个过程会受到数据库账号和密码的影响。

DBConnectionPool还实现了一个机制来管理空闲的连接。当不再需要一个连接时,它会被放回连接池(通过freeConnection()方法,如第240至250行所示)。如果客户程序等待时间过长仍无法获取连接,getConnection()方法会尝试创建新的连接。如果创建失败,则返回空值。

四、DBConnectionManager:单一实例的管理者

在数据库连接的世界中,有一个类独树一帜——DBConnectionManager。这个类只创建一个实例,其他对象可以通过其静态方法获取这个唯一实例的引用(如第31至36行所示)。由于其构造函数的私有性质(为了避免其他对象创建该类的实例),这个类确保了其实例的唯一性。

DBConnectionManager类是一个关键的管理工具,专门负责处理数据库连接。为了获得这个类的唯一实例,客户程序会调用getInstance()方法。这个方法在首次被调用时创建类的唯一实例,并将其实例引用保存在静态变量instance中。每次调用getInstance()都会增加一个客户程序计数,这个计数代表着引用DBConnectionManager唯一实例的客户程序总数。这个计数对于控制连接池的关闭操作至关重要。

类的初始化工作由init()方法完成,该方法位于私有代码块中的146至168行。其中,getResourceAsStream()方法用于打开外部文件,类装载器则负责定位这些文件。标准的类装载器搜索操作始于类文件所在路径,同时也会搜索CLASSPATH中声明的路径。

db.properties是一个包含连接池相关键-值对的属性文件。其中一些公用属性包括drivers(指定JDBC驱动程序类列表)、logfile(日志文件的绝对路径)等。每个连接池都有自己的属性前缀,例如.url表示数据库的JDBC URL,.maxconn表示允许建立的最大连接数等。其中url属性是必需的,其他属性则是可选的。

在Windows平台上,db.properties文件的示例如下:

```makefile

drivers=sun.jdbc.odbc.JdbcOdbcDriver jdbc.idbDriver

logfile=D:\\user\\src\\java\\DBConnectionManager\\log.txt

idb.url=jdbc:idb:c:\\local\\javawebserver1.1\\db\\db.prpidb

idb.maxconn=2aess.url=jdbc:odbc:demoaess.user=demoaess.password=demopw

```

在init()方法中,除了创建属性对象并读取db.properties文件,还会检查logfile属性并设置日志文件路径。如果没有指定日志文件路径,则默认为当前目录下的DBConnectionManager.log文件。如果日志文件无法使用,则会将日志记录输出到System.err。init()还会调用loadDrivers()方法装载并注册所有在drivers属性中指定的JDBC驱动程序。这个过程由170至192行之间的代码实现。

创建连接池对象的任务由createPools()方法完成。该方法会创建所有属性名字的枚举对象,并搜索以“.url”结尾的属性。对于每个符合条件的属性,都会提取其连接池名字,然后创建连接池对象并将其保存在实例变量pools中。pools是一个散列表,实现了连接池名字到连接池对象的映射。

为了方便客户程序从指定连接池获取连接或返回连接,DBConnectionManager提供了getConnection()和freeConnection()方法。这些方法都要求在参数中指定连接池名字,具体的连接获取或返回操作则通过对应的连接池对象完成。它们的实现分别在特定代码块中详细展示。

当我们的客户程序发出调用release()的指令时,背后隐藏的引用计数机制开始发挥作用。当这个计数递减至零,意味着是时候释放所有数据库连接了。这时,各个连接池的release()方法被唤醒,它们协同工作,默默关闭所有活跃的连接。在这背后,有一个英勇的管理类release()方法,它的任务是撤销所有JDBC驱动程序的注册,确保系统资源的妥善管理。

现在,让我们通过一个Servlet使用连接池的生动示例来深入理解这一过程。想象一下,您是一位厨师,而Servlet API就像是您的烹饪手册。这本手册定义了Servlet的生命周期,就像菜谱中的制作步骤。

1. 创建并初始化Servlet,这就像是准备厨房和食材的过程(init()方法)。在这里,我们通过DBConnectionManager获取数据库连接的实例,并将其保存起来。

2. 响应客户程序的服务请求,就像开始烹饪的过程(service()方法)。我们调用getConnection()来获取数据库连接,执行各种数据库操作。完成任务后,我们通过freeConnection()将连接温柔地送回连接池,就像餐具被放回抽屉一样。

3. Servlet终止运行,释放所有资源,就像完成烹饪并清理厨房的过程(destroy()方法)。在这里,我们调用release()来关闭所有连接,确保没有资源被遗漏。然后,我们告别厨房,留给食客一个干净整洁的环境。

示例程序清单如下:

```java

import java.io.;

import java.sql.;

import javax.servlet.;

import javax.servlet.http.;

public class TestServlet extends HttpServlet {

private DBConnectionManager connMgr; // 我们的连接管理大厨

public void init(ServletConfig conf) throws ServletException { // 初始化和准备食材的步骤

superit(conf);

connMgr = DBConnectionManager.getInstance(); // 获取连接管理实例

}

public void service(HttpServletRequest req, HttpServletResponse res) throws IOException { // 开始烹饪的过程

res.setContentType("text/html"); // 设置响应类型

PrintWriter out = res.getWriter(); // 准备输出工具

Connection con = connMgr.getConnection("idb"); // 从连接池获取连接

if (con == null) { // 如果无法获取连接,告知食客无法提供服务并退出厨房

out.println("不能获取数据库连接.");

return;

}

// 执行数据库操作并处理结果的过程省略...(这里像是忙碌的厨师在炒菜)

connMgr.freeConnection("idb", con); // 完成烹饪后将餐具放回抽屉(连接池)中备用。

上一篇:Angular.js实现注册系统的实例详解 下一篇:没有了

Copyright © 2016-2025 www.168986.cn 狼蚁网络 版权所有 Power by