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

前言


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

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

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

JDBC簡介

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

?????? ResultSet

?

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

在實(shí)際應(yīng)用中,我們會遇到同時提交多個
sql 語句,這些 sql 語句要么全部成功,要么全部失敗,如果其中一條提交失敗,則必須撤銷整個事務(wù)。為此, Connection 類提供了 3 個控制事務(wù)的方法:
 
方法 描述
setAutoCommit(boolen autoCommit) 設(shè)置是否自動提交事務(wù),默認(rèn)為自動提交 。
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;
}
}
通過一個 JSP 例子來訪問 oracle 數(shù)據(jù)庫:
<%@ page import=”java.util.*”>
<%@ page import=”java.sql.*”>
<%
try{
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
// 加載 oracle 驅(qū)動程序
Class.forName(“oracle.jdbc.driver.OracleDriver.”);
// 注冊 oracle 驅(qū)動程序
DriverManager.regidterDriver(new oracle.jdbc.driver.OracleDriver());
// 建立數(shù)據(jù)庫連接
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ù)源簡介

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

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

配置數(shù)據(jù)源其實(shí)相當(dāng)簡單:
首先在 server.xml 中加入 <Resource> 元素,打開 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 ,由兩個可選值: Container Application 。 Container 表示由容器來創(chuàng)建和管理 Resource Application 表示由 WEB 應(yīng)用來創(chuàng)建和管理 Resource 。如果在 web application deployment descriptor 中使用 <resource-ref> ,這個屬性是必需的,如果使用 <resource-env-ref> ,這個屬性是可選的。
type 指定 Resource 所屬的 java 類名
<ResourceParams> 元素的屬性如下:
屬性 描述
name 指定 ResourceParams JNDI 的名字,必須和 Resource name 保持一致
factory 指定生成 DataSource 對象的 factory 的類名
maxActive 指定數(shù)據(jù)庫連接池中處于活動狀態(tài)的數(shù)據(jù)庫連接最大數(shù)目, 0 表示不受限制
maxIdle 指定數(shù)據(jù)庫連接池中 處于 空閑狀態(tài)的數(shù)據(jù)庫連接的最大數(shù)目, 0 表示不受限制
maxWait 指定數(shù)據(jù)庫連接池中的數(shù)據(jù)庫連接處于空閑狀態(tài)的最長時間(單位為毫秒),超過這一事件,將會拋出異常。 -1 表示可以無限期等待。
username 指定連接數(shù)據(jù)庫的用戶名
password 指定連接數(shù)據(jù)庫的密碼
driverClassName 指定連接數(shù)據(jù)庫的 JDBC 驅(qū)動程序
url 指定連接數(shù)據(jù)庫的 URL

其他文章說以上配置就OK了,對于web.xml的配置可有可無,其實(shí)不是這樣子的。如果在web應(yīng)用中訪問了由Servlet容器管理的某個JNDI Resource,則必須在web.xml中聲明對這個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 對所引用的資源的說明
res-ref-name 指定所引用資源的 JNDI 名字,與 <Resource> 元素中的 name 屬性保持一致
res-type 指定所引用資源的類名字,與 <Resource> 元素中的 type 屬性保持一致
res-auth 指定所引用資源的 Manager ,與 <Resource> 元素中的 auth 屬性保持一致

到這里,數(shù)據(jù)源就已經(jīng)配置成功了。但是我在測試的時候除了一點(diǎn)小麻煩,主要原因是對DataSource的概念沒搞清楚。我是這么測試的,寫一個測試類,然后在eclipse中進(jìn)行junit測試,
捕獲的異常為:
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)行,后來翻了一些資料,終于找到了問題的所在了。原來DataSource是由容器(TOMCAT)提供的,所以我的測試會拋出異常。為了再次驗(yàn)證想法是否正確,在jsp文件中import剛才拋出異常的類,在進(jìn)行連接數(shù)據(jù)庫,結(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