1. 程式人生 > >JavaWeb配置資料來源連線資料庫詳解(概念詳解+多種情況原始碼範例+易出錯型別)

JavaWeb配置資料來源連線資料庫詳解(概念詳解+多種情況原始碼範例+易出錯型別)

我們平時連線資料庫大多是通過類似如下程式碼獲取資料庫連線:
public class DBUtil {
	private static final String URL = "jdbc:mysql://127.0.0.1:3306/jdbcTest?useUnicode=true&characterEncoding=utf-8";
	private static final String USER = "root";
	private static final String PASSWORD = "";
	private static Connection connection = null;
	static {
		try {
			// 第一步,載入驅動,此處載入MySQL驅動,如載入Oracle驅動則輸入com.jdbc.driver.OracalDriver
			Class.forName("com.mysql.jdbc.Driver");
			// 第二步,獲得資料庫連線,請注意這裡獲得的連線物件型別是java.sql.Connection
			connection = DriverManager.getConnection(URL, USER, PASSWORD);
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	//獲取資料庫連線物件的靜態方法
	public static Connection getConnection(){
		if(connection != null){
			System.out.println("獲得資料庫連線!");
			return connection;
		}
		System.out.println("連線失敗!");
		return null;
	}
}

習慣了以硬編碼的形式在程式中建立資料庫連線,處理完成後就在finally語句塊或自定義的關閉連線的方法中呼叫相關物件的

close方法。對於重視資料庫連線數的應用(高併發Web應用)來說,這樣做會耗費大量的時間和資料庫資源,硬編碼的形式也不甚

靈活。從邏輯上考慮,資料庫連線的維護理應是獨立於程式,以達到解耦的目的。

 在Tomcat這個JavaWeb容器(也是Servlet容器)下通過配置DataSource(資料來源)物件可以解決上面所述的問題。JDBC

javax.sql.DataSource介面負責建立於資料庫的連線,程式中直接從資料來源中獲取資料庫連線。DataSource物件由

Servlet容器

程式在請求資料庫連線時,Servlet容器就從連線池中選取空閒連線物件引用返回給程式。這個過程是基於JNDI實現的。

JNDI是一種將物件與名字繫結的技術,物件工廠(org.apache.commons.dbcp.BasicDataSourceFactory)生產物件,外部程式

可以通過名字來獲取相應物件的引用。在javax.naming包提供了Context介面,該介面提供了將物件與名字繫結的方法

bind(String name, Object obj)通過名字檢索物件的方法lookup(String name)

配置資料來源的基本步驟:

1、在META-INF下新建context.xml檔案配置<Resource>元素定義資料來源。

2、在web.xml中加入<resource-ref>元素引用資料來源。

3、在程式中通過Context介面獲取資料來源物件引用。

1、配置資料來源context.xml:

在Java Web應用的META-INF目錄下新建一個context.xml配置檔案,其中的<Resource>元素用於定義JNDI資源,內容如下:
<Context reloadable="true" > 
    <Resource  
        name="jdbc/DBname" 
        auth="Container"  
        type="javax.sql.DataSource" 
        maxActive="100" maxIdle="30" maxWait="10000" 
        username="root" password=""  
        driverClassName="com.mysql.jdbc.Driver" 
        url="jdbc:mysql://localhost:3306/DBname?autoReconnect=true" 
    /> 
</Context> 

<Resource>元素的屬性說明: 
Name:指定Resource資源的JNDI名稱;
auth:可選填Container或Application,指定Resource的管理者;
type:指定Resource資源的Java類名;
maxActive:設定資料庫連線池中活動狀態連線的最大數目,為0則不受限制;
maxIdle:設定資料庫連線池中空閒狀態連線的最大數目,為0則不受限制;
maxWait:設定資料庫連線池中空閒狀態連線的最長等待時間,超時則丟擲異常,為-1則可無限等待;
username:指定資料庫的使用者名稱;
password:指定連線資料庫的密碼;
driverClassName:指定資料庫的JDBC驅動器的Driver實現類名字(這裡為MySQL資料庫連線);
url:連線資料庫的url。
 
注:可在<CATALINA_HOME>/conf/server.xml 檔案中的對應<Host>元素中如上配置<Resource>子元素以供Tomcat容器內的多個Web應用使用。
 

2、在web.xml配置JNDI資源引用: 

Java Web應用中要使用JNDI資源,必須在web.xml中配置對該JNDI資源的引用<resource-ref>元素。內容如下:
<web-app> 
    <resource-ref> 
        <description>DB Connection</description> 
        <res-ref-name>jdbc/DBname </res-ref-name> 
        <res-type>javax.sql.DataSource</res-type> 
        <res-auth>Container</res-auth> 
    </resource-ref> 
</web-app> 
 
<resource-ref>元素的子元素說明: 
description:對所引用JNDI資源的描述;
res-ref-name:引用的JNDI資源的名稱,與上面<Resource>元素中的name屬性一致;
res-type:引用的JNDI資源的類名稱,與上面<Resource>元素中的type屬性一致;
res-auth:引用資源的管理者,上面<Resource>元素中的auth屬性一致;
 
3、Web應用中使用資料來源: 
使用DataSource連線資料庫,不再用以下方式來建立到資料庫的連線了。
Connection conn = null; 
                     
Class.forName("com.mysql.jdbc.Driver"); 
                     
String dbUrl = "jdbc:mysql://localhost:3306/DBname"; 
String dbUser = "root"; 
String dbPwd = ""; 
                     
conn = DriverManager.getConnection(dbUrl, dbUser, dbPwd); 

而是使用相對簡單些的JNDI資源訪問方式lookup方法,如下:
Context sourceCtx = new InitialContext(); 
DataSource ds = (DataSource) sourceCtx.lookup("java:comp/env/jdbc/DBname "); 
conn = ds.getConnection(); 

注:使用DataSource方式連線資料庫,當使用完資料庫操作之後呼叫各種資源物件的close方法時,由Tomcat容器調回這些連線到連線池中進行管理,而不是直接與資料庫斷開連線。

出錯說明與正確執行姿勢:

當我們在main()方法或者@Test中測試獲取資料來源時會丟擲如下異常:

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.initial


出錯的原因是:資料來源物件時由Servlet容器Tomcat管理的,在main()方法中或@Test測試單元中呼叫時,並沒有經過Servlet,是無法與Servlet容器建立連線的,所以我們應在Servlet中測試(JSP也是Servlet)。

<%@ page language="java" import="java.util.*" pageEncoding="GB18030"%>
<%@ page import="java.io.*"%>
<%@ page import="java.sql.*"%>
<%@ page import="javax.naming.*"%>
<%@ page import="javax.sql.*"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>測試資料來源</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->
  </head>
  
  <body>
    <% 
    	Context context = null;
		DataSource dataSource = null;
		Connection connection = null;
		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;
		try {
			context = new InitialContext();
			dataSource = (DataSource) context.lookup("java:comp/env/jdbc/test");
			connection = dataSource.getConnection();
			String sqlString = "select * from user";			
			preparedStatement = connection.prepareStatement(sqlString);
			resultSet = preparedStatement.executeQuery();
			while(resultSet.next()){
				out.println("<br><br>ID: " + resultSet.getInt("user_id") + "<br>姓名: " + resultSet.getString("user_name") + "<br>性別: " + resultSet.getString("user_sex"));
			}
			resultSet.close();
			preparedStatement.close();
			connection.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
    %>
  </body>
</html>

效果如圖(獲取了test資料庫user表的內容):


附:spring資料來源配置(在applicationContext中):
<!-- 資料庫的連線資訊,使用的是資料來源的方式jdbc/dbcp/c3p0 -->
	<!-- 配置資料來源,將資料來源交給spring容器管理 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
		destroy-method="close">
		<property name="driverClass" value="com.mysql.jdbc.Driver" />
		<property name="jdbcUrl" value="jdbc:mysql:///edusystem" />
		<property name="user" value="root" />
		<property name="password" value="" />
		<!--初始化時獲取的連線數,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
		<property name="initialPoolSize" value="1" />
		<!--連線池中保留的最小連線數。 -->
		<property name="minPoolSize" value="1" />
		<!--連線池中保留的最大連線數。Default: 15 -->
		<property name="maxPoolSize" value="300" />
		<!--最大空閒時間,60秒內未使用則連線被丟棄。若為0則永不丟棄。Default: 0 -->
		<property name="maxIdleTime" value="60" />
		<!--當連線池中的連線耗盡的時候c3p0一次同時獲取的連線數。Default: 3 -->
		<property name="acquireIncrement" value="5" />
		<!--每60秒檢查所有連線池中的空閒連線。Default: 0 -->
		<property name="idleConnectionTestPeriod" value="60" />
	</bean>