作者:baggio785
日期:2006-4-24
關(guān)鍵詞:DataSource(數(shù)據(jù)源),Tomcat ,連接池

前言


本文根據(jù)實(shí)例詳細(xì)介紹了如何在tomcat中配置數(shù)據(jù)源。網(wǎng)上此類文章很多,但是基本都是雷同的,而且對(duì)一些特殊問(wèn)題以及原理并未詳細(xì)闡述,所以想根據(jù)自己的實(shí)際經(jīng)驗(yàn),并結(jié)合例子寫一篇詳細(xì)的文章。
本文是偶的一些拙見(jiàn),有不正確的地方請(qǐng)大家多多評(píng)論指正

開(kāi)發(fā)環(huán)境

本文的環(huán)境:JDK1.4.2,TOMCAT5.0.28,Oracle9i

JDBC簡(jiǎn)介

提到數(shù)據(jù)源,那就不能不說(shuō)JDBC。JDBCJava Database Connectivity的縮寫。在java.sql包中提供了JDBC API,定義了訪問(wèn)數(shù)據(jù)庫(kù)的接口和類。但是JDBC API不能直接訪問(wèn)數(shù)據(jù)庫(kù),必須依賴于數(shù)據(jù)庫(kù)廠商提供的JDBC驅(qū)動(dòng)程序,即JDBC DRIVER
Java.sql 中常用的接口和類如下:
?????? Driver 接口和 DriverManager
?????? Connection
?????? Statement
?????? PreparedSataement

?????? ResultSet

?

1???? Driver 接口和 DriverManager
?????? DriverManager
類用來(lái)建立和數(shù)據(jù)庫(kù)的連接以及管理 JDBC 驅(qū)動(dòng)程序,常用方法如下:
方法 描述
registerDriver(Driver driver) DriverManager 中注冊(cè) JDBC 驅(qū)動(dòng)程序
getConnection(String url,String user,String pwd) 建立和數(shù)據(jù)庫(kù)的連接,返回 Connection 對(duì)象
setLoginTimeOut(int seconds) 設(shè)定等待數(shù)據(jù)庫(kù)連接的最長(zhǎng)時(shí)間
setLogWriter(PrintWriter out) 設(shè)定輸入數(shù)據(jù)庫(kù)日至的 PrintWriter 對(duì)象
2???? Connection
?????? Connection
代表和數(shù)據(jù)庫(kù)的連接,其常用方法如下:
方法 描述
getMetaData() 返回?cái)?shù)據(jù)庫(kù)的 MetaData 數(shù)據(jù)。 MetaData 數(shù)據(jù)包含了數(shù)據(jù)庫(kù)的相關(guān)信息,例如當(dāng)前數(shù)據(jù)庫(kù)連接的用戶名、使用的 JDBC 驅(qū)動(dòng)程序、數(shù)據(jù)庫(kù)允許的最大連接數(shù)、數(shù)據(jù)庫(kù)的版本等等。
createStatement() 創(chuàng)建并返回 Statement 對(duì)象
PrepareStatement(String sql) 創(chuàng)建并返回 prepareStatement 對(duì)象
3 ???? Statement
?????? Statement
用來(lái)執(zhí)行靜態(tài) sql 語(yǔ)句。例如,對(duì)于 insert update delete 語(yǔ)句,調(diào)用 executeUpdate(String sql) 方法,而 select 語(yǔ)句可以調(diào)用 executeQuery(String sql) 方法, executeQuery(String sql) 方法返回 ResultSet 對(duì)象。
4 ???? PrepareStatement
?? PrepareStatement
用于執(zhí)行動(dòng)態(tài)的 sql 語(yǔ)句,即允許 sql 語(yǔ)句中包含參數(shù)。使用方法為:
?? String sql = “select col1 from tablename where col2=? And col3=?”;
?? PrepareStatement perpStmt = conn.preparestatement(sql);
?? perpStmt.setstring(1,col2Value);
????? perpStmt.setFloat(2,col3Value);
????? ResultSet rs = perpStmt.executeQuery();
5 ???? ResultSet
ResultSet
用來(lái)表示 select 語(yǔ)句查詢得到的記錄集,一個(gè) StateMent 對(duì)象在同一時(shí)刻只能打開(kāi)一個(gè) ResultSet 對(duì)象。通過(guò) ResultSet getXXX() 方法來(lái)得到字段值。 ResultSet 提供了 getString() getFloat() getInt() 等方法。可以通過(guò)字段的序號(hào)或者字段的名字來(lái)制定獲取某個(gè)字段的值。例如:在上例中 getString(0),getString(col1) 都可以獲得字段 col1 的值。
 
事務(wù)處理

在實(shí)際應(yīng)用中,我們會(huì)遇到同時(shí)提交多個(gè)
sql 語(yǔ)句,這些 sql 語(yǔ)句要么全部成功,要么全部失敗,如果其中一條提交失敗,則必須撤銷整個(gè)事務(wù)。為此, Connection 類提供了 3 個(gè)控制事務(wù)的方法:
 
方法 描述
setAutoCommit(boolen autoCommit) 設(shè)置是否自動(dòng)提交事務(wù),默認(rèn)為自動(dòng)提交
commit() 提交事務(wù)
rollback() 撤銷事務(wù)
參考例子:
try{
conn.SetautoCommit(false);
stmt = conn.createstatement();
stmt.executeUpdate(“delete form table1 where col1=1”);
stmt.eecuteUpdate(“delete from table2 where col2=1”);
conn.comm.it();
}catch(Exception e){
?????? e.printStackTrace;
try{
?????? ?????? conn.rollback();
} catch(Exception e1){
?????? e1.printStackTrace;
}
}
通過(guò)一個(gè) JSP 例子來(lái)訪問(wèn) oracle 數(shù)據(jù)庫(kù):
<%@ page import=”java.util.*”>
<%@ page import=”java.sql.*”>
<%
try{
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
// 加載 oracle 驅(qū)動(dòng)程序
Class.forName(“oracle.jdbc.driver.OracleDriver.”);
// 注冊(cè) oracle 驅(qū)動(dòng)程序
DriverManager.regidterDriver(new oracle.jdbc.driver.OracleDriver());
// 建立數(shù)據(jù)庫(kù)連接
conn=DriverManager.getConnection(“jdbc:oracle:thin:@your db ip:your db port:sid”,dbuser,dbpassword);
stmt = conn.createStatement();
rs = stmt.executeQuery(“select * from tablename”);
while(rs.next){
?????? out.print(rs.getstring(“colname”));
}
}catch(Exception e){
}
finally{
rs.close();
stmt.close();
conn.close();
}
%>

 
數(shù)據(jù)源簡(jiǎn)介

JDBC2.0
提供了 javax.sql.DataSource 的接口,負(fù)責(zé)與數(shù)據(jù)庫(kù)建立連接,實(shí)際應(yīng)用時(shí)不需要編寫連接數(shù)據(jù)庫(kù)代碼,直接從數(shù)據(jù)源獲得數(shù)據(jù)庫(kù)的連接。 Dataource 中事先建立了多個(gè)數(shù)據(jù)庫(kù)連接,這些數(shù)據(jù)庫(kù)連接保持在數(shù)據(jù)庫(kù)連接池中,當(dāng)程序訪問(wèn)數(shù)據(jù)庫(kù)時(shí),只需要從連接池從取出空閑的連接,訪問(wèn)數(shù)據(jù)庫(kù)結(jié)束,在將這些連接歸還給連接池。
DataSource 對(duì)象由容器( Tomcat )提供,不能使用創(chuàng)建實(shí)例的方法來(lái)生成 DataSource 對(duì)象,要采用 JAVA JNDI Java Nameing and Directory Interface java 命名和目錄接口)來(lái)獲得 DataSource 對(duì)象的引用。(另有一種說(shuō)法:“其實(shí)從技術(shù)上來(lái)說(shuō),數(shù)據(jù)源連接方式是不需要目錄服務(wù)的,我們同樣可以通過(guò)序列化數(shù)據(jù)源對(duì)象直接訪問(wèn)文件系統(tǒng)。這點(diǎn)是需要明確的。”感興趣的朋友可以試試。) JNDI 是一種將對(duì)象和名字綁定的技術(shù),對(duì)象工廠負(fù)責(zé)生產(chǎn)出對(duì)象,這些對(duì)象都和唯一的名字相綁定。程序中可以通過(guò)這個(gè)名字來(lái)獲得對(duì)象的引用。 Tomcat DataSource 作為一種可配置的 JNDI 資源來(lái)處理,生成 DataSource 對(duì)象的工廠為 org.apache.comm.ons.dbcp.BasicDataSourceFactory ?

配置數(shù)據(jù)源

配置數(shù)據(jù)源其實(shí)相當(dāng)簡(jiǎn)單:
首先在 server.xml 中加入 <Resource> 元素,打開(kāi) server.xml ,在 <Context> 中加入以下代碼(以 oracle 為例):
 
<Resource name="jdbc/ JNDI 名字 "
?????????????? auth="Container"
?????????????? type="javax.sql.DataSource"/>
?<ResourceParams name="jdbc/JNDI 名字 ">
??? <parameter>
?????<name>factory</name>
????? <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
??? </parameter>
??? <parameter>
????? <name>maxActive</name>
????? <value>100</value>
??? </parameter>
??? <parameter>
????? <name>maxIdle</name>
????? <value>30</value>
??? </parameter>
?
??? <parameter>
????? <name>maxWait</name>
????? <value>10000</value>
??? </parameter>
?
??? <parameter>
???? <name>username</name>
???? <value> 用戶名 </value>
??? </parameter>
??? <parameter>
???? <name>password</name>
???? <value> 密碼 </value>
??? </parameter>
?
??? <parameter>
?????? <name>driverClassName</name>
?????? <value>oracle.jdbc.driver.OracleDriver</value>
??? </parameter>
?
??? <parameter>
?? ???<name>url</name>
????? <value>jdbc:oracle:thin:@ip: 端口 :sid </value>
??? </parameter>
?</ResourceParams>
<Resource> 元素的屬性如下:
屬性 描述
name 指定 Resource JNDI 的名字
auth 指定管理 Resource Manager ,由兩個(gè)可選值: Container Application Container 表示由容器來(lái)創(chuàng)建和管理 Resource Application 表示由 WEB 應(yīng)用來(lái)創(chuàng)建和管理 Resource 。如果在 web application deployment descriptor 中使用 <resource-ref> ,這個(gè)屬性是必需的,如果使用 <resource-env-ref> ,這個(gè)屬性是可選的。
type 指定 Resource 所屬的 java 類名
<ResourceParams> 元素的屬性如下:
屬性 描述
name 指定 ResourceParams JNDI 的名字,必須和 Resource name 保持一致
factory 指定生成 DataSource 對(duì)象的 factory 的類名
maxActive 指定數(shù)據(jù)庫(kù)連接池中處于活動(dòng)狀態(tài)的數(shù)據(jù)庫(kù)連接最大數(shù)目, 0 表示不受限制
maxIdle 指定數(shù)據(jù)庫(kù)連接池中 處于 空閑狀態(tài)的數(shù)據(jù)庫(kù)連接的最大數(shù)目, 0 表示不受限制
maxWait 指定數(shù)據(jù)庫(kù)連接池中的數(shù)據(jù)庫(kù)連接處于空閑狀態(tài)的最長(zhǎng)時(shí)間(單位為毫秒),超過(guò)這一事件,將會(huì)拋出異常。 -1 表示可以無(wú)限期等待。
username 指定連接數(shù)據(jù)庫(kù)的用戶名
password 指定連接數(shù)據(jù)庫(kù)的密碼
driverClassName 指定連接數(shù)據(jù)庫(kù)的 JDBC 驅(qū)動(dòng)程序
url 指定連接數(shù)據(jù)庫(kù)的 URL

其他文章說(shuō)以上配置就OK了,對(duì)于web.xml的配置可有可無(wú),其實(shí)不是這樣子的。如果在web應(yīng)用中訪問(wèn)了由Servlet容器管理的某個(gè)JNDI Resource,則必須在web.xml中聲明對(duì)這個(gè)JNDI Resource的引用。表示資源引用的元素為<resource-ref>,該元素加在<wepapp></ wepapp >中。

<resource-ref>
???? <descryiption>DB Connection</descryiption>
<res-ref-name>jdbc/JNDI 名字 </ res-ref-name>
<res-type>javax.sql.DataSource </ res- type>
<res-auth>Container </ res- auth>
</resource-ref>

<resource-ref>
元素的屬性如下:

屬性 描述
description 對(duì)所引用的資源的說(shuō)明
res-ref-name 指定所引用資源的 JNDI 名字,與 <Resource> 元素中的 name 屬性保持一致
res-type 指定所引用資源的類名字,與 <Resource> 元素中的 type 屬性保持一致
res-auth 指定所引用資源的 Manager ,與 <Resource> 元素中的 auth 屬性保持一致

到這里,數(shù)據(jù)源就已經(jīng)配置成功了。但是我在測(cè)試的時(shí)候除了一點(diǎn)小麻煩,主要原因是對(duì)DataSource的概念沒(méi)搞清楚。我是這么測(cè)試的,寫一個(gè)測(cè)試類,然后在eclipse中進(jìn)行junit測(cè)試,
捕獲的異常為:
javax.naming.NoInitialContextException : Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file:?java.naming.factory.initia l
同樣的代碼在JSP文件中正常運(yùn)行,后來(lái)翻了一些資料,終于找到了問(wèn)題的所在了。原來(lái)DataSource是由容器(TOMCAT)提供的,所以我的測(cè)試會(huì)拋出異常。為了再次驗(yàn)證想法是否正確,在jsp文件中import剛才拋出異常的類,在進(jìn)行連接數(shù)據(jù)庫(kù),結(jié)果一切正常。
下面的例子是實(shí)際應(yīng)用中使用 DataSource ,在 jsp 文件中連接 oracle
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page import="java.sql.*"%>
<%@ page import="javax.naming.*"%>
<%@ page import="javax.sql.*"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
?<head>
?</head>
<body>
<%
Context initContext = new InitialContext();
Context envContext = (Context) initContext.lookup("java:/comp/env");
DataSource db = (DataSource)envContext.lookup("jdbc/javablogorl");
// javablogorl <Resource> 元素中 name 屬性的值
Connection conn = db.getConnection( );?
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM blog_systemadmin");
while(rs.next()){
?????? out.print(rs.getString("admin_name")+" ");
?????? out.print(rs.getString("admin_password")+"<br>");
}
rs.close();
stmt.close();
conn.close();
%>?
?</body>
</html>
 


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=674822