javaweb連接池總結

一、應用程序直接獲取數據庫連接的缺點

  用戶每次請求都需要向數據庫獲得鏈接,而數據庫創建連接通常需要消耗相對較大的資源,創建時間也較長。假設網站一天10萬訪問量,數據庫服務器就需要創建10萬次連接,極大的浪費數據庫的資源,並且極易造成數據庫服務器內存溢出、拓機。如下圖所示:

  

二、使用數據庫連接池優化程序性能

2.1、數據庫連接池的基本概念

  數據庫連接是一種關鍵的有限的昂貴的資源,這一點在多用戶的網頁應用程序中體現的尤爲突出.對數據庫連接的管理能顯著影響到整個應用程序的伸縮性和健壯性,影響到程序的性能指標.數據庫連接池正式針對這個問題提出來的.數據庫連接池負責分配,管理和釋放數據庫連接,它允許應用程序重複使用一個現有的數據庫連接,而不是重新建立一個。如下圖所示:

  

       數據庫連接池在初始化時將創建一定數量的數據庫連接放到連接池中, 這些數據庫連接的數量是由最小數據庫連接數來設定的.無論這些數據庫連接是否被使用,連接池都將一直保證至少擁有這麼多的連接數量.連接池的最大數據庫連接數量限定了這個連接池能佔有的最大連接數,當應用程序向連接池請求的連接數超過最大連接數量時,這些請求將被加入到等待隊列中.

       數據庫連接池的最小連接數和最大連接數的設置要考慮到以下幾個因素:

  1. 最小連接數:是連接池一直保持的數據庫連接,所以如果應用程序對數據庫連接的使用量不大,將會有大量的數據庫連接資源被浪費.
  2. 最大連接數:是連接池能申請的最大連接數,如果數據庫連接請求超過次數,後面的數據庫連接請求將被加入到等待隊列中,這會影響以後的數據庫操作
  3. 如果最小連接數與最大連接數相差很大:那麼最先連接請求將會獲利,之後超過最小連接數量的連接請求等價於建立一個新的數據庫連接.不過,這些大於最小連接數的數據庫連接在使用完不會馬上被釋放,他將被放到連接池中等待重複使用或是空間超時後被釋放.

2.2、編寫數據庫連接池

  編寫連接池需實現java.sql.DataSource接口。DataSource接口中定義了兩個重載的getConnection方法:

  • Connection getConnection()
  • Connection getConnection(String username, String password)

  實現DataSource接口,並實現連接池功能的步驟:

  1. 在DataSource構造函數中批量創建與數據庫的連接,並把創建的連接加入LinkedList對象中。
  2. 實現getConnection方法,讓getConnection方法每次調用時,從LinkedList中取一個Connection返回給用戶。
  3. 當用戶使用完Connection,調用Connection.close()方法時,Collection對象應保證將自己返回到LinkedList中,而不要把conn還給數據庫。Collection保證將自己返回到LinkedList中是此處編程的難點

 數據庫連接池核心代碼

  使用動態代理技術構建連接池中的connection

複製代碼
 1 proxyConn = (Connection) Proxy.newProxyInstance(this.getClass()
 2             .getClassLoader(), conn.getClass().getInterfaces(),
 3             new InvocationHandler() {
 4         //此處爲內部類,當close方法被調用時將conn還回池中,其它方法直接執行
 5             public Object invoke(Object proxy, Method method,
 6                       Object[] args) throws Throwable {
 7                 if (method.getName().equals("close")) {
 8                     pool.addLast(conn);
 9                     return null;
10             }
11             return method.invoke(conn, args);
12         }
13     });
複製代碼

數據庫連接池編寫範例:

複製代碼
  1 package me.gacl.demo;
  2 
  3 import java.io.InputStream;
  4 import java.io.PrintWriter;
  5 import java.lang.reflect.InvocationHandler;
  6 import java.lang.reflect.Method;
  7 import java.lang.reflect.Proxy;
  8 import java.sql.Connection;
  9 import java.sql.DriverManager;
 10 import java.sql.SQLException;
 11 import java.util.LinkedList;
 12 import java.util.Properties;
 13 import javax.sql.DataSource;
 14 
 15 /**
 16 * @ClassName: JdbcPool
 17 * @Description:編寫數據庫連接池
 18 * @author: 孤傲蒼狼
 19 * @date: 2014-9-30 下午11:07:23
 20 *
 21 */ 
 22 public class JdbcPool implements DataSource{
 23 
 24     /**
 25     * @Field: listConnections
 26     *         使用LinkedList集合來存放數據庫鏈接,
 27     *        由於要頻繁讀寫List集合,所以這裏使用LinkedList存儲數據庫連接比較合適
 28     */ 
 29     private static LinkedList<Connection> listConnections = new LinkedList<Connection>();
 30     
 31     static{
 32         //在靜態代碼塊中加載db.properties數據庫配置文件
 33         InputStream in = JdbcPool.class.getClassLoader().getResourceAsStream("db.properties");
 34         Properties prop = new Properties();
 35         try {
 36             prop.load(in);
 37             String driver = prop.getProperty("driver");
 38             String url = prop.getProperty("url");
 39             String username = prop.getProperty("username");
 40             String password = prop.getProperty("password");
 41             //數據庫連接池的初始化連接數大小
 42             int jdbcPoolInitSize =Integer.parseInt(prop.getProperty("jdbcPoolInitSize"));
 43             //加載數據庫驅動
 44             Class.forName(driver);
 45             for (int i = 0; i < jdbcPoolInitSize; i++) {
 46                 Connection conn = DriverManager.getConnection(url, username, password);
 47                 System.out.println("獲取到了鏈接" + conn);
 48                 //將獲取到的數據庫連接加入到listConnections集合中,listConnections集合此時就是一個存放了數據庫連接的連接池
 49                 listConnections.add(conn);
 50             }
 51             
 52         } catch (Exception e) {
 53             throw new ExceptionInInitializerError(e);
 54         }
 55     }
 56     
 57     @Override
 58     public PrintWriter getLogWriter() throws SQLException {
 59         // TODO Auto-generated method stub
 60         return null;
 61     }
 62 
 63     @Override
 64     public void setLogWriter(PrintWriter out) throws SQLException {
 65         // TODO Auto-generated method stub
 66         
 67     }
 68 
 69     @Override
 70     public void setLoginTimeout(int seconds) throws SQLException {
 71         // TODO Auto-generated method stub
 72         
 73     }
 74 
 75     @Override
 76     public int getLoginTimeout() throws SQLException {
 77         // TODO Auto-generated method stub
 78         return 0;
 79     }
 80 
 81     @Override
 82     public <T> T unwrap(Class<T> iface) throws SQLException {
 83         // TODO Auto-generated method stub
 84         return null;
 85     }
 86 
 87     @Override
 88     public boolean isWrapperFor(Class<?> iface) throws SQLException {
 89         // TODO Auto-generated method stub
 90         return false;
 91     }
 92 
 93     /* 獲取數據庫連接
 94      * @see javax.sql.DataSource#getConnection()
 95      */
 96     @Override
 97     public Connection getConnection() throws SQLException {
 98         //如果數據庫連接池中的連接對象的個數大於0
 99         if (listConnections.size()>0) {
100             //從listConnections集合中獲取一個數據庫連接
101             final Connection conn = listConnections.removeFirst();
102             System.out.println("listConnections數據庫連接池大小是" + listConnections.size());
103             //返回Connection對象的代理對象
104             return (Connection) Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler(){
105                 @Override
106                 public Object invoke(Object proxy, Method method, Object[] args)
107                         throws Throwable {
108                     if(!method.getName().equals("close")){
109                         return method.invoke(conn, args);
110                     }else{
111                         //如果調用的是Connection對象的close方法,就把conn還給數據庫連接池
112                         listConnections.add(conn);
113                         System.out.println(conn + "被還給listConnections數據庫連接池了!!");
114                         System.out.println("listConnections數據庫連接池大小爲" + listConnections.size());
115                         return null;
116                     }
117                 }
118             });
119         }else {
120             throw new RuntimeException("對不起,數據庫忙");
121         }
122     }
123 
124     @Override
125     public Connection getConnection(String username, String password)
126             throws SQLException {
127         return null;
128     }
129 }
複製代碼

 db.properties配置文件如下:

複製代碼
1 driver=com.mysql.jdbc.Driver
2 url=jdbc:mysql://localhost:3306/jdbcStudy
3 username=root
4 password=XDP
5 
6 jdbcPoolInitSize=10
複製代碼

寫一個JdbcUtil測試數據庫連接池

複製代碼
 1 package me.gacl.utils;
 2 
 3 import java.sql.Connection;
 4 import java.sql.ResultSet;
 5 import java.sql.SQLException;
 6 import java.sql.Statement;
 7 import me.gacl.demo.JdbcPool;
 8 
 9 public class JdbcUtil {
10     
11     /**
12     * @Field: pool
13     *          數據庫連接池
14     */ 
15     private static JdbcPool pool = new JdbcPool();
16     
17     /**
18     * @Method: getConnection
19     * @Description: 從數據庫連接池中獲取數據庫連接對象
20     * @Anthor:孤傲蒼狼
21     * @return Connection數據庫連接對象
22     * @throws SQLException
23     */ 
24     public static Connection getConnection() throws SQLException{
25         return pool.getConnection();
26     }
27     
28     /**
29     * @Method: release
30     * @Description: 釋放資源,
31     * 釋放的資源包括Connection數據庫連接對象,負責執行SQL命令的Statement對象,存儲查詢結果的ResultSet對象
32     * @Anthor:孤傲蒼狼
33     *
34     * @param conn
35     * @param st
36     * @param rs
37     */ 
38     public static void release(Connection conn,Statement st,ResultSet rs){
39         if(rs!=null){
40             try{
41                 //關閉存儲查詢結果的ResultSet對象
42                 rs.close();
43             }catch (Exception e) {
44                 e.printStackTrace();
45             }
46             rs = null;
47         }
48         if(st!=null){
49             try{
50                 //關閉負責執行SQL命令的Statement對象
51                 st.close();
52             }catch (Exception e) {
53                 e.printStackTrace();
54             }
55         }
56         
57         if(conn!=null){
58             try{
59                 //關閉Connection數據庫連接對象
60                 conn.close();
61             }catch (Exception e) {
62                 e.printStackTrace();
63             }
64         }
65     }
66 }
複製代碼

 三、開源數據庫連接池

  現在很多WEB服務器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的實現,即連接池的實現。通常我們把DataSource的實現,按其英文含義稱之爲數據源,數據源中都包含了數據庫連接池的實現。
  也有一些開源組織提供了數據源的獨立實現:

  • DBCP 數據庫連接池
  • C3P0 數據庫連接池

  在使用了數據庫連接池之後,在項目的實際開發中就不需要編寫連接數據庫的代碼了,直接從數據源獲得數據庫的連接。

3.1、DBCP數據源

  DBCP 是 Apache 軟件基金組織下的開源連接池實現,要使用DBCP數據源,需要應用程序應在系統中增加如下兩個 jar 文件:

  • Commons-dbcp.jar:連接池的實現
  • Commons-pool.jar:連接池實現的依賴庫

  Tomcat 的連接池正是採用該連接池來實現的。該數據庫連接池既可以與應用服務器整合使用,也可由應用程序獨立使用。

3.2、在應用程序中加入dbcp連接池

  1.導入相關jar包
        commons-dbcp-1.2.2.jar、commons-pool.jar
  2、在類目錄下加入dbcp的配置文件:dbcpconfig.properties

    dbcpconfig.properties的配置信息如下:

複製代碼
 1 #連接設置
 2 driverClassName=com.mysql.jdbc.Driver
 3 url=jdbc:mysql://localhost:3306/jdbcstudy
 4 username=root
 5 password=XDP
 6 
 7 #<!-- 初始化連接 -->
 8 initialSize=10
 9 
10 #最大連接數量
11 maxActive=50
12 
13 #<!-- 最大空閒連接 -->
14 maxIdle=20
15 
16 #<!-- 最小空閒連接 -->
17 minIdle=5
18 
19 #<!-- 超時等待時間以毫秒爲單位 6000毫秒/1000等於60秒 -->
20 maxWait=60000
21 
22 
23 #JDBC驅動建立連接時附帶的連接屬性屬性的格式必須爲這樣:[屬性名=property;] 
24 #注意:"user" 與 "password" 兩個屬性會被明確地傳遞,因此這裏不需要包含他們。
25 connectionProperties=useUnicode=true;characterEncoding=UTF8
26 
27 #指定由連接池所創建的連接的自動提交(auto-commit)狀態。
28 defaultAutoCommit=true
29 
30 #driver default 指定由連接池所創建的連接的只讀(read-only)狀態。
31 #如果沒有設置該值,則“setReadOnly”方法將不被調用。(某些驅動並不支持只讀模式,如:Informix)
32 defaultReadOnly=
33 
34 #driver default 指定由連接池所創建的連接的事務級別(TransactionIsolation)。
35 #可用值爲下列之一:(詳情可見javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
36 defaultTransactionIsolation=READ_UNCOMMITTED
複製代碼

  如下圖所示:

  

  3、在獲取數據庫連接的工具類(如jdbcUtils)的靜態代碼塊中創建池

複製代碼
 1 package me.gacl.util;
 2 
 3 import java.io.InputStream;
 4 import java.sql.Connection;
 5 import java.sql.ResultSet;
 6 import java.sql.SQLException;
 7 import java.sql.Statement;
 8 import java.util.Properties;
 9 import javax.sql.DataSource;
10 import org.apache.commons.dbcp.BasicDataSourceFactory;
11 
12 /**
13 * @ClassName: JdbcUtils_DBCP
14 * @Description: 數據庫連接工具類
15 * @author: 孤傲蒼狼
16 * @date: 2014-10-4 下午6:04:36
17 *
18 */ 
19 public class JdbcUtils_DBCP {
20     /**
21      * 在java中,編寫數據庫連接池需實現java.sql.DataSource接口,每一種數據庫連接池都是DataSource接口的實現
22      * DBCP連接池就是java.sql.DataSource接口的一個具體實現
23      */
24     private static DataSource ds = null;
25     //在靜態代碼塊中創建數據庫連接池
26     static{
27         try{
28             //加載dbcpconfig.properties配置文件
29             InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
30             Properties prop = new Properties();
31             prop.load(in);
32             //創建數據源
33             ds = BasicDataSourceFactory.createDataSource(prop);
34         }catch (Exception e) {
35             throw new ExceptionInInitializerError(e);
36         }
37     }
38     
39     /**
40     * @Method: getConnection
41     * @Description: 從數據源中獲取數據庫連接
42     * @Anthor:孤傲蒼狼
43     * @return Connection
44     * @throws SQLException
45     */ 
46     public static Connection getConnection() throws SQLException{
47         //從數據源中獲取數據庫連接
48         return ds.getConnection();
49     }
50     
51     /**
52     * @Method: release
53     * @Description: 釋放資源,
54     * 釋放的資源包括Connection數據庫連接對象,負責執行SQL命令的Statement對象,存儲查詢結果的ResultSet對象
55     * @Anthor:孤傲蒼狼
56     *
57     * @param conn
58     * @param st
59     * @param rs
60     */ 
61     public static void release(Connection conn,Statement st,ResultSet rs){
62         if(rs!=null){
63             try{
64                 //關閉存儲查詢結果的ResultSet對象
65                 rs.close();
66             }catch (Exception e) {
67                 e.printStackTrace();
68             }
69             rs = null;
70         }
71         if(st!=null){
72             try{
73                 //關閉負責執行SQL命令的Statement對象
74                 st.close();
75             }catch (Exception e) {
76                 e.printStackTrace();
77             }
78         }
79         
80         if(conn!=null){
81             try{
82                 //將Connection連接對象還給數據庫連接池
83                 conn.close();
84             }catch (Exception e) {
85                 e.printStackTrace();
86             }
87         }
88     }
89 }
複製代碼

  測試DBCP數據源

複製代碼
 1 package me.gacl.test;
 2 
 3 import java.sql.Connection;
 4 import java.sql.PreparedStatement;
 5 import java.sql.ResultSet;
 6 import org.junit.Test;
 7 import me.gacl.util.JdbcUtils_DBCP;
 8 
 9 public class DataSourceTest {
10     
11     @Test
12     public void dbcpDataSourceTest() {
13         Connection conn = null;
14         PreparedStatement st = null;
15         ResultSet rs = null;
16         try{
17             //獲取數據庫連接
18             conn = JdbcUtils_DBCP.getConnection();
19             String sql = "insert into test1(name) values(?)";
20             st = conn.prepareStatement(sql);
21             st.setString(1, "gacl");
22             st.executeUpdate();
23             //獲取數據庫自動生成的主鍵
24             rs = st.getGeneratedKeys();
25             if(rs.next()){
26                 System.out.println(rs.getInt(1));
27             }
28         }catch (Exception e) {
29             e.printStackTrace();
30         }finally{
31             //釋放資源
32             JdbcUtils_DBCP.release(conn, st, rs);
33         }
34     }
35 }
複製代碼

 3.3、C3P0數據源

  C3P0是一個開源的JDBC連接池,它實現了數據源和JNDI綁定,支持JDBC3規範和JDBC2的標準擴展。目前使用它的開源項目有Hibernate,Spring等。C3P0數據源在項目開發中使用得比較多。

  c3p0與dbcp區別
  1. dbcp沒有自動回收空閒連接的功能
  2. c3p0有自動回收空閒連接功能

3.4、在應用程序中加入C3P0連接池

  1.導入相關jar包
       c3p0-0.9.2-pre1.jar、mchange-commons-0.2.jar,如果操作的是Oracle數據庫,那麼還需要導入c3p0-oracle-thin-extras-0.9.2-pre1.jar
  2、在類目錄下加入C3P0的配置文件:c3p0-config.xml

    c3p0-config.xml的配置信息如下:

複製代碼
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!--
 3 c3p0-config.xml必須位於類路徑下面
 4 private static ComboPooledDataSource ds;
 5 static{
 6     try {
 7         ds = new ComboPooledDataSource("MySQL");
 8     } catch (Exception e) {
 9         throw new ExceptionInInitializerError(e);
10     }
11 }
12 -->
13 
14 <c3p0-config>
15     <!--
16     C3P0的缺省(默認)配置,
17     如果在代碼中“ComboPooledDataSource ds = new ComboPooledDataSource();”這樣寫就表示使用的是C3P0的缺省(默認)配置信息來創建數據源
18     -->
19     <default-config>
20         <property name="driverClass">com.mysql.jdbc.Driver</property>
21         <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy</property>
22         <property name="user">root</property>
23         <property name="password">XDP</property>
24         
25         <property name="acquireIncrement">5</property>
26         <property name="initialPoolSize">10</property>
27         <property name="minPoolSize">5</property>
28         <property name="maxPoolSize">20</property>
29     </default-config>
30 
31     <!--
32     C3P0的命名配置,
33     如果在代碼中“ComboPooledDataSource ds = new ComboPooledDataSource("MySQL");”這樣寫就表示使用的是name是MySQL的配置信息來創建數據源
34     -->
35     <named-config name="MySQL">
36         <property name="driverClass">com.mysql.jdbc.Driver</property>
37         <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy</property>
38         <property name="user">root</property>
39         <property name="password">XDP</property>
40         
41         <property name="acquireIncrement">5</property>
42         <property name="initialPoolSize">10</property>
43         <property name="minPoolSize">5</property>
44         <property name="maxPoolSize">20</property>
45     </named-config>
46 
47 </c3p0-config>
複製代碼

  如下圖所示:

  

  3、在獲取數據庫連接的工具類(如jdbcUtils)的靜態代碼塊中創建池

複製代碼
 1 package me.gacl.util;
 2 
 3 import java.sql.Connection;
 4 import java.sql.ResultSet;
 5 import java.sql.SQLException;
 6 import java.sql.Statement;
 7 import com.mchange.v2.c3p0.ComboPooledDataSource;
 8 
 9 /**
10 * @ClassName: JdbcUtils_C3P0
11 * @Description: 數據庫連接工具類
12 * @author: 孤傲蒼狼
13 * @date: 2014-10-4 下午6:04:36
14 *
15 */ 
16 public class JdbcUtils_C3P0 {
17     
18     private static ComboPooledDataSource ds = null;
19     //在靜態代碼塊中創建數據庫連接池
20     static{
21         try{
22             //通過代碼創建C3P0數據庫連接池
23             /*ds = new ComboPooledDataSource();
24             ds.setDriverClass("com.mysql.jdbc.Driver");
25             ds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbcstudy");
26             ds.setUser("root");
27             ds.setPassword("XDP");
28             ds.setInitialPoolSize(10);
29             ds.setMinPoolSize(5);
30             ds.setMaxPoolSize(20);*/
31             
32             //通過讀取C3P0的xml配置文件創建數據源,C3P0的xml配置文件c3p0-config.xml必須放在src目錄下
33             //ds = new ComboPooledDataSource();//使用C3P0的默認配置來創建數據源
34             ds = new ComboPooledDataSource("MySQL");//使用C3P0的命名配置來創建數據源
35             
36         }catch (Exception e) {
37             throw new ExceptionInInitializerError(e);
38         }
39     }
40     
41     /**
42     * @Method: getConnection
43     * @Description: 從數據源中獲取數據庫連接
44     * @Anthor:孤傲蒼狼
45     * @return Connection
46     * @throws SQLException
47     */ 
48     public static Connection getConnection() throws SQLException{
49         //從數據源中獲取數據庫連接
50         return ds.getConnection();
51     }
52     
53     /**
54     * @Method: release
55     * @Description: 釋放資源,
56     * 釋放的資源包括Connection數據庫連接對象,負責執行SQL命令的Statement對象,存儲查詢結果的ResultSet對象
57     * @Anthor:孤傲蒼狼
58     *
59     * @param conn
60     * @param st
61     * @param rs
62     */ 
63     public static void release(Connection conn,Statement st,ResultSet rs){
64         if(rs!=null){
65             try{
66                 //關閉存儲查詢結果的ResultSet對象
67                 rs.close();
68             }catch (Exception e) {
69                 e.printStackTrace();
70             }
71             rs = null;
72         }
73         if(st!=null){
74             try{
75                 //關閉負責執行SQL命令的Statement對象
76                 st.close();
77             }catch (Exception e) {
78                 e.printStackTrace();
79             }
80         }
81         
82         if(conn!=null){
83             try{
84                 //將Connection連接對象還給數據庫連接池
85                 conn.close();
86             }catch (Exception e) {
87                 e.printStackTrace();
88             }
89         }
90     }
91 }
複製代碼

  測試C3P0數據源

複製代碼
 1 package me.gacl.test;
 2 
 3 import java.sql.Connection;
 4 import java.sql.PreparedStatement;
 5 import java.sql.ResultSet;
 6 import org.junit.Test;
 7 import me.gacl.util.JdbcUtils_C3P0;
 8 import me.gacl.util.JdbcUtils_DBCP;
 9 
10 public class DataSourceTest {
11     
12     @Test
13     public void c3p0DataSourceTest() {
14         Connection conn = null;
15         PreparedStatement st = null;
16         ResultSet rs = null;
17         try{
18             //獲取數據庫連接
19             conn = JdbcUtils_C3P0.getConnection();
20             String sql = "insert into test1(name) values(?)";
21             st = conn.prepareStatement(sql);
22             st.setString(1, "gacl");
23             st.executeUpdate();
24             //獲取數據庫自動生成的主鍵
25             rs = st.getGeneratedKeys();
26             if(rs.next()){
27                 System.out.println(rs.getInt(1));
28             }
29         }catch (Exception e) {
30             e.printStackTrace();
31         }finally{
32             //釋放資源
33             JdbcUtils_C3P0.release(conn, st, rs);
34         }
35     }
36 }
複製代碼

 四、配置Tomcat數據源

  在實際開發中,我們有時候還會使用服務器提供給我們的數據庫連接池,比如我們希望Tomcat服務器在啓動的時候可以幫我們創建一個數據庫連接池,那麼我們在應用程序中就不需要手動去創建數據庫連接池,直接使用Tomcat服務器創建好的數據庫連接池即可。要想讓Tomcat服務器在啓動的時候幫我們創建一個數據庫連接池,那麼需要簡單配置一下Tomcat服務器。

4.1、JNDI技術簡介

  JNDI(Java Naming and Directory Interface),Java命名和目錄接口,它對應於J2SE中的javax.naming包,
  這套API的主要作用在於:它可以把Java對象放在一個容器中(JNDI容器),併爲容器中的java對象取一個名稱,以後程序想獲得Java對象,只需通過名稱檢索即可。其核心API爲Context,它代表JNDI容器,其lookup方法爲檢索容器中對應名稱的對象。

  Tomcat服務器創建的數據源是以JNDI資源的形式發佈的,所以說在Tomat服務器中配置一個數據源實際上就是在配置一個JNDI資源,通過查看Tomcat文檔,我們知道使用如下的方式配置tomcat服務器的數據源:

複製代碼
1 <Context>
2   <Resource name="jdbc/datasource" auth="Container"
3             type="javax.sql.DataSource" username="root" password="XDP"
4             driverClassName="com.mysql.jdbc.Driver" 
5             url="jdbc:mysql://localhost:3306/jdbcstudy"
6             maxActive="8" maxIdle="4"/>
7 </Context>
複製代碼

  服務器創建好數據源之後,我們的應用程序又該怎麼樣得到這個數據源呢,Tomcat服務器創建好數據源之後是以JNDI的形式綁定到一個JNDI容器中的,我們可以把JNDI想象成一個大大的容器,我們可以往這個容器中存放一些對象,一些資源,JNDI容器中存放的對象和資源都會有一個獨一無二的名稱,應用程序想從JNDI容器中獲取資源時,只需要告訴JNDI容器要獲取的資源的名稱,JNDI根據名稱去找到對應的資源後返回給應用程序。我們平時做javaEE開發時,服務器會爲我們的應用程序創建很多資源,比如request對象,response對象,服務器創建的這些資源有兩種方式提供給我們的應用程序使用:第一種是通過方法參數的形式傳遞進來,比如我們在Servlet中寫的doPost和doGet方法中使用到的request對象和response對象就是服務器以參數的形式傳遞給我們的。第二種就是JNDI的方式,服務器把創建好的資源綁定到JNDI容器中去,應用程序想要使用資源時,就直接從JNDI容器中獲取相應的資源即可。

  對於上面的name="jdbc/datasource"數據源資源,在應用程序中可以用如下的代碼去獲取

1 Context initCtx = new InitialContext();
2 Context envCtx = (Context) initCtx.lookup("java:comp/env");
3 dataSource = (DataSource)envCtx.lookup("jdbc/datasource");

  此種配置下,數據庫的驅動jar文件需放置在tomcat的lib下

  

4.2、配置Tomcat數據源

  1、在Web項目的WebRoot目錄下的META-INF目錄創建一個context.xml文件

  如下圖所示:

  

  2、在context.xml文件配置tomcat服務器的數據源

複製代碼
 1 <Context>
 2    <Resource 
 3        name="jdbc/datasource" 
 4        auth="Container"
 5        type="javax.sql.DataSource" 
 6        username="root" 
 7        password="XDP"
 8        driverClassName="com.mysql.jdbc.Driver" 
 9        url="jdbc:mysql://localhost:3306/jdbcstudy"
10        maxActive="8" 
11        maxIdle="4"/>
12 </Context>
複製代碼

  3、將數據庫的驅動jar文件需放置在tomcat的lib下

  

  4、在獲取數據庫連接的工具類(如jdbcUtils)的靜態代碼塊中獲取JNDI容器中的數據源

複製代碼
 1 package me.gacl.util;
 2 
 3 import java.sql.Connection;
 4 import java.sql.ResultSet;
 5 import java.sql.SQLException;
 6 import java.sql.Statement;
 7 import javax.naming.Context;
 8 import javax.naming.InitialContext;
 9 import javax.sql.DataSource;
10 
11 /**
12 * @ClassName: JdbcUtils_DBCP
13 * @Description: 數據庫連接工具類
14 * @author: 孤傲蒼狼
15 * @date: 2014-10-4 下午6:04:36
16 *
17 */ 
18 public class JdbcUtils_JNDI {
19     
20     private static DataSource ds = null;
21     //在靜態代碼塊中創建數據庫連接池
22     static{
23         try{
24              //初始化JNDI
25             Context initCtx = new InitialContext();
26              //得到JNDI容器
27             Context envCtx = (Context) initCtx.lookup("java:comp/env");
28              //從JNDI容器中檢索name爲jdbc/datasource的數據源
29             ds = (DataSource)envCtx.lookup("jdbc/datasource");
30         }catch (Exception e) {
31             throw new ExceptionInInitializerError(e);
32         }
33     }
34     
35     /**
36     * @Method: getConnection
37     * @Description: 從數據源中獲取數據庫連接
38     * @Anthor:孤傲蒼狼
39     * @return Connection
40     * @throws SQLException
41     */ 
42     public static Connection getConnection() throws SQLException{
43         //從數據源中獲取數據庫連接
44         return ds.getConnection();
45     }
46     
47     /**
48     * @Method: release
49     * @Description: 釋放資源,
50     * 釋放的資源包括Connection數據庫連接對象,負責執行SQL命令的Statement對象,存儲查詢結果的ResultSet對象
51     * @Anthor:孤傲蒼狼
52     *
53     * @param conn
54     * @param st
55     * @param rs
56     */ 
57     public static void release(Connection conn,Statement st,ResultSet rs){
58         if(rs!=null){
59             try{
60                 //關閉存儲查詢結果的ResultSet對象
61                 rs.close();
62             }catch (Exception e) {
63                 e.printStackTrace();
64             }
65             rs = null;
66         }
67         if(st!=null){
68             try{
69                 //關閉負責執行SQL命令的Statement對象
70                 st.close();
71             }catch (Exception e) {
72                 e.printStackTrace();
73             }
74         }
75         
76         if(conn!=null){
77             try{
78                 //將Connection連接對象還給數據庫連接池
79                 conn.close();
80             }catch (Exception e) {
81                 e.printStackTrace();
82             }
83         }
84     }
85 }
複製代碼

  寫一個Servlet測試JNDI數據源

複製代碼
 1 package me.gacl.test;
 2 
 3 import java.io.IOException;
 4 import java.sql.Connection;
 5 import java.sql.PreparedStatement;
 6 import java.sql.ResultSet;
 7 import javax.servlet.ServletException;
 8 import javax.servlet.http.HttpServlet;
 9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
11 import me.gacl.util.JdbcUtils_JNDI;
12 
13 public class JNDITest extends HttpServlet {
14 
15     public void doGet(HttpServletRequest request, HttpServletResponse response)
16             throws ServletException, IOException {
17         Connection conn = null;
18         PreparedStatement st = null;
19         ResultSet rs = null;
20         try{
21             //獲取數據庫連接
22             conn = JdbcUtils_JNDI.getConnection();
23             String sql = "insert into test1(name) values(?)";
24             st = conn.prepareStatement(sql);
25             st.setString(1, "gacl");
26             st.executeUpdate();
27             //獲取數據庫自動生成的主鍵
28             rs = st.getGeneratedKeys();
29             if(rs.next()){
30                 System.out.println(rs.getInt(1));
31             }
32         }catch (Exception e) {
33             e.printStackTrace();
34         }finally{
35             //釋放資源
36             JdbcUtils_JNDI.release(conn, st, rs);
37         }
38     }
39 
40     public void doPost(HttpServletRequest request, HttpServletResponse response)
41             throws ServletException, IOException {
42         doGet(request, response);
43     }
44 
45 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章