1. 程式人生 > >在Tomcat中配置連線池和資料來源

在Tomcat中配置連線池和資料來源


1、DataSource介面介紹
  • (1)DataSource 概述
JDBC1.0原來是用DriverManager類來產生一個對資料來源的連線。JDBC2.0用一種替代的方法,使用DataSource的實現,程式碼變的更小巧精緻,也更容易控制。 一個DataSource物件代表了一個真正的資料來源。根據DataSource的實現方法,資料來源既可以是從關係資料庫,也電子表格,還可以是一個表格形式的檔案。當一個DataSource物件註冊到名字服務中(JNDI),應用程式就可以通過名字服務獲得DataSource物件,並用它來產生一個與DataSource代表的資料來源之間的連線。javax.sql包中的DataSource介面,可以採用三種實現形式:簡單的實現(只提供Connection物件)、連線池形式的實現和分散式事務形式的實現。javax.sql包中的connectionPoolDataSource提供對連線池實現的介面。
  • (2)使用DataSource的優點
1,DataSource與DriverManager的不同
關於資料來源的資訊和如何來定位資料來源,例如資料庫伺服器的名字,在哪臺機器上,埠號等等,都包含在DataSource物件的屬性裡面去了。這樣,對應用程式的設計來說是更方便了,因為並不需要硬性的把驅動的名字寫死到程式裡面去。通常驅動名字中都包含了驅動提供商的名字,而在DriverManager類中通常是這麼做的。
2,可移植性
如果資料來源要移植到另一個數據庫驅動中,程式碼也很容易做修改。所需要做的修改只是更改DataSource的相關的屬性。而使用DataSource物件的程式碼不需要做任何改動。 
  • (3)配置DataSource
主要包括設定DataSource的屬性,然後將它註冊到JNDI名字服務中去。在註冊DataSource物件的的過程中,系統管理員需要把DataSource物件和一個邏輯名字關聯起來。名字可以是任意的,通常取成能代表資料來源並且容易記住的名字。
在下面的例子中,名字起為:WebMisDB,按照慣例,邏輯名字通常都在jdbc的子上下文中。這樣,邏輯名字的全名就是:jdbc/WebMisDB。
  • (4)產生一個與資料來源的連線
一旦配置好了資料來源物件,應用程式設計者就可以用它來產生一個與資料來源的連線。下面的程式碼片段示例瞭如何用JNDI上下文獲得一個數據源物件,然後如何用資料來源物件產生一個與資料來源的連線。開始的兩行用的是JNDI API,第三行用的才是JDBC的API: 
  Context ctx = new InitialContext(); 
DataSource ds = (DataSource)ctx.lookup("jdbc/WebMisDB");
Connection con = ds.getConnection("myPassword", "myUserName"); 
  在一個基本的DataSource實現中,DataSource.getConnection方法返回的Connection物件和用DriverManager.getConnection方法返回的Connection物件是一樣的。因為DataSource提供的方便性,我們推薦使用DataSource物件來得到一個Connection物件。
  • (5)DataSource的應用場合
對於普通的應用程式設計者,是否使用DataSource物件只是一個選擇問題。但是,對於那些需要用的連線池或者分散式的事務的應用程式設計者來說,就必須使用DataSource物件來獲得Connection。需要注意的是對Tomcat而言,在JNDI的名稱前面應該加上"java:comp/env/" ?其他的伺服器不需要加?
  • (6)資料來源(DataSource)的作用

它相當於客戶端程式和連線池的中介,想要獲得連線池中的連線物件,必須建立一個與該連線池相應的資料來源,然後通過該資料來源獲得連線。

2、JNDI(JAVA NAMING AND DIRECTORY INTERFACE---Java 命名和目錄介面) 
  • (1)JNDI簡介
分散式計算環境通常使用命名和目錄服務來獲取共享的元件和資源。命名和目錄服務將名稱與位置、服務、資訊和資源關聯起來。它是一個為JAVA應用程式提供命名服務的應用程式程式設計介面(API)。命名服務提供了一種為物件命名的機制,這樣你就可以在無需知道物件位置的情況下獲取和使用物件。只要該物件在命名伺服器上註冊過,且你必須知道命名伺服器的地址和該物件在命名伺服器上註冊的JNDI名。就可以找到該物件,獲得其引用,從而運用它提供的服務。命名服務提供名稱—物件的對映。目錄服務提供有關物件的資訊,並提供定位這些物件所需的搜尋工具。Java 命名和目錄介面或 JNDI 提供了一個用於訪問不同的命名和目錄服務的公共介面(JAVA API)。運用一個命名服務來查詢與一個特定名字相關的一個物件,JDBC可以用JNDI來訪問一個關係資料庫。
  • (2)獲得JNDI的初始環境
在JNDI中,在目錄結構中的每一個結點稱為Context 。每一個JNDI名字都是相對於Context 的。這裡沒有絕對名字的概念存在。對一個應用來說,它可以通過使用InitialContext 類來得到其第一個Context:
Context  ctx = new InitialContext ();
應用可以通過這個初始化的Context經由這個目錄樹來定位它所需要的資源或物件。InitialContext在網頁應用程式初始化時被設定,用來支援網頁應用程式元件。所有的入口和資源都放在JNDI名稱空間裡的java:comp/env段裡。
  • (3)查詢已繫結的物件
用ctx..lookup(String name); 根據name找物件
import javax.naming.*;
public class TestJNDI
{	
public static void main(String[] args)
{
        try
{
        Context ctx=new InitialContext();
        Object object=ctx.lookup(“JNDIName”); 	//根據JNDI名查詢繫結的物件
        String str=(String) object;		//強制轉換
        }
catch(NamingException e)
{    e.printStackTrace();
        }
catch(ClassCastException e)
{    e.printStackTrace();
        }
   }
}

3、資料庫連線池技術


  • (1)傳統的Web資料庫程式設計模式
在主程式(如Servlet、Beans)中建立資料庫連線。 
進行SQL操作,取出資料。
斷開資料庫連線。
使用這種模式開發,存在很多問題。
首先,我們要為每一次WEB請求(例如察看某一篇文章的內容)建立一次資料庫連線,對於一次或幾次操作來講,或許你覺察不到系統的開銷,但是,對於WEB程式來講,即使在某一較短的時間段內,其操作請求數也遠遠不是一兩次,而是數十上百次(想想全世界的網友都有可能在您的網頁上查詢資料),在這種情況下,系統開銷是相當大的。事實上,在一個基於資料庫的WEB系統中,建立資料庫連線的操作將是系統中代價最大的操作之一。很多時候,可能您的網站速度瓶頸就在於此。
其次,使用傳統的模式,你必須去管理每一個連線,確保他們能被正確關閉,如果出現程式異常而導致某些連線未能關閉,將導致資料庫系統中的記憶體洩露,最終我們將不得不重啟資料庫。
頻繁的建立、關閉連線,會極大的減低系統的效能,因為對於連線的使用成了系統性能的瓶頸。
  • (2)資料庫連線是一種關鍵的有限的昂貴的資源
這一點在多使用者的網頁應用程式中體現得尤為突出。對資料庫連線的管理能顯著影響到整個應用程式的伸縮性和健壯性,影響到程式的效能指標。資料庫連線池正是針對這個問題提出來的。
連線池是這麼一種機制,當應用程式關閉一個Connection的時候,這個連線被回收,而不是被destroy,因為建立一個連線是一個很費資源的操作。如果能把回收的連線重新利用,會減少新建立連線的數目,顯著的提高執行的效能。該策略的核心思想是:連線複用。
通過採用連線池的方法,伺服器在啟動時先開啟一定數量的連線。 當應用需要連線時,就可以從伺服器請求一個連線。當應用結束該連線時,伺服器就把它釋放到連線池,以備其他客戶機使用。
伺服器監聽客戶的連線請求                    客戶獲得連線

  • (3)連線池的主要作用
減少了建立和釋放資料庫連線的消耗
資料庫連線池負責分配、管理和釋放資料庫連線,它允許應用程式重複使用一個現有的資料庫連線,而再不是重新建立一個;
釋放空閒時間超過最大空閒時間的資料庫連線來避免因為沒有釋放資料庫連線而引起的資料庫連線遺漏。這項技術能明顯提高對資料庫操作的效能。
封裝使用者資訊  使用連線池可以封裝連線資料庫系統所用的使用者資訊(帳號和密碼),這樣客戶端程式在建立連線時不用考慮安全資訊。
  • (4)資料庫連線池的工作原理
當程式中需要建立資料庫連線時,只須從記憶體中取一個來用而不用新建。同樣,使用完畢後,只需放回記憶體即可。而連線的建立、斷開都有連線池自身來管理。同時,我們還可以通過設定連線池的引數來控制連線池中的連線數、每個連線的最大使用次數等等

  • (5)資料庫連線池的最小連線數和最大連線數
資料庫連線池的最小連線數和最大連線數的設定要考慮到下列幾個因素:
最小連線數是連線池一直保持的資料庫連線,所以如果應用程式對資料庫連線的使用量不大,將會有大量的資料庫連線資源被浪費;
最大連線數是連線池能申請的最大連線數,如果資料庫連線請求超過此數,後面的資料庫連線請求將被加入到等待佇列中,這會影響之後的資料庫操作。
如果最小連線數與最大連線數相差太大,那麼最先的連線請求將會獲利,之後超過最小連線數量的連線請求等價於建立一個新的資料庫連線。不過,這些大於最小連線數的資料庫連線在使用完不會馬上被釋放,它將被放到連線池中等待重複使用或是空閒超時後被釋放。
  • (6)使用連線池得到連線
  假設應用程式需要建立到一個名字為EmpolyeeDB的DataSource的連線。使用連線池得到連線的程式碼如下: 
  
Context ctx = new InitialContext(); 
DataSource ds = (DataSource)ctx.lookup("jdbc/EmployeeDB");
Connection con = ds.getConnection("myPassword", "myUserName");
或者:
  Context ctx = new InitialContext(); 
ConnectionPoolDataSource ds = (ConnectionPoolDataSource)ctx.lookup("jdbc/EmployeeDB");
PooledConnection con = ds.getConnection("myPassword", "myUserName");

是否使用連線池獲得一個連線,在應用程式的程式碼上是看不出不同的。在使用這個Connection連線上也沒有什麼不一樣的地方,唯一的不同是在java的finally語句塊中來關閉一個連線。在finally中關閉連線是一個好的程式設計習慣。這樣,即使方法丟擲異常,Connection也會被關閉並回收到連線池中去。程式碼應該如下所示: 
  try
{… 
  }
catch()
{… 
  }
finally
{ 
if(con!=null)
con.close();
}


4、在Tomcat中配置資料庫的連線池


(1)連線池配置(Database Connection Pool (DBCP) Configurations)
DBCP使用的是Jakarta-Commons Database Connection Pool 要使用連線池需要如下的元件即jar檔案。

Jakarta-Commons DBCP 1.1 對應commons-dbcp-1.1.jar。

Jakarta-Commons Collections 2.0 對應commons-collections.jar。

Jakarta-Commons Pool 1.1 對應commons-pool-1.1.jar。
這三個jar檔案要與你的JDBC驅動程式一起放到【TOMCAT_HOME】\common\lib目錄下以便讓tomcat和你的web應用都能夠找到。
 
注:
這三個jar檔案是預設存在與【TOMCAT_HOME】\common\lib下的。
需要注意的地方:第三方的驅動程式或者其他類只能以*.jar的形式放到Tomcat的common\lib目錄中,因為Tomcat只把*.jar檔案加到CLASSPATH中。
不要把上訴三個檔案放到WEB-INF/lib或者其他地方因為這樣會引起混淆。


(2)通過配置阻止連線池漏洞
資料庫連線池建立和管理連線池中建立好的資料庫連線,迴圈使用這些連線以得到更好的效率。這樣比始終為一個使用者保持一個連線和為使用者的請求頻繁的建立和銷燬資料庫連線要高效的多。
這樣就有一個問題出現了,一個Web應用程式必須顯示的釋放ResultSet,Statement和Connection。如果在關閉這些資源的過程中失敗將導致這些資源永遠不在可用,這就是所謂的連線池漏洞。這個漏洞最終會導致連線池中所有的連線不可用。
通過配置Jakarta Common DBCP可以跟蹤和恢復那些被遺棄的資料庫連線。
以下是一系列相關配置:
通過配置DBCP資料來源中的引數removeAbandoned來保證刪除被遺棄的連線使其可以被重新利用。
為ResourceParams(見下文的資料來源配置)標籤新增引數removeAbandoned
<parameter>
<name>removeAbandoned</name>
<value>true</value>
</parameter>
通過這樣配置的以後當連線池中的有效連線接近用完時DBCP將試圖恢復和重用被遺棄的連線。這個引數的值預設是false。
通過設定removeAbandonedTimeout來設定被遺棄的連線的超時的時間,即當一個連線連線被遺棄的時間超過設定的時間時那麼它會自動轉換成可利用的連線。
    <parameter>
     <name>removeAbandonedTimeout</name>
     <value>60</value>
     </parameter>
    預設的超時時間是300秒。
設定logAbandoned引數,以將被遺棄的資料庫連線的回收記入日誌中
<parameter>
<name>logAbandoned</name>
<value>true</value>
</parameter>
這個引數預設為false。
(3)修改server.xml檔案
<Context path="/WebMis" docBase="WebMis" debug="0" reloadable="true"> 
			
		     <Resource name="jdbc/webmis" auth="Container" type="javax.sql.DataSource"/>   
		     <ResourceParams name="jdbc/webmis">	
		     		  <parameter>		
		     		  	    <name>
		     		  	    	factory
		     		  	    </name>		
		     		  	    <value>org.apache.commons.dbcp.BasicDataSourceFactory
		     		  	    </value>		
		         	  </parameter>
		         	  <parameter>		
		    	      	    <name>
		    	      	    	driverClassName
		    	      	    </name>		
		     		  	    <value>com.microsoft.jdbc.sqlserver.SQLServerDriver
		     		  	    </value>		
		     		   </parameter>
		     		  <parameter>	
		     		  	    <name>
		     		  	    	url
		     		  	    </name>		
    		  		<value>jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=DataBase
     		  		</value> 
		     		  </parameter>		
		     		  <parameter>
		     		  		<name>
		     		  			username
		     		  		 </name>		
		     	  	     	 <value>
		     	  	     	 	sa
		     	  	     	 </value>
		     	  	  </parameter>		
		     		  <parameter>
		     		  		<name>
		     		  			password
		     		  		</name>
		     		  		<value>
		     		  		</value>	
		     		  </parameter>		
		     		  <parameter>


		     		  		<name>
		     		  			maxActive
		     		  		</name>
		     		  		<value>
		     		  			20
		     		  		</value>
		     		  </parameter>
		     		  <parameter>		
		     		  	   <name>
		     		  	   		maxIdle
		     		  	   	</name>
		     		  	   <value>10</value>
		     		 </parameter>
		     		 <parameter>	
		     	  			<name>maxWait</name>
		     	  			<value>-1</value>
		     	  	</parameter>	
<parameter>
  <name>removeAbandoned</name>
  <!-- Abandoned DB connections are removed and recycled -->
  <value>true</value>
 </parameter>
 <parameter>
  <name>removeAbandonedTimeout</name>
  <!-- Use the removeAbandonedTimeout parameter to set the number of seconds a DB connection has been idle before it is considered abandoned.  -->
  <value>60</value>
 </parameter>
 <parameter>
  <name>logAbandoned</name>
  <!-- Log a stack trace of the code which abandoned -->
  <value>false</value>
 </parameter>	
		     </ResourceParams>
		</Context>


注意:
所有的入口和資源都放在JNDI名稱空間裡的java:comp/env段裡
設定JNDI資源要在$CATALINA_HOME/conf/server.xml檔案裡使用下列標誌符:
1) <Resource>--設定應用程式可用的資源的名字和型別(同上面說的<resource-ref>等價)。
2) <ResourceParams>--設定Java資源類工廠的名稱或將用的JavaBean屬性。
上述這些標誌符必須放在<Context>和</Context>之間


(2)、拷貝SQLServer的JDBC驅動程式到Tomcat的\common\lib目錄下


(3)、在程式中利用資料來源來訪問資料庫
try
		{
			Context initCtx = new InitialContext(); 
   			Context envCtx = (Context) initCtx.lookup("java:comp/env"); 
   		    DataSource ds = (DataSource)envCtx.lookup("jdbc/webmis"); 
      		Connection con=ds.getConnection();
      	}
        catch (NamingException e)
	    {
   			e.printStackTrace();
   		}
   		catch (SQLException e)
	    {
   			e.printStackTrace();
  		}


5、在server.xml檔案中與資料來源的描述相關的標籤含義
maxActive 連線池的最大資料庫連線數。設為0表示無限制。
maxIdle  資料庫連線的最大空閒時間。超過此空閒時間,資料庫連線將被標記為不可用,然後被釋放。設為0表示無限制。
maxWait 最大建立連線等待時間。如果超過此時間將接到異常。設為-1表示無限制。
removeAbandoned 回收被遺棄的(一般是忘了釋放的)資料庫連線到連線池中。
removeAbandonedTimeout 資料庫連線過多長時間不用將被視為被遺棄而收回連線池中。
logAbandoned 將被遺棄的資料庫連線的回收記入日誌。
driverClassName JDBC驅動程式。
url   資料庫DSN連線字串


6、在Web應用的web.xml檔案中引用該資源


將下面的標籤放在放在<web-app>和</web-app>中間
<!-- Database Config start -->
<resource-ref>
<description>connectDB test</description>
<res-ref-name>jdbc/webmis</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<!-- Database Config end -->


以下是一個綜合配置例項


首先在C:根目錄下建立資料夾mywebapp,作為一個虛擬目錄的位置。


建立一個Sql Server資料庫DataBonus


找到C:\jakarta-tomcat-5.0.19\conf\server.xml,開啟。
 <Context path="/mywebapp" docBase="C:/mywebapp" debug="0" reloadable="true"> 
			
	<Resource name="jdbc/mybonusds" auth="Container" type="javax.sql.DataSource"/>   
<ResourceParams name="jdbc/mybonusds">	
		<parameter>		
		   <name>factory</name>		
		   <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>		
		</parameter>
		<parameter>		
		   <name>driverClassName</name>		
		   <value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>		
		</parameter>
		<parameter>	
		    <name>url</name>		
    		<value>
jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=DataBonus
     		  </value> 
		 </parameter>		
		 <parameter>
		   <name>username</name>		
		   <value>sa</value>
		  </parameter>		
		  <parameter>
		     <name>password</name>
		     <value></value>	
		  </parameter>		
		  <parameter>
<name>maxActive</name>
		      <value>20</value>
		  </parameter>
		  <parameter>		
		     <name>maxIdle</name>
		     <value>10</value>
		  </parameter>
		  <parameter>	
		     <name>maxWait</name>
		     <value>-1</value>
		  </parameter>	
<parameter>
             <name>removeAbandoned</name>
             <!-- Abandoned DB connections are removed and recycled -->
             <value>true</value>
          </parameter>
          <parameter>
              <name>removeAbandonedTimeout</name>
  <!-- Use the removeAbandonedTimeout parameter to set the number of seconds a DB connection has been idle before it is considered abandoned.  -->
              <value>60</value>
           </parameter>
           <parameter>
              <name>logAbandoned</name>
            <!-- Log a stack trace of the code which abandoned -->
              <value>false</value>
           </parameter>	
	  </ResourceParams>
</Context>


   做一個JSP頁面index.jsp放到mywebapp下面,程式碼:

<%--字符集設為"gb2312",使動態頁面支援中文--%>
<%@ page contentType="text/html; charset=GB2312"%>


<!-- 這裡使用一個字串變數 ("PAGETITLE") 保持題目和主標題的一致性。-->
<html>
<head>
<title>
<%= pagetitle %>
</title>
</head>


<body bgcolor=#FFFFFF>


<font face="Helvetica">


<h2>
<font color=#DB1260>
<%= pagetitle %>
</font>
</h2>


<!-- 匯入必要的類和類庫 -->


<%@ page import="
    javax.naming.*,
    java.sql.*,
    javax.sql.DataSource
"%>


<!-- 宣告一個類方法 -->


<%!
//宣告變數
//標題
  String pagetitle = "這是JSP呼叫資料庫的例子";


%>


<!-- 下面這些程式碼將被插入到servlet中 -->


<%


   java.sql.Connection conn= null;
   java.sql.Statement stmt =null;
   java.sql.ResultSet rs=null;


  try {
    // 通過JNDI獲取主介面


     Context initCtx = new InitialContext(); 
     Context envCtx = (Context) initCtx.lookup("java:comp/env"); 
     DataSource ds = (DataSource)envCtx.lookup("jdbc/mybonusds"); 
     conn=ds.getConnection();


      stmt = conn.createStatement();


    
    	//執行SQL語句
      stmt.execute("select * from 獎金");
    //取得結果集
      rs = stmt.getResultSet();
      
    %>


  <table border="1">
   <tr>
      <td width="60" height="20"><% out.print("編號"); %></td>
      <td width="80" height="20"><% out.print("姓名"); %></td>
      <td width="200" height="20"><% out.print("發獎名稱"); %></td>
      <td width="100" height="20"><% out.print("金額"); %></td>
      <td width="200" height="20"><% out.print("備註"); %></td>
   </tr>
  <%   while (rs.next()) {


  %>
    <tr>
      <td width="60" height="20"><% out.print(rs.getString("編號")); %></td>
      <td width="80" height="20"><% out.print(rs.getString("姓名")); %></td>
      <td width="200" height="20"><% out.print(rs.getString("發獎名稱")); %></td>
      <td width="100" height="20"><% out.print(rs.getString("金額")); %></td>
      <td width="200" height="20"><% out.print(rs.getString("備註")); %></td>
   </tr>
     <%  } %>
  </table>




<%
 // Catch exceptions
  } 
  
  catch (Exception e) {
  }
  finally {


 if (rs != null)
     {
      try{rs.close();}catch(Exception ignore){};
      }


     if (stmt != null)
     {
      try{stmt.close();}catch(Exception ignore){};
      }
     if (conn != null)
     {
      try{conn.close();}catch(Exception ignore){};
      }




 %>




<%
  }
%>


</font>
</body>
</html>

啟動Tomcat。

瀏覽:http://127.0.0.1:8080/mywebapp/index.jsp