天天看點

深入分析JavaWeb 32 -- 資料庫連接配接池

一、應用程式直接擷取資料庫連接配接的缺點

  使用者每次請求都需要向資料庫獲得連結,而資料庫建立連接配接通常需要消耗相對較大的資源,建立時間也較長。假設網站一天10萬通路量,資料庫伺服器就需要建立10萬次連接配接,極大的浪費資料庫的資源,并且極易造成資料庫伺服器記憶體溢出、拓機。如下圖所示:

  

深入分析JavaWeb 32 -- 資料庫連接配接池

二、使用資料庫連接配接池優化程式性能

2.1、資料庫連接配接池的基本概念

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

  

深入分析JavaWeb 32 -- 資料庫連接配接池

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

資料庫連接配接池的最小連接配接數和最大連接配接數的設定要考慮到以下幾個因素:

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

2.2、編寫資料庫連接配接池

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

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

      

      實作DataSource接口,并實作連接配接池功能的步驟:

  • 在DataSource構造函數中批量建立與資料庫的連接配接,并把建立的連接配接加入LinkedList對象中。
  • 實作getConnection方法,讓getConnection方法每次調用時,從LinkedList中取一個Connection傳回給使用者。
  • 當使用者使用完Connection,調用Connection.close()方法時,Collection對象應保證将自己傳回到LinkedList中,而不要把conn還給資料庫。Collection保證将自己傳回到LinkedList中是此處程式設計的難點。

    資料庫連接配接池核心代碼

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

proxyConn = (Connection) Proxy.newProxyInstance(this.getClass()
            .getClassLoader(), conn.getClass().getInterfaces(),
            new InvocationHandler() {
        //此處為内部類,當close方法被調用時将conn還回池中,其它方法直接執行
            public Object invoke(Object proxy, Method method,
                      Object[] args) throws Throwable {
                if (method.getName().equals("close")) {
                    pool.addLast(conn);
                    return null;
            }
            return method.invoke(conn, args);
        }
    });
           

資料庫連接配接池編寫範例:

package me.gacl.demo;

import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.Properties;
import javax.sql.DataSource;

/**
* @ClassName: JdbcPool
* @Description:編寫資料庫連接配接池
* @author: 孤傲蒼狼
* @date: 2014-9-30 下午11:07:23
*
*/ 
public class JdbcPool implements DataSource{

    /**
    * @Field: listConnections
    *         使用LinkedList集合來存放資料庫連結,
    *        由于要頻繁讀寫List集合,是以這裡使用LinkedList存儲資料庫連接配接比較合适
    */ 
    private static LinkedList<Connection> listConnections = new LinkedList<Connection>();

    static{
        //在靜态代碼塊中加載db.properties資料庫配置檔案
        InputStream in = JdbcPool.class.getClassLoader().getResourceAsStream("db.properties");
        Properties prop = new Properties();
        try {
            prop.load(in);
            String driver = prop.getProperty("driver");
            String url = prop.getProperty("url");
            String username = prop.getProperty("username");
            String password = prop.getProperty("password");
            //資料庫連接配接池的初始化連接配接數大小
            int jdbcPoolInitSize =Integer.parseInt(prop.getProperty("jdbcPoolInitSize"));
            //加載資料庫驅動
            Class.forName(driver);
            for (int i = ; i < jdbcPoolInitSize; i++) {
                Connection conn = DriverManager.getConnection(url, username, password);
                System.out.println("擷取到了連結" + conn);
                //将擷取到的資料庫連接配接加入到listConnections集合中,listConnections集合此時就是一個存放了資料庫連接配接的連接配接池
                listConnections.add(conn);
            }

        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        // TODO Auto-generated method stub
        return ;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    /* 擷取資料庫連接配接
     * @see javax.sql.DataSource#getConnection()
     */
    @Override
    public Connection getConnection() throws SQLException {
        //如果資料庫連接配接池中的連接配接對象的個數大于0
        if (listConnections.size()>) {
            //從listConnections集合中擷取一個資料庫連接配接
            final Connection conn = listConnections.removeFirst();
            System.out.println("listConnections資料庫連接配接池大小是" + listConnections.size());
            //傳回Connection對象的代理對象
            return (Connection) Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler(){
                @Override
                public Object invoke(Object proxy, Method method, Object[] args)
                        throws Throwable {
                    if(!method.getName().equals("close")){
                        return method.invoke(conn, args);
                    }else{
                        //如果調用的是Connection對象的close方法,就把conn還給資料庫連接配接池
                        listConnections.add(conn);
                        System.out.println(conn + "被還給listConnections資料庫連接配接池了!!");
                        System.out.println("listConnections資料庫連接配接池大小為" + listConnections.size());
                        return null;
                    }
                }
            });
        }else {
            throw new RuntimeException("對不起,資料庫忙");
        }
    }

    @Override
    public Connection getConnection(String username, String password)
            throws SQLException {
        return null;
    }
}
           

db.properties配置檔案如下:

driver=com.mysql.jdbc.Driver
 url=jdbc:mysql://localhost:/jdbcStudy
 username=root
 password=XDP

 jdbcPoolInitSize=
           

寫一個JdbcUtil測試資料庫連接配接池

package me.gacl.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import me.gacl.demo.JdbcPool;

public class JdbcUtil {

    /**
    * @Field: pool
    *          資料庫連接配接池
    */ 
    private static JdbcPool pool = new JdbcPool();

    /**
    * @Method: getConnection
    * @Description: 從資料庫連接配接池中擷取資料庫連接配接對象
    * @Anthor:孤傲蒼狼
    * @return Connection資料庫連接配接對象
    * @throws SQLException
    */ 
    public static Connection getConnection() throws SQLException{
        return pool.getConnection();
    }

    /**
    * @Method: release
    * @Description: 釋放資源,
    * 釋放的資源包括Connection資料庫連接配接對象,負責執行SQL指令的Statement對象,存儲查詢結果的ResultSet對象
    * @Anthor:孤傲蒼狼
    *
    * @param conn
    * @param st
    * @param rs
    */ 
    public static void release(Connection conn,Statement st,ResultSet rs){
        if(rs!=null){
            try{
                //關閉存儲查詢結果的ResultSet對象
                rs.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if(st!=null){
            try{
                //關閉負責執行SQL指令的Statement對象
                st.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }

        if(conn!=null){
            try{
                //關閉Connection資料庫連接配接對象
                conn.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
           

三、開源資料庫連接配接池

  現在很多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的配置資訊如下:

#連接配接設定
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy
username=root
password=XDP





           

# <!– 初始化連接配接 –> initialSize=10 #最大連接配接數量 maxActive=50 # <!– 最大空閑連接配接 –> maxIdle=20 # <!– 最小空閑連接配接 –> minIdle=5 # <!– 逾時等待時間以毫秒為機關 6000毫秒/1000等于60秒 –> maxWait=60000 #JDBC驅動建立連接配接時附帶的連接配接屬性屬性的格式必須為這樣:[屬性名=property;] #注意:”user” 與 “password” 兩個屬性會被明确地傳遞,是以這裡不需要包含他們。 connectionProperties=useUnicode=true;characterEncoding=UTF8 #指定由連接配接池所建立的連接配接的自動送出(auto-commit)狀态。 defaultAutoCommit=true #driver default 指定由連接配接池所建立的連接配接的隻讀(read-only)狀态。 #如果沒有設定該值,則“setReadOnly”方法将不被調用。(某些驅動并不支援隻讀模式,如:Informix) defaultReadOnly= #driver default 指定由連接配接池所建立的連接配接的事務級别(TransactionIsolation)。 #可用值為下列之一:(詳情可見javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE defaultTransactionIsolation=READ_UNCOMMITTED

  如下圖所示:

  

深入分析JavaWeb 32 -- 資料庫連接配接池

  3、在擷取資料庫連接配接的工具類(如jdbcUtils)的靜态代碼塊中建立池

package me.gacl.util;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;

/**
* @ClassName: JdbcUtils_DBCP
* @Description: 資料庫連接配接工具類
* @author: 孤傲蒼狼
* @date: 2014-10-4 下午6:04:36
*
*/ 
public class JdbcUtils_DBCP {
    /**
     * 在java中,編寫資料庫連接配接池需實作java.sql.DataSource接口,每一種資料庫連接配接池都是DataSource接口的實作
     * DBCP連接配接池就是java.sql.DataSource接口的一個具體實作
     */
    private static DataSource ds = null;
    //在靜态代碼塊中建立資料庫連接配接池
    static{
        try{
            //加載dbcpconfig.properties配置檔案
            InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
            Properties prop = new Properties();
            prop.load(in);
            //建立資料源
            ds = BasicDataSourceFactory.createDataSource(prop);
        }catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
    * @Method: getConnection
    * @Description: 從資料源中擷取資料庫連接配接
    * @Anthor:孤傲蒼狼
    * @return Connection
    * @throws SQLException
    */ 
    public static Connection getConnection() throws SQLException{
        //從資料源中擷取資料庫連接配接
        return ds.getConnection();
    }

    /**
    * @Method: release
    * @Description: 釋放資源,
    * 釋放的資源包括Connection資料庫連接配接對象,負責執行SQL指令的Statement對象,存儲查詢結果的ResultSet對象
    * @Anthor:孤傲蒼狼
    *
    * @param conn
    * @param st
    * @param rs
    */ 
    public static void release(Connection conn,Statement st,ResultSet rs){
        if(rs!=null){
            try{
                //關閉存儲查詢結果的ResultSet對象
                rs.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if(st!=null){
            try{
                //關閉負責執行SQL指令的Statement對象
                st.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }

        if(conn!=null){
            try{
                //将Connection連接配接對象還給資料庫連接配接池
                conn.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
           

  測試DBCP資料源

package me.gacl.test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import org.junit.Test;
import me.gacl.util.JdbcUtils_DBCP;

public class DataSourceTest {

    @Test
    public void dbcpDataSourceTest() {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            //擷取資料庫連接配接
            conn = JdbcUtils_DBCP.getConnection();
            String sql = "insert into test1(name) values(?)";
            st = conn.prepareStatement(sql);
            st.setString(, "gacl");
            st.executeUpdate();
            //擷取資料庫自動生成的主鍵
            rs = st.getGeneratedKeys();
            if(rs.next()){
                System.out.println(rs.getInt());
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally{
            //釋放資源
            JdbcUtils_DBCP.release(conn, st, rs);
        }
    }
}
           

3.3、C3P0資料源

  C3P0是一個開源的JDBC連接配接池,它實作了資料源和JNDI綁定,支援JDBC3規範和JDBC2的标準擴充。目前使用它的開源項目有Hibernate,Spring等。C3P0資料源在項目開發中使用得比較多。

c3p0與dbcp差別

  

- dbcp沒有自動回收空閑連接配接的功能

- 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的配置資訊如下:

<?xml version="1.0" encoding="UTF-8"?>
<!--
c3p0-config.xml必須位于類路徑下面
private static ComboPooledDataSource ds;
static{
    try {
        ds = new ComboPooledDataSource("MySQL");
    } catch (Exception e) {
        throw new ExceptionInInitializerError(e);
    }
}
-->

<c3p0-config>
    <!--
    C3P0的預設(預設)配置,
    如果在代碼中“ComboPooledDataSource ds = new ComboPooledDataSource();”這樣寫就表示使用的是C3P0的預設(預設)配置資訊來建立資料源
    -->
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy</property>
        <property name="user">root</property>
        <property name="password">XDP</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </default-config>

    <!--
    C3P0的命名配置,
    如果在代碼中“ComboPooledDataSource ds = new ComboPooledDataSource("MySQL");”這樣寫就表示使用的是name是MySQL的配置資訊來建立資料源
    -->
    <named-config name="MySQL">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy</property>
        <property name="user">root</property>
        <property name="password">XDP</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>

</c3p0-config>
           

  如下圖所示:

  

深入分析JavaWeb 32 -- 資料庫連接配接池

  3、在擷取資料庫連接配接的工具類(如jdbcUtils)的靜态代碼塊中建立池

package me.gacl.util;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
* @ClassName: JdbcUtils_C3P0
* @Description: 資料庫連接配接工具類
* @author: 孤傲蒼狼
* @date: 2014-10-4 下午6:04:36
*
*/ 
public class JdbcUtils_C3P0 {

    private static ComboPooledDataSource ds = null;
    //在靜态代碼塊中建立資料庫連接配接池
    static{
        try{
            //通過代碼建立C3P0資料庫連接配接池
            /*ds = new ComboPooledDataSource();
            ds.setDriverClass("com.mysql.jdbc.Driver");
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbcstudy");
            ds.setUser("root");
            ds.setPassword("XDP");
            ds.setInitialPoolSize(10);
            ds.setMinPoolSize(5);
            ds.setMaxPoolSize(20);*/

            //通過讀取C3P0的xml配置檔案建立資料源,C3P0的xml配置檔案c3p0-config.xml必須放在src目錄下
            //ds = new ComboPooledDataSource();//使用C3P0的預設配置來建立資料源
            ds = new ComboPooledDataSource("MySQL");//使用C3P0的命名配置來建立資料源

        }catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
    * @Method: getConnection
    * @Description: 從資料源中擷取資料庫連接配接
    * @Anthor:孤傲蒼狼
    * @return Connection
    * @throws SQLException
    */ 
    public static Connection getConnection() throws SQLException{
        //從資料源中擷取資料庫連接配接
        return ds.getConnection();
    }

    /**
    * @Method: release
    * @Description: 釋放資源,
    * 釋放的資源包括Connection資料庫連接配接對象,負責執行SQL指令的Statement對象,存儲查詢結果的ResultSet對象
    * @Anthor:孤傲蒼狼
    *
    * @param conn
    * @param st
    * @param rs
    */ 
    public static void release(Connection conn,Statement st,ResultSet rs){
        if(rs!=null){
            try{
                //關閉存儲查詢結果的ResultSet對象
                rs.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if(st!=null){
            try{
                //關閉負責執行SQL指令的Statement對象
                st.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }

        if(conn!=null){
            try{
                //将Connection連接配接對象還給資料庫連接配接池
                conn.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
           

  測試C3P0資料源

package me.gacl.test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import org.junit.Test;
import me.gacl.util.JdbcUtils_C3P0;
import me.gacl.util.JdbcUtils_DBCP;

public class DataSourceTest {

    @Test
    public void c3p0DataSourceTest() {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            //擷取資料庫連接配接
            conn = JdbcUtils_C3P0.getConnection();
            String sql = "insert into test1(name) values(?)";
            st = conn.prepareStatement(sql);
            st.setString(, "gacl");
            st.executeUpdate();
            //擷取資料庫自動生成的主鍵
            rs = st.getGeneratedKeys();
            if(rs.next()){
                System.out.println(rs.getInt());
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally{
            //釋放資源
            JdbcUtils_C3P0.release(conn, st, rs);
        }
    }
}
           

四、配置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伺服器的資料源:

<Context>
  <Resource name="jdbc/datasource" auth="Container"
            type="javax.sql.DataSource" username="root" password="XDP"
            driverClassName="com.mysql.jdbc.Driver" 
            url="jdbc:mysql://localhost:3306/jdbcstudy"
            maxActive="8" maxIdle="4"/>
</Context>
           

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

  對于上面的name=”jdbc/datasource”資料源資源,在應用程式中可以用如下的代碼去擷取

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

  此種配置下,資料庫的驅動jar檔案需放置在tomcat的lib下

  

深入分析JavaWeb 32 -- 資料庫連接配接池

4.2、配置Tomcat資料源

  1、在Web項目的WebRoot目錄下的META-INF目錄建立一個context.xml檔案

  如下圖所示:

  

深入分析JavaWeb 32 -- 資料庫連接配接池

  2、在context.xml檔案配置tomcat伺服器的資料源

<Context>
   <Resource 
       name="jdbc/datasource" 
       auth="Container"
       type="javax.sql.DataSource" 
       username="root" 
       password="XDP"
       driverClassName="com.mysql.jdbc.Driver" 
       url="jdbc:mysql://localhost:3306/jdbcstudy"
       maxActive="8" 
       maxIdle="4"/>
</Context>
           

  3、将資料庫的驅動jar檔案需放置在tomcat的lib下

  

深入分析JavaWeb 32 -- 資料庫連接配接池

  4、在擷取資料庫連接配接的工具類(如jdbcUtils)的靜态代碼塊中擷取JNDI容器中的資料源

package me.gacl.util;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

/**
* @ClassName: JdbcUtils_DBCP
* @Description: 資料庫連接配接工具類
* @author: 孤傲蒼狼
* @date: 2014-10-4 下午6:04:36
*
*/ 
public class JdbcUtils_JNDI {

    private static DataSource ds = null;
    //在靜态代碼塊中建立資料庫連接配接池
    static{
        try{
             //初始化JNDI
            Context initCtx = new InitialContext();
             //得到JNDI容器
            Context envCtx = (Context) initCtx.lookup("java:comp/env");
             //從JNDI容器中檢索name為jdbc/datasource的資料源
            ds = (DataSource)envCtx.lookup("jdbc/datasource");
        }catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
    * @Method: getConnection
    * @Description: 從資料源中擷取資料庫連接配接
    * @Anthor:孤傲蒼狼
    * @return Connection
    * @throws SQLException
    */ 
    public static Connection getConnection() throws SQLException{
        //從資料源中擷取資料庫連接配接
        return ds.getConnection();
    }

    /**
    * @Method: release
    * @Description: 釋放資源,
    * 釋放的資源包括Connection資料庫連接配接對象,負責執行SQL指令的Statement對象,存儲查詢結果的ResultSet對象
    * @Anthor:孤傲蒼狼
    *
    * @param conn
    * @param st
    * @param rs
    */ 
    public static void release(Connection conn,Statement st,ResultSet rs){
        if(rs!=null){
            try{
                //關閉存儲查詢結果的ResultSet對象
                rs.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if(st!=null){
            try{
                //關閉負責執行SQL指令的Statement對象
                st.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }

        if(conn!=null){
            try{
                //将Connection連接配接對象還給資料庫連接配接池
                conn.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
           

  寫一個Servlet測試JNDI資料源

package me.gacl.test;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import me.gacl.util.JdbcUtils_JNDI;

public class JNDITest extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            //擷取資料庫連接配接
            conn = JdbcUtils_JNDI.getConnection();
            String sql = "insert into test1(name) values(?)";
            st = conn.prepareStatement(sql);
            st.setString(, "gacl");
            st.executeUpdate();
            //擷取資料庫自動生成的主鍵
            rs = st.getGeneratedKeys();
            if(rs.next()){
                System.out.println(rs.getInt());
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally{
            //釋放資源
            JdbcUtils_JNDI.release(conn, st, rs);
        }
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}