下面我们将介绍 JDBC API,这是 J2EE 下的行业标准接口集,它允许 Java 程序使用统一的 API 调用集来访问关系数据库。JDBC 允许您开发应用程序一次,然后针对 JDBC 相容的任何数据库部署它们――DB2、Oracle、SQL Server、Sybase、mySQL、Informix 以及其他数据库。
JDBC API 允许任何 Java 组件(包括 Java Servlets 和 JSP 页面)无缝地访问关系数据库中的数据。 JDBC 由两部分组成:用于访问数据库的 API,以及数据库厂商为各自的数据库提供的可插入 JDBC 驱动程序。JDBC API 最便利的特性之一在于,您不必使用任何厂商专用的类。您可以在运行时使用一个字符串常量来指定适当的驱动程序,以后就可以使用相同的编程接口,而不管所使用的是哪种数据库。这样允许您切换数据库,而不必更改代码(只要您提供在用于运行时指定驱动程序的字符串常量)。
一个 JDBC 例子
典型的 JDBC 应用程序必须完成以下步骤才能与数据库交互:
识别驱动程序。(每个应用程序只需执行这个步骤一次。)
识别数据库并与之建立连接(在需要时提供身份验证信息)。
执行查询和/或更新。
处理结果。
断开与数据库的连接。
清单 29 说明了上述步骤。
清单 29. 一个 JDBC 例子
2
3 Class.forName("COM.ibm.db2.jdbc.app.DB2Driver");
4
5 // Step 2. Identify the database and connect to it
6
7 Connection conn = DriverManager.getConnection("jdbc:db2:SAMPLE","userid","password");
8
9 // Step 3. Execute query
10
11 Statement stmt = conn.createStatement();
12
13 stmt.execute("SELECT * FROM USERID.EMPLOYEE");
14
15 // Step 4. Get the results
16
17 ResultSet rs = stmt.getResultSet();
18
19 while (rs.next()) {
20
21 String firstCol = rs.getString(1);
22
23 int secondCol = rs.getInt(2);
24
25 ...
26
27 }
28
29 // Step 5. Disconnect from the database
30
31 conn.close();
32
33
清单 29 中的 Step 1 动态地加载适当的驱动程序。这个例子使用的是 DB2;其他 JDBC 驱动程序具有不同的完全限定名称。Step 2 提供特定于驱动程序的 String 来指出要连接到的数据库。每种 JDBC 驱动程序都有自己的 String 格式。DB2 使用的格式是 "jdbc:db2:DBNAME" ,因此在此例中要连接到的是一个名为 SAMPLE 的数据库。例子中还提供了身份验证信息,以便数据库服务器能够对连接请求进行身份验证。在 Step 4 中,注意例子中如何循环迭代结果集,直至 next() 方法返回 false 。您还必须知道每个列的类型,并对每个列和每种类型调用适当的 getXXXX(n) 方法。还要注意您传递给 getXXXX(n) 方法的整数是列编号。
这种方法相当类似于使用 ADO.NET DataReader,因为程序员要负责打开和关闭数据库连接。在 ADO.NET 中,您最初对数据库使用 ADO.NET 提供程序所提供的类,因此不能像使用 JDBC 那样容易地在运行时切换数据库。
连接池
ADO.NET 还提供一组称为 DataAdapter 的类,它们允许在断开连接的模式下操作,从而使您不必显式地管理数据库连接。使用 DataAdapter 的典型应用流程如下:
使用提供程序的 DataAdapter 类来识别数据库(在需要时提供身份验证信息)和查询。
执行查询。
处理结果。
使用 JDBC,您可以从显式地管理连接的工作中解放出来。可以配置 J2EE 应用服务器来维护可通过 JDBC 访问的数据库的数据库连接池。然后应用程序就可以从应用服务器管理的池中请求连接,并在完成使用时将它们返回池中。大多数应用服务器都具有管理连接池的相当高级的选项,包括“唤醒”连接的能力,即从没有在给定时间内将连接返回池中的错误应用程序那里取回连接。
使用连接池的应用程序的典型流程如下:
查找应用服务器资源库中的 DataSource (用于访问特定的连接池)。(每个应用服务器都有一个资源库,其中包含所有已配置的连接和其他资源,比如消息队列和 SMTP 提供程序。您可以使用名为 JNDI 的标准 J2EE API 来访问资源库。)(您只需执行这个步骤一次。)
从池中获得连接(在需要时提供身份验证信息)。
执行查询和/或更新。
处理结果。
将连接返回池中。
清单 30 显示了使用连接池的一个例子。
清单 30. 一个连接池例子
2
3 InitialContext ic = new InitialContext();
4
5 DataSource ds = (DataSource) ic.lookup("jdbc/MyDS");
6
7 // Step 2. Get a connection from the pool
8
9 Connection conn = ds.getConnection();
10
11 // Step 3. Execute query
12
13 Statement stmt = conn.createStatement();
14
15 stmt.execute("SELECT * FROM USERID.EMPLOYEE");
16
17 // Step 4. Get the results
18
19 ResultSet rs = stmt.getResultSet();
20
21 while (rs.next()) {
22
23 String firstCol = rs.getString(1);
24
25 int secondCol = rs.getInt(2);
26
27 ...
28
29 }
30
31 // Step 5. Return connection to the pool
32
33 conn.close();
34
35
Step 1 假设应用服务器已经使用名为 "jdbc/MyDS" 的 DataSource 来配置好了。这样就封装了一个特定的 JDBC 驱动程序和数据库。您还可以向 DataSource 定义添加身份验证信息,这样应用程序就不必提供该信息。可以使用 InitialContext (它是 JNDI API 的一部分)来查找应用服务器控制的各种资源。Step 3 至 Step 5 与前一个例子相同,不过要注意 Step 5 中对 close() 方法的调用仅把连接返回池中,而不是关闭该数据库连接。
J2EE 应用程序状态管理
在编写 J2EE Web 应用程序时,可以任意选择一组丰富的类和接口来管理应用程序的状态。我们将介绍 J2EE HttpSession 类(它类似于 ASP.NET HttpSessionState 类),以及允许您管理应用程序状态的其他类和接口。我们还会讨论如何同时在 Java Servlets 和 JSP 页面中使用这些类和接口。不过,我们将首先介绍 范围的概念,它是理解 J2EE 中的应用程序状态管理的关键。
范围
从程序员的角度看,状态管理涉及临时地存储数据和在需要时检索它们。在 J2EE 中,您可以选择多个“存储位置”,每个位置具有它自己规则,控制着所存储的任何数据在多长时间内可用。持续时间范围从处理特定页面时临时存储一些数据到在应用程序运行生命期内存储数据不等。J2EE 中的“存储位置”选择称为特定存储请求或检索的 范围。该范围决定了您将把数据附加到哪些 J2EE 对象,以及那些数据将在多长时间内可用。可用的范围宝库o:
会话:这类似于 ASP.NET 中的会话范围。只要会话还是活动的,您就可以在该用户会话范围内放置任何对象并检索它。J2EE 应用程序使用 HttpSession 接口(类似于 ASP.NET 中的 HttpSessionState )。对象通过一个 String 作为标签来添加到会话中,并使用相同的标签来检索它。
请求:在 J2EE 中, HttpServletRequest 对象允许您向它附加数据,这非常类似 HttpSession 接口。当多个资源处理单个请求时,这是很有用的。例如,某个 Java Servlet 可能是一个 HTML 表单提交的目标,然后它将请求转发给一个 JSP 页面以完成它。在这个例子中,该 sevlet 能够向 HttpRequest 对象附加数据,并且 JSP 页面能够访问它。注意在这种场景中,该 servlet 和 JSP 页面都使用相同的 HttpRequest 对象。向相同请求内的不同资源转发的能力类似于 ASP.NET 中的 Server.Transfer 。
应用程序:所有 J2EE Web 应用程序在部署之前都打包到一个具有 .war 扩展名的文件中。该文件的格式是标准的,因此您可以把同一个应用程序部署到不同的应用服务器。单个 .war 文件中的所有 J2EE 组件都被认为是同一个应用程序的组成部分,并且共享共同的应用程序上下文。这是通过 ServletContext 接口向开发人员公开的,这个接口(就像 HttpSession 和 HttpRequest 接口一样)允许您附加和删除任何 Java 对象。只要应用程序还在运行,添加到 ServletContext 的项就可用,并且会在单独会话的创建和销毁过程中保留下来。
页面:页面上下文在处理单个页面的过程中可用。例如,JSP 页面顶部的 Java scriptlet 能够在 PageContext 中放置对象,然后相同页面中的其他 scriptlet 就可以访问它。
管理应用程序状态
现在您已经对范围有了更好的了解,下面我们可以深入地讨论管理 J2EE 应用程序中的状态的机制。非常好的实践坚持认为,对于任何临时的状态存储,您都应该确定需要存储该数据多长时间,然后使用满足需要的、具有最短生存时间的范围。例如,假设您需要某个 JSP 页面中的数据,该 JSP 页面是从某个 servlet 转发的请求的目标。虽然会话状态和应用程序状态也满足您的需要,但是在这两种情况下,数据都会在使用完之后悬置在那里。这样不必要地增加了当前应用程序的资源需求。对于这个例子,请求范围能够满足需要,却不会在您不需要之后还将数据悬置在那里。
管理 JSP 页面中的状态
在 JSP 脚本环境中,所有范围对象都是用隐含变量来表示的。您可以在自己的 sciptlet 中使用这些变量,而不需要显式地声明它们。 可以使用的隐含变量包括:
session:实现 HttpSession 接口的类的一个实例
application:实现 HttpSession 接口的类的一个实例
request:实现 HttpServletRequest 接口的类的一个实例
pageContext: PageContext 类的一个实例
清单 31 显示了如何在 JSP scriptlet 中针对不同的范围添加和删除对象。
清单 31. JSP 页面中的状态管理
2 <%
3 String foo = "I am a Foo";
4 // Place object in session scope
5 session.setAttribute("Foo", foo);
6 // Retrieve from session scope
7 String sessionFoo = (String) session.getAttribute("Foo");
8 // Place object in application scope
9 application.setAttribute("Foo", foo);
10 // Retrieve from application scope
11 String applicationFoo = (String) application.getAttribute("Foo");
12 // Place object in page scope
13 pageContext.setAttribute("Foo", foo);
14 // Retrieve from page scope
15 String pageFoo = (String) application.getAttribute("Foo");
16 // Place object in request scope
17 request.setAttribute("Foo", foo);
18 // Retrieve from request scope
19 String requestFoo = (String) request.getAttribute("Foo");
20 %>
21 ....
22
清单 31 中的例子的第一行是 page 指令,它允许您定义整个页面的属性。请注意 session 属性的使用,它决定了该页面是否要使得会话可用。如果将它设置为 true 却还没有建立会话,那么新的会话就会自动为您创建。如果将它设置为 false,那么会话范围将在该 JSP 页面中不可用。(这个属性的面默认设置是 true,因此这里使用它是多余的,只是出于说明目的。)
清单 32 是使用各种可用范围来存储和检索数据的 servlet 的一个例子。
清单 32. servlet 中的状态管理
2
3 public void doGet(HttpServletRequest request, HttpServletResponse response)
4
5 throws ServletException, IOException {
6
7 performTask(request, response);
8
9 }
10
11 public void doPost(HttpServletRequest request, HttpServletResponse response)
12
13 throws ServletException, IOException {
14
15 performTask(request, response);
16
17 }
18
19 /**
20
21 * Handles all HTTP GET and POST requests
22
23 *
24
25 * @param request Object that encapsulates the request to the servlet
26
27 * @param response Object that encapsulates the response from the servlet
28
29 */
30
31 public void performTask(
32
33 javax.servlet.http.HttpServletRequest req,
34
35 javax.servlet.http.HttpServletResponse res)
36
37 throws ServletException, IOException {
38
39 try {
40
41 // This is how you create a session if is has not been created yet
42
43 // Note that this will return the existing session if you've
44
45 // already created it
46
47 HttpSession session = req.getSession();
48
49 //This is how the application context is retrieved in a servlet
50
51 ServletContext application = getServletContext();
52
53 String foo = "I am a Foo";
54
55 // Place object in session scope
56
57 session.setAttribute("Foo", foo);
58
59 // Retrieve from session scope
60
61 String sessionFoo = (String) session.getAttribute("Foo");
62
63 // Place object in application scope
64
65 application.setAttribute("Foo", foo);
66
67 // Retrieve from application scope
68
69 String applicationFoo = (String) application.getAttribute("Foo");
70
71 // Place object in request scope
72
73 request.setAttribute("Foo", foo);
74
75 // Retrieve from request scope
76
77 String requestFoo = (String) request.getAttribute("Foo");
78
79 ...
80
81 } catch (Throwable t) {
82
83 // All errors go to error page
84
85 throw new ServletException(t.getMessage());
86
87 }
88
89 }
90
91 }
92
93
JSP 页面和 sevlet 中的会话之间的区别在于,在 sevlet 中,您必须显式地创建和检索它,而在 JSP 页面中,这是自动为您完成的。注意 pageContext 仅适用于 JSP 页面,而不适用于 servlet;还要注意 servlet API 如何提供了两个重载方法来同时处理 GET 和 POST HTTP 请求。这个例子提供了一个名为 performTask() 的通用方法来同时处理这两种类型的请求,不过您可以基于触发 servlet 调用的 HTTP 类型而使用不同的处理。
编程方式的导航
在代码中从一个资源导航到另一个资源的情况在 Web 应用程序中是相当普遍的。导航与应用程序状态密切相关,因为当你在一系列资源中导航时,可能需要维护状态信息。例如,如果从 sevlet 向 JSP 页面转发请求,那么您可能希望附加某些在该 servlet 中完成的处理结果,以便这些结果能够显示在 JSP 页面中。
有三个 ASP.NET 方法支持编程方式的导航:
HttpResponse.Redirect() 导致向客户端浏览器返回一个特殊的 HTTP 返回代码(连同要重从定向的页面),然后客户端浏览器又对重定向的目标发出新的请求。如果需要在这两个请求之间共享数据,那就必须将数据存储在会话状态中。
Server.Transfer() 导致调用此方法的资源终止,同时终止对作为转移目标的资源的调用。对客户端浏览器来说,这看起来就像是单个请求。
Server.Execute() 导致调用此方法的资源挂起,同时挂起对目标资源的调用。当目标资源完成时,挂起的资源又继续处理。 对客户端浏览器来说,这看起来就像是单个请求。
J2EE 支持以下形式的编程式导航:
适用 HttpServletResponse.sendRedirect() 方法 :这会导致向客户端浏览器返回一个特殊的 HTTP 返回代码(连同要重从定向的页面),然后客户端浏览器又对重定向的目标发出新的请求。如果需要在这两个请求之间共享数据,那就必须将数据存储在会话或应用程序范围中。
在 servlet 或 JSP 页面的特殊标签中使用 RequestDispatcher.forward() 方法 :这会导致调用此方法的资源终止,同时终止对作为转发目标的资源的调用。对客户端浏览器来说,这看起来就像是单个请求。
在 JSP 页面的特殊标签中使用 RequestDispatcher.include() 方法 :这会导致调用此方法的资源挂起,同时挂起对目标资源的调用。当目标资源完成时,挂起的资源又继续处理。 对客户端浏览器来说,这看起来就像是单个请求。
清单 33 包括了这些导航方法在 JSP 页面中的一些例子。
清单 33. JSP 页面中的编程式导航
2 <%
3 response.sendRedirect("anotherPage.jsp");
4 %>
5 <!-- This special JSP tag allows you to forward the request to another resource -->
6 <jsp:forward page="anotherPage.jsp"/>
7 <!-- This special JSP tag allows you to include another resource as
8 part of the processing of this page -->
9 <jsp:include page="anotherPage.jsp" flush="true"/>
10
清单 34. servlet 中的编程式导航
2
3 throws ServletException, IOException {
4
5 ...
6
7 // Redirecting to another resource
8
9 response.sendRedirect("anotherResource.jsp");
10
11 ...
12
13 }
14
15 // This code snippet shows the use of a RequestDispatcher to forward to another resource
16
17 public void doGet(HttpServletRequest request, HttpServletResponse response)
18
19 throws ServletException, IOException {
20
21 ...
22
23 // Get a RequestDispatcher instance
24
25 RequestDispatcher rd = getServletContext().getRequestDispatcher("anotherResource.jsp");
26
27 // Forward the request
28
29 rd.forward(request, response);
30
31 ...
32
33 }
34
35 // This code snippet shows the use of a RequestDispatcher to include another resource
36
37 public void doGet(HttpServletRequest request, HttpServletResponse response)
38
39 throws ServletException, IOException {
40
41 ...
42
43 // Get a RequestDispatcher instance
44
45 RequestDispatcher rd = getServletContext().getRequestDispatcher("anotherResource.jsp");
46
47 // Forward the request
48
49 rd.include(request, response);
50
51 // Continue processing the request
52
53 ...
54
55 }
56
57
Cookie
在 ASP.NET 中,cookie 的处理使用了 HttpRequest 和 HttpResponse 对象的 Cookies 属性。这个属性表示 HttpCookie 对象的一个集合,集合中的每个成员分别表示一个单独的 cookie。
在 J2EE 中,cookie 使用 Cookie 类来表示,并且可通过 HttpServletRequest 和 HttpServletResponse 接口来访问它们。现有的 cookie 使用 HttpServletRequest 接口来访问,新的 cookie 使用 HttpServletResponse 接口来创建。
清单 35 演示了如何在 J2EE 应用程序中使用 cookie。你可以在 Java Servlet 或 JSP 页面的 scriptlet 中使用这些代码片断。
清单 35. 在 J2EE Web 应用程序使用 cookie
2
3 // The first parameter of the constructor is the name of the cookie
4
5 // and the second parameter is the cookie's value
6
7 Cookie myCookie = new Cookie("myCookie", "someValue");
8
9 // Set the age of the cookie - by default cookies will be stored in memory
10
11 // by the browser and will be destroyed when the user closes the browser
12
13 // The setMaxAge() methods takes an integer as its parameter which represents
14
15 // the age in seconds. In this example we're specifying an age of 24 hours for the cookie
16
17 myCookie.setMaxAge(86400);
18
19 // Add cookie to the response
20
21 response.addCookie(myCookie);
22
23 // Here's an example of retrieving cookies from the HttpServletRequest interface
24
25 // Note that the cookies are returned as an array of Cookie objects
26
27 Cookies[] myCookies = request.getCookies();
28
29 // The getCookies method will return null if there are no cookies
30
31 if (myCookies != null) {
32
33 for (int i = 0; i < myCookies.length; i++) {
34
35 Cookie eachCookie = myCookies[i];
36
37 // Do something w/each cookie
38
39 ....
40
41 }
42
43 }
44
45
46
结束语
感谢您使用这个关于 J2EE 开发的入门教程。我们已尽力提供足够的信息使您走上 J2EE 之路,并且使您的开放标准编程之旅尽可能的顺利。