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> 部分描述。该标签支持以下几个属性:
在存在多个数据源时用于标识特定数据源。 | |
用于访问数据库的实例化类名称。该类通常位于数据库管理系统(DBMS)供应商提供的类库(.jar)中。该库包含用于数据库访问的 JDBC 驱动程序。driverClass 属性指定此驱动程序。包含 DBMS 访问类的 .jar 文件将放置在应用程序的 WEB-INF/lib 文件夹中。 | |
连接到特定数据库的连接字符串。JDBC 驱动程序允许访问由数据库管理系统 (DBMS) 管理的所有数据库。通过 url 属性,我们可以指定要使用的数据库。 | |
数据库访问受保护。DBMS 管理通过用户名和密码进行身份验证的用户。这些信息在标签的 user 和 password 属性中指定。 | |
Struts 管理着一个连接到 DBMS 的连接池。建立与 DBMS 的连接是一项在时间和内存方面都消耗大量资源的操作。应用程序不会针对每个请求都打开和关闭与 DBMS 的连接,而是管理一个包含 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>
唯一的操作名为 /list,并与类 istia.st.struts.articles.ListeArticlesAction 相关联。该操作最终会显示:
- 文章页面(/vues/listarticles.jsp)
- 错误页面(/views/errors.jsp)
9.2.3. 消息文件
ApplicationResources.properties 消息文件将放置在 WEB-INF/classes/istia/st/struts/articles 文件夹中。其内容如下:
# erreurs
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. errors.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 dans la requête
// listArticles(i) : tableau (String[5]) de 5 éléments
%>
<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>
/list 操作将把 ARTICLES 表中的内容放入请求中的一个名为 listArticles 的 ArrayList 对象中。listArticles 对象的每个元素都是一个包含 5 个字符串的数组,代表与表中某项相关的 5 项信息(代码、名称、价格、当前库存、最低库存)。 视图必须将 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 的当前元素在此处称为 row。 该 row 元素表示一个 String[5] 对象,该对象通过第二个迭代进行遍历。此第二个迭代的元素称为 column。它表示字符串数组的当前元素,因此是一个字符串(code、name、price、currentStock、minimumStock)。其值通过 <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. /list 操作
/list 操作的目的是在请求中包含一个代表 ARTICLES 表内容的 ArrayList 对象,以便视图可以使用它。其代码如下:
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 {
// reads the contents of a connection's items table
// performed at context init
// retrieve the dbarticles data source
DataSource dataSource = this.getDataSource(request, "dbarticles");
if (dataSource == null) {
// the data source could not be created
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");
}
// here the data source exists - we use it Conne ction connexion = null;
Statement st = null;
ResultSet rs = null;
String requête = null;
ArrayList alArticles = new ArrayList();
// error handling
try {
// get a connection
connexion = dataSource.getConnection();
// prepare query SQL
requête =
"select code, nom, prix, stockActuel, stockMinimum from articles order by nom";
// execute it
st = connexion.createStatement();
rs = st.executeQuery(requête);
// exploit results
while (rs.next()) {
// save current line
alArticles.add(
new String[] {
rs.getString("code"),
rs.getString("nom"),
rs.getString("prix"),
rs.getString("stockactuel"),
rs.getString("stockMinimum")});
// next line
} //while
// free up resources
rs.close();
st.close();
connexion.close();
} catch (Exception ex) {
// errors have occurred
ActionErrors erreurs = new ActionErrors();
erreurs.add("dbarticles",new ActionError("erreur.dbarticles", ex.getMessage()));
this.saveErrors(request, erreurs);
return mapping.findForward("afficherErreurs");
}
// it's good
request.setAttribute("listArticles", alArticles);
return mapping.findForward("afficherListeArticles");
} //execute
} //class
读者应该能够理解上述代码的本质。我们将仅关注其中一个新元素:Struts 框架提供的 DataSource 对象的使用。该 DataSource 对象代表了配置文件中 <data-source key="dbarticles"> 部分所配置的连接池。创建后,DataSource 对象被放入应用程序上下文中,以便该上下文中的所有对象都能访问它。因此,其获取方式如下:
// retrieve the dbarticles data source
DataSource dataSource = this.getDataSource(request, "dbarticles");
在此,我们请求由键“dbarticles”标识的数据源。如果检索到空指针,则表示在应用程序初始化期间无法创建该数据源。在这种情况下,我们会将错误记录到 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>
新数据源的标识符(键)为 pgdbarticles。该新功能源自实现 javax.sql.DataSource 接口的类。 默认类是 org.apache.struts.util.GenericDataSource,定义在 struts.jar 库中。该类将在未来的 Struts 版本中被弃用,对于 1.1 版本,建议使用 commons-dbcp-1.1.jar 库中的 org.apache.commons.dbcp.BasicDataSource 类。 该库并不一定包含在 Struts 软件包中。您可以在 URL http://jakarta.apache.org/commons/index.html 上找到它,具体位于 http://jakarta.apache.org/site/binindex.cgi。您必须下载名为 Commons DBCP 的产品。对于 Windows 系统,您可以下载 .zip 文件。该文件包含库的源代码以及相应的 .jar 文件。 您必须将该文件从 .zip 压缩包中解压,并将其放置在应用程序的 WEB-INF/lib 文件夹中。同时,您还必须将所用数据库管理系统(DBMS)的驱动程序放置在同一文件夹中:

要使用此数据源,我们需要修改 /list 操作中的代码:
这次,我们请求名为 pgdbarticles 的数据源,即 Postgres 数据源。
剩下的就是编译所有内容,启动 Tomcat,并访问 URL http://localhost:8080/listarticles/liste.do:

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





