9. 利用数据源
本文旨在为在 Struts 应用程序中利用数据库奠定基础。
9.1. Struts 应用程序 /listarticles
我们希望显示记录商店所售商品特征的表中的内容。
![]() |
|
表的内容如下:

listarticles 应用程序将在网页中显示相同的内容(效果稍差):

9.2. Struts 应用程序 /listarticles 的配置
该应用程序的 web.xml 文件为标准配置:
web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<taglib>
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
</web-app>
文件 struts-config.xml 引入了一个新的 <data-sources> 部分,用于声明和配置数据源:
struts-config.xml
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<data-sources>
<data-source key="dbarticles">
<set-property property="driverClass" value="com.mysql.jdbc.Driver"></set-property>
<set-property property="url" value="jdbc:mysql://localhost/dbarticles"></set-property>
<set-property property="user" value="admarticles"></set-property>
<set-property property="password" value="mdparticles"></set-property>
<set-property property="minCount" value="2"></set-property>
<set-property property="maxCount" value="5"></set-property>
</data-source>
</data-sources>
<action-mappings>
<action path="/liste" type="istia.st.struts.articles.ListeArticlesAction">
<forward name="afficherListeArticles" path="/vues/listarticles.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
</action-mappings>
<message-resources parameter="istia.st.struts.articles.ApplicationResources"
null="false" />
</struts-config>
9.2.1. 数据源
<data-sources> 部分用于声明应用程序的所有数据源,每个数据源由一个 <data-source> 部分描述。该标签支持多个属性:
用于在存在多个数据源时标识特定数据源。 | |
用于访问数据库时需实例化的类名称。该类通常位于 SGBD 发布商提供的类库(.jar)中。该类库包含用于访问数据库的 JDBC 驱动程序。 属性 driverClass 指定了该驱动程序。用于访问 SGBD 的类所包含的 .jar 文件将放置在应用程序的 WEB-INF/lib 文件夹中。 | |
连接到特定数据库的连接字符串。实际上,驱动程序 JDBC 允许访问由 SGBD 管理的所有数据库。通过 url 属性,我们可以指定要使用的数据库。 | |
数据库访问受保护。SGBD 管理通过用户名和密码进行身份验证的用户。这些信息在标签的 user 和 password 属性中指定。 | |
Struts 将管理与 SGBD 的连接池。建立与 SGBD 的连接是一项耗费大量时间和内存资源的操作。 与其针对应用程序收到的每个请求都单独建立并关闭与 SGBD 的连接,该应用程序会管理一个包含 n 个连接的连接池,其中 n 属于 [minCount, maxCount] 范围。如果在处理请求时,应用程序需要一个连接: - 它将尝试从连接池中获取一个连接。如果找到一个空闲的连接,则直接使用。 - 若连接池中无空闲连接,且池内连接数少于 maxCount,则会建立一个新连接并将其放入连接池。当前请求可使用该连接。 - 如果没有空闲连接且无法建立新连接,则该请求会被挂起。 当当前请求关闭连接时,该连接并非真正关闭,而是被归还给连接池。 |
这里,我们有以下属性:
dbarticles - 这是数据源在应用程序上下文中将使用的名称 | |
com.mysql.jdbc.Driver。我们使用的是 MySQL 数据库。 | |
jdbc:mysql://localhost/dbarticles。该数据库名为 dbarticles,位于本地机器上。 | |
admarticles、mdparticles。该用户已被定义为拥有 dbarticles 数据库的所有权限。 | |
2, 5。连接池中至少保留 2 个连接,最多保留 5 个。 |
9.2.2. 操作
Struts 配置文件中声明的操作如下:
<action-mappings>
<action path="/liste" type="istia.st.struts.articles.ListeArticlesAction">
<forward name="afficherListeArticles" path="/vues/listarticles.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
</action-mappings>
唯一的操作名为 /liste,并关联到类 istia.st.struts.articles.ListeArticlesAction。该操作最终会显示:
- 文章页面(/vues/listarticles.jsp)
- 错误页面(/vues/erreurs.jsp)
9.2.3. 消息文件
消息文件 ApplicationResources.properties 将放置在文件夹 WEB-INF/classes/istia/st/struts/articles 中。其内容如下:
# 错误
errors.header=<ul>
errors.footer=</ul>
erreur.dbarticles=<li>Erreur d'accès à la base des articles ({0})</li>
可能仅发生一种错误,即数据库访问错误。实际上可能存在多种类型的错误,但它们都被归纳在同一个错误消息中。不过,错误的确切原因将在参数 {0} 中明确说明。
9.3. 视图
9.3.1. 视图 erreurs.jsp
该视图应显示错误列表。我们此前已多次遇到过它。
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<head>
<title>Liste d'articles - erreurs</title>
</head>
<body>
<h2>Les erreurs suivantes se sont produites</h2>
<html:errors/>
</body>
</html>
该视图仅通过 <html:errors/> 标签显示错误列表。
9.3.2. 视图 listarticles.jsp
该视图应显示数据库中 ARTICLES 表的内容。其代码如下:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%
// listarticles:查询中的 ArrayList
// listArticles(i):包含 5 个元素的数组 (String[5])
%>
<html>
<head>
<title>DataSource Struts</title>
</head>
<body>
<h3>Liste des articles</h3>
<hr>
<table border="1">
<logic:iterate id="ligne" name="listArticles">
<tr>
<logic:iterate id="colonne" name="ligne">
<td><bean:write name="colonne"/></td>
</logic:iterate>
</tr>
</logic:iterate>
</table>
</body>
</html>
/liste 操作将把表 ARTICLES 的内容放入一个名为 ArrayList 的对象中,该对象在查询中以 listArticles 的名称存在。 对象 listArticles 的每个元素都是一个包含 5 个字符串的数组,这些字符串分别代表与表中某条记录相关的 5 项信息(代码、名称、价格、stockActuel、stockMinimum)。 该视图需将对象 listArticles 的内容显示在表格 HTML 中。为此,它使用了 <logic:iterate> 标签:
<logic:iterate id="ligne" name="listArticles">
<tr>
<logic:iterate id="colonne" name="ligne">
<td><bean:write name="colonne"/></td>
</logic:iterate>
</tr>
</logic:iterate>
这里有两个迭代过程。第一个循环遍历对象 ArrayList listArticles 的元素。 listArticles 的当前元素在此处称为行。行元素表示一个 String[5] 对象,我们通过第二个迭代对其进行遍历。 该第二次迭代的元素被称为“列”。它代表字符串数组中的当前元素,因此本身是一个字符串(代码、名称、价格、stockActuel、stockMinimum)。其值通过 <bean:write> 标签显示。
该视图使用了 struts-logic 和 struts-bean 库中的标签。因此需要声明使用这些库:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
9.4. /liste 操作
/liste 操作旨在将一个 ArrayList 对象(该对象代表 ARTICLES 表的内容)放入请求中,以便视图进行利用。其代码如下:
package istia.st.struts.articles;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
public class ListeArticlesAction extends Action {
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// 读取连接中“articles”表的内容
// 在上下文初始化时建立
// 获取数据源 dbarticles
DataSource dataSource = this.getDataSource(request, "dbarticles");
if (dataSource == null) {
// 无法创建数据源
ActionErrors erreurs = new ActionErrors();
erreurs.add( "dbarticles",new ActionError("erreur.dbarticles","La source de données n'a pu être créée"));
this.saveErrors(request, erreurs);
return mapping.findForward("afficherErreurs");
}
// 此处数据源已存在——正在使用它 Connection connection = null ;
Statement st = null;
ResultSet rs = null;
String requête = null;
ArrayList alArticles = new ArrayList();
// 处理错误
try {
// 获取连接
connexion = dataSource.getConnection();
// 准备查询 SQL
requête =
"select code, nom, prix, stockActuel, stockMinimum from articles order by nom";
// 执行查询
st = connexion.createStatement();
rs = st.executeQuery(requête);
// 处理结果
while (rs.next()) {
// 保存当前行
alArticles.add(
new String[] {
rs.getString("code"),
rs.getString("nom"),
rs.getString("prix"),
rs.getString("stockactuel"),
rs.getString("stockMinimum")});
// 下一行
} //while
// 释放资源
rs.close();
st.close();
connexion.close();
} catch (Exception ex) {
// 发生错误
ActionErrors erreurs = new ActionErrors();
erreurs.add("dbarticles",new ActionError("erreur.dbarticles", ex.getMessage()));
this.saveErrors(request, erreurs);
return mapping.findForward("afficherErreurs");
}
// 成功
request.setAttribute("listArticles", alArticles);
return mapping.findForward("afficherListeArticles");
} //执行
} //类
读者应该能够理解上述代码的本质。 我们仅重点探讨唯一的新内容,即使用由 Struts 框架提供的 DataSource 对象。该 DataSource 对象代表了由配置文件中 <data-source key="dbarticles"> 部分配置的连接池。 DataSource 对象在创建后已被放入应用程序上下文中,以便该上下文中的所有对象都能访问它。因此,可通过以下方式获取该对象:
此处我们请求由键“dbarticles”标识的数据源。如果检索到指针 null,则表示该数据源在应用程序初始化时未能创建。 在这种情况下,我们将错误记录在对象 ActionErrors 中,并将控制权移交给配置文件中由键“afficherErreurs”标识的错误页面:
<action path="/liste" type="istia.st.struts.articles.ListeArticlesAction">
<forward name="afficherListeArticles" path="/vues/listarticles.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
</action-mappings>
获取数据源后,我们即可访问连接池。通过以下代码可建立数据库连接:
在此,连接池将返回一个回收的连接,或者在仍有创建余地时创建一个新连接。
9.5. 部署
应用程序的上下文在 Tomcat 的配置文件 server.xml 中定义:
<Context path="/listarticles" reloadable="true" docBase="E:\data\serge\web\struts\articles\liste" />
应用程序的目录结构如下:
![]() ![]() | ||
![]() ![]() | ||
![]() | ||
请注意 WEB-INF/lib 目录下的 mysql-connector-java-3.0.10-stable-bin.jar 库。该库包含此处使用的 MySQL 基础库中的 JDBC 驱动程序。
9.6. 测试
我们启动 Tomcat 并请求 URL http://localhost:8080/listarticles/liste.do:

9.7. 第二个数据源
现在我们使用位于 Postgres 数据库中的第二个数据源。同样,数据位于 ARTICLES 表中,列结构与之前相同。其内容如下:

该新数据源已在 Struts 配置文件中声明:
<data-sources>
<data-source key="dbarticles" >
<set-property property="driverClass" value="com.mysql.jdbc.Driver"></set-property>
<set-property property="url" value="jdbc:mysql://localhost/dbarticles"></set-property>
<set-property property="user" value="admarticles"></set-property>
<set-property property="password" value="mdparticles"></set-property>
<set-property property="minCount" value="2"></set-property>
<set-property property="maxCount" value="5"></set-property>
</data-source>
<data-source key="pgdbarticles" type="org.apache.commons.dbcp.BasicDataSource">
<set-property property="driverClassName" value="org.postgresql.Driver" />
<set-property property="url" value="jdbc:postgresql://localhost/dbarticles" />
<set-property property="username" value="serge" />
<set-property property="password" value="serge" />
<set-property property="maxActive" value="10" />
<set-property property="maxWait" value="5000" />
<set-property property="defaultAutoCommit" value="false" />
<set-property property="defaultReadOnly" value="false" />
</data-source>
</data-sources>
新数据源的标识符(key)为 pgdbarticles。这一新功能源自实现 javax.sql.DataSource 接口的类。默认类是定义在 struts.jar 库中的 org.apache.struts.util.GenericDataSource。 该类将在未来的 Struts 版本中被弃用,建议在 1.1 版本中使用位于 commons-dbcp-1.1.jar 库中的 org.apache.commons.dbcp.BasicDataSource 类。该类并不一定随 Struts 软件包一起提供。 可通过网址 http://jakarta.apache.org/commons/index.html 获取,具体位于 http://jakarta.apache.org/site/binindex.cgi。 需要下载名为 Commons DBCP 的产品。 Windows用户可下载该.zip文件。该文件包含库的源代码以及相应的.jar文件。需将.jar文件从.zip压缩包中解压,并放置在应用程序的WEB-INF/lib文件夹内。同时,还需将所使用的SGBD驱动程序放置在同一文件夹中:

要使用此源代码,我们需要修改 /liste 操作中的一个指令:
此次,我们将数据源名称设置为 pgdbarticles,即 c.a.d。该数据源为 Postgres 数据库。
接下来只需编译所有内容,启动 Tomcat,并调用 URL http://localhost:8080/listarticles/liste.do:

9.8. 结论
我们演示了如何使用连接池访问数据库。需要注意的是,我们并未遵循MVC架构。 实际上,/liste 动作本身会直接执行 SQL 查询来访问数据。更理想的做法是让它调用一个中间业务类,由该类隐藏从 SGBD 中检索数据这一过程。 这样,连接池很可能会由业务类来创建,也就无需在 Struts 配置文件中声明。这是一个线索:配置文件中存在 <data-sources> 部分,可能意味着我们的应用程序未遵循 MVC 架构。





