JNDI 數(shù)據(jù)源配置的相關(guān)內(nèi)容已經(jīng)在 JNDI 資源文檔中詳細(xì)介紹過(guò)。但從 Tomcat 用戶的反饋意見(jiàn)來(lái)看,有些配置的細(xì)節(jié)問(wèn)題非常棘手。
針對(duì)常用的數(shù)據(jù)庫(kù),我們已經(jīng)給 Tomcat 用戶提供了一些配置范例,以及關(guān)于數(shù)據(jù)庫(kù)使用的一些通用技巧。本章就將展示這些范例和技巧。
另外,雖然有些注意事項(xiàng)來(lái)自于用戶所提供的配置和反饋信息,但你可能也有不同的實(shí)踐。如果經(jīng)過(guò)試驗(yàn),你發(fā)現(xiàn)某些配置可能具有廣泛的助益作用,或者你覺(jué)得它們會(huì)使本章內(nèi)容更加完善,請(qǐng)務(wù)必不吝賜教。
請(qǐng)注意,對(duì)比 Tomcat 7.x 和 Tomcat 8.x,JNDI 資源配置多少有些不同,這是因?yàn)槭褂玫?Apache Commons DBCP 庫(kù)的版本不同所致。所以,為了在 Tomcat 8 中使用,你最好修改老版本的 JNDI 資源配置,以便能夠匹配下文范例中的格式。詳情可參看Tomcat 遷移文檔。
另外還要提示的是,一般來(lái)說(shuō)(特別是對(duì)于本教程而言),JNDI 數(shù)據(jù)源配置會(huì)假定你已經(jīng)理解了 Context 與 Host 的配置偏好,其中包括在后者配置偏好中的應(yīng)用自動(dòng)部署的相關(guān)內(nèi)容。
java.sql.DriverManager
支持服務(wù)提供者機(jī)制。這項(xiàng)功能的實(shí)際作用在于:對(duì)于所有可用的 JDBC 驅(qū)動(dòng),只要它們聲明提供 META-INF/services/java.sql.Driver
文件,就會(huì)被自動(dòng)發(fā)現(xiàn)、加載并注冊(cè),從而減輕了我們?cè)趧?chuàng)建
JDBC 連接之前還需要顯式地加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)的負(fù)擔(dān)。但在 servlet 容器環(huán)境的所有 Java 版本中,卻根本沒(méi)法實(shí)現(xiàn)這種功能。問(wèn)題在于 java.sql.DriverManager
只會(huì)掃描一次驅(qū)動(dòng)。
Tomcat 自帶的阻止 JRE 內(nèi)存泄漏偵聽(tīng)器可以在一定程度上解決這個(gè)問(wèn)題,它會(huì)在 Tomcat 啟動(dòng)時(shí)觸發(fā)驅(qū)動(dòng)掃描。該偵聽(tīng)器默認(rèn)是啟用的。只有可見(jiàn)于該偵聽(tīng)器的庫(kù)(比如 $CATALINA_BASE/lib
中的庫(kù))才能被數(shù)據(jù)庫(kù)驅(qū)動(dòng)所掃描。如果你想禁用該功能,那么一定要記?。菏紫仁褂? JDBC 的 Web 應(yīng)用會(huì)觸發(fā)掃描,從而當(dāng)該應(yīng)用重新加載時(shí)會(huì)出錯(cuò);對(duì)于其他依賴該功能的 Web 應(yīng)用來(lái)說(shuō)也會(huì)導(dǎo)致出錯(cuò)。
所以,假如應(yīng)用的 WEB-INF/lib
目錄中存在數(shù)據(jù)庫(kù)驅(qū)動(dòng),那么這些應(yīng)用就不能依賴服務(wù)提供者機(jī)制,而應(yīng)該顯式地注冊(cè)驅(qū)動(dòng)。
java.sql.DriverManager
中的驅(qū)動(dòng)已經(jīng)被認(rèn)為是內(nèi)存泄露之源。當(dāng) Web 應(yīng)用停止運(yùn)行時(shí),它所注冊(cè)的任何驅(qū)動(dòng)都必須重新注冊(cè)。當(dāng) Web 應(yīng)用停止運(yùn)行時(shí),Tomcat 會(huì)嘗試自動(dòng)尋找并重新注冊(cè)任何由 Web 應(yīng)用類加載器所加載的 JDBC 驅(qū)動(dòng)。但最好是由應(yīng)用通過(guò) ServletContextListener
來(lái)實(shí)現(xiàn)這一點(diǎn)。
Apache Tomcat 的默認(rèn)數(shù)據(jù)庫(kù)連接池實(shí)現(xiàn)基于的是 Apache Commons 項(xiàng)目的庫(kù),具體來(lái)說(shuō)是這兩個(gè)庫(kù):
這兩個(gè)庫(kù)都位于一個(gè) JAR 文件中:$CATALINA_HOME/lib/tomcat-dbcp.jar
。但該文件只包括連接池所需要的類,包名也已經(jīng)改變了,以避免與應(yīng)用沖突。
DBCP 2.0 支持 JDBC 4.1。
可參閱 DBCP 文檔了解完整的配置參數(shù)。
數(shù)據(jù)庫(kù)連接池創(chuàng)建并管理著一些與數(shù)據(jù)庫(kù)的連接。與打開(kāi)新的連接相比,回收或重用現(xiàn)有的數(shù)據(jù)庫(kù)連接要更為高效一些。
連接池化還存在一個(gè)問(wèn)題。Web 應(yīng)用必須明確地關(guān)閉 ResultSet、Statement,以及 Connection。假如 Web 應(yīng)用無(wú)法關(guān)閉這些資源時(shí),會(huì)導(dǎo)致這些資源再也無(wú)法被重用,從而造成了數(shù)據(jù)庫(kù)連接池“泄露”。如果再也沒(méi)有可用連接時(shí),最終這將導(dǎo)致 Web 應(yīng)用數(shù)據(jù)庫(kù)連接失敗。
針對(duì)該問(wèn)題,有一個(gè)解決辦法:通過(guò)配置 Apache Commons DBCP,記錄并恢復(fù)這些廢棄的數(shù)據(jù)庫(kù)連接。它不僅能恢復(fù)這些連接,而且還能針對(duì)打開(kāi)這些連接而又永遠(yuǎn)不關(guān)閉它們的代碼生成堆棧跟蹤。
為了配置 DBCP 數(shù)據(jù)源來(lái)移除并回收廢棄的數(shù)據(jù)庫(kù)連接,將下列屬性(一個(gè)或全部)添加到你的 DBCP 數(shù)據(jù)源中的 Resource
配置中:
removeAbandonedOnBorrow=true
removeAbandonedOnMaintenance=true
以上屬性默認(rèn)都為 false
。注意,只有當(dāng) timeBetweenEvictionRunsMillis
為正值,從而啟用池維護(hù)時(shí),removeAbandonedOnMaintenance
才能生效。關(guān)于這些屬性的詳情,可查看 DBCP 文檔 。
使用 removeAbandonedTimeout
屬性設(shè)置某個(gè)數(shù)據(jù)庫(kù)連接閑置的秒數(shù),超過(guò)此時(shí)段,即被認(rèn)為是廢棄連接。
removeAbandonedTimeout="60"
默認(rèn)的去除廢棄連接的超時(shí)為 300 秒。
將 logAbandoned
設(shè)為 true
,可以讓 DBCP 針對(duì)那些拋棄數(shù)據(jù)庫(kù)連接資源的代碼,記錄堆棧跟蹤信息。
logAbandoned="true"
默認(rèn)為 false
。
已報(bào)告的能夠正常運(yùn)作的 MySQL 與 JDBC 驅(qū)動(dòng)的版本號(hào)為:
在繼續(xù)下一步的操作之前,千萬(wàn)不要忘了將 JDBC 驅(qū)動(dòng)的 JAR 文件復(fù)制到 $CATALINA_HOME/lib
中。
一定要按照下面的說(shuō)明去操作,否則會(huì)出現(xiàn)問(wèn)題。
創(chuàng)建一個(gè)新的測(cè)試用戶、一個(gè)新的數(shù)據(jù)庫(kù),以及一張新的測(cè)試表。必須為 MySQL 用戶指定一個(gè)密碼。如果密碼為空,那么在連接時(shí),就會(huì)無(wú)法正常驅(qū)動(dòng)。
mysql> GRANT ALL PRIVILEGES ON *.* TO javauser@localhost
-> IDENTIFIED BY 'javadude' WITH GRANT OPTION;
mysql> create database javatest;
mysql> use javatest;
mysql> create table testdata (
-> id int not null auto_increment primary key,
-> foo varchar(25),
-> bar int);
注意:一旦測(cè)試結(jié)束,就該把上例中的這個(gè)用戶刪除!
下面在 testdata
表中插入一些測(cè)試數(shù)據(jù):
mysql> insert into testdata values(null, 'hello', 12345);
Query OK, 1 row affected (0.00 sec)
mysql> select * from testdata;
+----+-------+-------+
| ID | FOO | BAR |
+----+-------+-------+
| 1 | hello | 12345 |
+----+-------+-------+
1 row in set (0.00 sec)
mysql>
在 Context 中添加資源聲明,以便在 Tomcat 中配置 JNDI 數(shù)據(jù)源。
范例如下:
<Context>
<!-- maxTotal: Maximum number of database connections in pool. Make sure you
configure your mysqld max_connections large enough to handle
all of your db connections. Set to -1 for no limit.
-->
<!-- maxIdle: Maximum number of idle database connections to retain in pool.
Set to -1 for no limit. See also the DBCP documentation on this
and the minEvictableIdleTimeMillis configuration parameter.
-->
<!-- maxWaitMillis: Maximum time to wait for a database connection to become available
in ms, in this example 10 seconds. An Exception is thrown if
this timeout is exceeded. Set to -1 to wait indefinitely.
-->
<!-- username and password: MySQL username and password for database connections -->
<!-- driverClassName: Class name for the old mm.mysql JDBC driver is
org.gjt.mm.mysql.Driver - we recommend using Connector/J though.
Class name for the official MySQL Connector/J driver is com.mysql.jdbc.Driver.
-->
<!-- url: The JDBC connection url for connecting to your MySQL database.
-->
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
maxTotal="100" maxIdle="30" maxWaitMillis="10000"
username="javauser" password="javadude" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/javatest"/>
</Context>
為該測(cè)試應(yīng)用創(chuàng)建一個(gè) WEB-INF/web.xml
文件:
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<description>MySQL Test App</description>
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/TestDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
創(chuàng)建一個(gè)簡(jiǎn)單的 test.jsp
頁(yè)面,稍后將用到它。
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<sql:query var="rs" dataSource="jdbc/TestDB">
select id, foo, bar from testdata
</sql:query>
<html>
<head>
<title>DB Test</title>
</head>
<body>
<h2>Results</h2>
<c:forEach var="row" items="${rs.rows}">
Foo ${row.foo}<br/>
Bar ${row.bar}<br/>
</c:forEach>
</body>
</html>
JSP 頁(yè)面用到了 JSTL 的 SQL 和 Core taglibs。你可以從 Apache Tomcat Taglibs - Standard Tag Library 項(xiàng)目中獲取它,不過(guò)要注意應(yīng)該是
1.1.x 或之后的版本。下載 JSTL 后,將 jstl.jar
和 standard.jar
復(fù)制到 Web 應(yīng)用的 WEB-INF/lib
目錄中。
最后,將你的應(yīng)用部署到 $CATALINA_BASE/webapps
,可以采用兩種方式:或者將應(yīng)用以名叫 DBTest.war
的 WAR 文件形式部署;或者把應(yīng)用放入一個(gè)叫 DBTest
的子目錄中。
部署完畢后,就可以在瀏覽器輸入 http://localhost:8080/DBTest/test.jsp
,查看你的第一個(gè)勞動(dòng)成果了。
Oracle 需要的配置和 MySQL 差不多,只不過(guò)也存在一些常見(jiàn)問(wèn)題。
針對(duì)過(guò)去版本的 Oracle 的驅(qū)動(dòng)可能以 .zip 格式(而不是 .jar 格式)進(jìn)行分發(fā)的。Tomcat 只使用 *.jar
文件,而且它們還必須安裝在 $CATALINA_HOME/lib
中。因此,classes111.zip
或 classes12.zip
這樣的文件后綴應(yīng)該改成 .jar
。因?yàn)?jar 文件本來(lái)就是一種 zip 文件,因此不需要將原 zip
文件解壓縮然后創(chuàng)建相應(yīng)的 jar 文件,只需改換后綴名即可。
對(duì)于 Oracle 9i 之后的版本,應(yīng)該使用 oracle.jdbc.OracleDriver
而不是 oracle.jdbc.driver.OracleDriver
,因?yàn)?Oracle 規(guī)定開(kāi)始棄用 oracle.jdbc.driver.OracleDriver
,下一個(gè)重大版本將不再支持這一驅(qū)動(dòng)類。
跟前文 MySql 的配置一樣,你也需要在 Context 中定義數(shù)據(jù)源。下面定義一個(gè)叫做 myoracle 的數(shù)據(jù)源,使用上文說(shuō)的短驅(qū)動(dòng)來(lái)連接(用戶名為 scott,密碼為 tiger)到名為 mysid 的SID(Oracle 系統(tǒng)ID,標(biāo)識(shí)一個(gè)數(shù)據(jù)庫(kù)的唯一標(biāo)示符)。 用戶 scott 使用的 Schema 就是默認(rèn)的 schema。
使用 OCI 驅(qū)動(dòng),只需在 URL 字符串中將 thin 變?yōu)?oci 即可。
<Resource name="jdbc/myoracle" auth="Container"
type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@127.0.0.1:1521:mysid"
username="scott" password="tiger" maxTotal="20" maxIdle="10"
maxWaitMillis="-1"/>
在創(chuàng)建 Web 應(yīng)用的 web.xml 文件時(shí),一定要遵從 Web 應(yīng)用部署描述符文件中 DTD 所需要的元素順序。
<resource-ref>
<description>Oracle Datasource example</description>
<res-ref-name>jdbc/myoracle</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
可以使用上文所列的范例應(yīng)用(假如你創(chuàng)建了所需的 DB 實(shí)例和表,等等),將數(shù)據(jù)源代碼用下面的代碼替換:
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/myoracle");
Connection conn = ds.getConnection();
//etc.
PostgreSQL 配置與 Oracle 基本相似。
將 Postgres 的 JDBC jar 文件復(fù)制到 $CATALINA_HOME/lib
中。和 Oracle 配置一樣,jar 文件必須放在這個(gè)目錄中,DBCP 類加載器才能找到它們。不管接下來(lái)如何配置,這是首先必須要做的。
目前有兩種選擇:定義一個(gè)能夠被 Tomcat 所有應(yīng)用所共享的數(shù)據(jù)源,或者定義只能被單個(gè)應(yīng)用所使用的數(shù)據(jù)源。
如果想定義能夠被多個(gè) Tomcat 應(yīng)用所共享的數(shù)據(jù)源,或者只想在文件中定義自己的數(shù)據(jù)源,則采用如下配置:
盡管有些用戶反饋說(shuō)這樣可行,但本文檔作者卻沒(méi)有成功,希望有人能闡述清楚。
<Resource name="jdbc/postgres" auth="Container"
type="javax.sql.DataSource" driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://127.0.0.1:5432/mydb"
username="myuser" password="mypasswd" maxTotal="20" maxIdle="10" maxWaitMillis="-1"/>
如果希望專門為某一應(yīng)用定義數(shù)據(jù)源,其他 Tomcat 應(yīng)用無(wú)法使用,可以使用如下配置。這種方法對(duì) Tomcat 安裝的損害性要小一些。
在你的應(yīng)用的 Context 中創(chuàng)建一個(gè)資源定義,如下所示:
<Context>
<Resource name="jdbc/postgres" auth="Container"
type="javax.sql.DataSource" driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://127.0.0.1:5432/mydb"
username="myuser" password="mypasswd" maxTotal="20" maxIdle="10"
maxWaitMillis="-1"/>
</Context>
<resource-ref>
<description>postgreSQL Datasource example</description>
<res-ref-name>jdbc/postgres</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
在利用程序訪問(wèn)數(shù)據(jù)庫(kù)時(shí),記住把 java:/comp/env
放在你的 JNDI lookup 方法參數(shù)的前部,如下面這段代碼所示。另外,可以用任何你想用的值來(lái)替換 jdbc/postgres
,不過(guò)記得也要用同樣的值來(lái)修改上面的資源定義文件。
InitialContext cxt = new InitialContext();
if ( cxt == null ) {
throw new Exception("Uh oh -- no context!");
}
DataSource ds = (DataSource) cxt.lookup( "java:/comp/env/jdbc/postgres" );
if ( ds == null ) {
throw new Exception("Data source not found!");
}
這些方案或者使用一個(gè)單獨(dú)的數(shù)據(jù)庫(kù)連接(建議僅作測(cè)試用?。蛘呤褂闷渌恍┏鼗夹g(shù)。
雖然并不能嚴(yán)格地解決如何使用 OCI 客戶端來(lái)創(chuàng)建 JNDI 數(shù)據(jù)源的問(wèn)題,但這些注意事項(xiàng)卻能和上文提到的 Oracle 與 DBCP 解決方案結(jié)合起來(lái)使用。
為了使用 OCI 驅(qū)動(dòng),應(yīng)該先安裝一個(gè) Oracle 客戶。你應(yīng)該已經(jīng)通過(guò)光盤安裝好了 Oracle 8i(8.1.7)客戶端,并從 otn.oracle.com 下載了適用的 JDBC/OCI 驅(qū)動(dòng)(Oracle8i 8.1.7.1 JDBC/OCI 驅(qū)動(dòng))。
將 classes12.zip
重命名為 classes12.jar
后,將其復(fù)制到 $CATALINA_HOME/lib
中。根據(jù) Tomcat 的版本以及你所使用的 JDK,你可能還必須該文件中的刪除 javax.sql.*
類。
確保在 $PATH
或 LD_LIBRARY_PATH
(可能在 $ORAHOME\bin
)目錄下存在 ocijdbc8.dll
或 .so
文件,另外還要確認(rèn)能否使用 System.loadLibrary("ocijdbc8");
這樣的簡(jiǎn)單測(cè)試程序加載本地庫(kù)。
下面你應(yīng)該創(chuàng)建一個(gè)簡(jiǎn)單測(cè)試用 servlet 或 jsp,其中應(yīng)該包含以下關(guān)鍵代碼:
DriverManager.registerDriver(new
oracle.jdbc.driver.OracleDriver());
conn =
DriverManager.getConnection("jdbc:oracle:oci8:@database","username","password");
目前數(shù)據(jù)庫(kù)是 host:port:SID
形式,如果你試圖訪問(wèn)測(cè)試用servlet/jsp,那么你會(huì)得到一個(gè) ServletException
異常,造成異常的根本原因在于 java.lang.UnsatisfiedLinkError:get_env_handle
。
分析一下,首先 UnsatisfiedLinkError
表明:
JDBC 類文件和 Oracle 客戶端版本不匹配。消息中透露出的意思是沒(méi)有找到需要的庫(kù)文件。比如,你可能使用 Oracle 8.1.6 的 class12.zip 文件,而 Oracle 客戶端版本則是 8.1.5。classeXXXs.zip 文件必須與 Oracle 客戶端文件版本相一致。
出現(xiàn)了一個(gè) $PATH, LD_LIBRARY_PATH
問(wèn)題。
$ORAHOME\jdbc\lib
目錄中的 class12.zip 文件,同樣能夠正常運(yùn)作。接下來(lái),你可能還會(huì)遇到另一個(gè)錯(cuò)誤消息:ORA-06401 NETCMN: invalid driver designator
。
Oracle 文檔是這么說(shuō)的:“異常原因:登錄(連接)字符串包含一個(gè)不合法的驅(qū)動(dòng)標(biāo)識(shí)符。解決方法:修改字符串,重新提交?!彼裕缦旅孢@樣來(lái)修改數(shù)據(jù)庫(kù)(host:port:SID
)連接字符串:
(description=(address=(host=myhost)(protocol=tcp)(port=1521))(connect_data=(sid=orcl)))
下面是一些 Web 應(yīng)用在使用數(shù)據(jù)庫(kù)時(shí)經(jīng)常會(huì)遇到的問(wèn)題,以及一些應(yīng)對(duì)技巧。
Tomcat 運(yùn)行在 JVM 中。JVM 周期性地會(huì)執(zhí)行垃圾回收(GC),清除不再使用的 Java 對(duì)象。當(dāng) JVM 執(zhí)行 GC 時(shí),Tomcat 中的代碼執(zhí)行就會(huì)終止。如果配置好的數(shù)據(jù)庫(kù)連接建立的最長(zhǎng)時(shí)間小于垃圾回收的時(shí)間,數(shù)據(jù)庫(kù)連接就會(huì)失敗。
在啟動(dòng) Tomcat 時(shí),將 -verbose:gc
參數(shù)添加到 CATALINA_OPTS
環(huán)境變量中,就能知道垃圾回收所占用的時(shí)間了。在啟用 verbose:gc
后, $CATALINA_BASE/logs/catalina.out
日志文件就能包含每次垃圾回收的數(shù)據(jù),其中也包括它所占用的時(shí)間。
正確調(diào)整 JVM 后,垃圾回收可以做到在 99% 的情況下占用時(shí)間不超過(guò) 1 秒。剩余的情況則只占用幾秒鐘的時(shí)間,只有極少數(shù)情況下 GC 會(huì)占用超過(guò) 10 秒鐘的時(shí)間。
保證讓數(shù)據(jù)庫(kù)連接超時(shí)設(shè)定在 10~15 秒。對(duì)于 DBCP,可以使用 maxWaitMillis
參數(shù)來(lái)設(shè)置。
當(dāng)某一請(qǐng)求從連接池中獲取了一個(gè)數(shù)據(jù)庫(kù)連接,然后關(guān)閉了它兩次時(shí),往往會(huì)出現(xiàn)這樣的異常消息。使用連接池時(shí),關(guān)閉連接,就會(huì)把它歸還給連接池,以便之后其他的請(qǐng)求能夠重用該連接,而并不會(huì)關(guān)閉連接。Tomcat 使用多個(gè)線程來(lái)處理并發(fā)請(qǐng)求。下面這個(gè)范例就演示了,在 Tomcat 中,一系列事件導(dǎo)致了這種錯(cuò)誤。
運(yùn)行在線程 1 中的請(qǐng)求 1 獲取了一個(gè)連接。
請(qǐng)求 1 關(guān)閉了數(shù)據(jù)庫(kù)連接。
JVM 將運(yùn)行的線程切換為線程 2。
線程 2 中運(yùn)行的請(qǐng)求 2 獲取了一個(gè)數(shù)據(jù)庫(kù)連接。
(同一個(gè)數(shù)據(jù)庫(kù)連接剛被請(qǐng)求 1 關(guān)閉)
JVM 又將運(yùn)行的線程切換回為線程 1。
請(qǐng)求 1 第二次關(guān)閉了數(shù)據(jù)庫(kù)連接。
JVM 將運(yùn)行的線程切換回線程 2。
請(qǐng)求 2 和線程 2 試圖使用數(shù)據(jù)庫(kù)連接,但卻失敗了。因?yàn)?strong>請(qǐng)求 1 已經(jīng)關(guān)閉了它。
Connection conn = null;
Statement stmt = null; // Or PreparedStatement if needed
ResultSet rs = null;
try {
conn = ... get connection from connection pool ...
stmt = conn.createStatement("select ...");
rs = stmt.executeQuery();
... iterate through the result set ...
rs.close();
rs = null;
stmt.close();
stmt = null;
conn.close(); // Return to connection pool
conn = null; // Make sure we don't close it twice
} catch (SQLException e) {
... deal with errors ...
} finally {
// Always make sure result sets and statements are closed,
// and the connection is returned to the pool
if (rs != null) {
try { rs.close(); } catch (SQLException e) { ; }
rs = null;
}
if (stmt != null) {
try { stmt.close(); } catch (SQLException e) { ; }
stmt = null;
}
if (conn != null) {
try { conn.close(); } catch (SQLException e) { ; }
conn = null;
}
}
注意,雖然在上面的說(shuō)明中,把 JNDI 聲明放在一個(gè) Context 元素里面,但還是有可能(而且有時(shí)更需要)把這些聲明放在服務(wù)器配置文件的 GlobalNamingResources 區(qū)域。被放置在 GlobalNamingResources 區(qū)域的資源將會(huì)被服務(wù)器的各個(gè)上下文所共享。
為了讓 Realm 能運(yùn)作,realm 必須指向定義在 <GlobalNamingResources>
或 <Context>
區(qū)域中的數(shù)據(jù)源,而不是<ResourceLink>
重新命名的數(shù)據(jù)源。
更多建議: