JDBC數(shù)據(jù)庫連接池怎么實現(xiàn)

本篇內(nèi)容介紹了“JDBC數(shù)據(jù)庫連接池 怎么實現(xiàn)”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

創(chuàng)新互聯(lián)建站成立于2013年,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目成都網(wǎng)站建設(shè)、做網(wǎng)站網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元修水做網(wǎng)站,已為上家服務(wù),為修水各地企業(yè)和個人服務(wù),聯(lián)系電話:13518219792

什么情況下使用連接池?

對于一個簡單的數(shù)據(jù)庫應(yīng)用,由于對于數(shù)據(jù)庫的訪問不是很頻繁。這時可以簡單地在需要訪問數(shù)據(jù)庫時,就新創(chuàng)建一個連接,用完后就關(guān)閉它,這樣做也不會帶來什么明顯的性能上的開銷。但是對于一個復(fù)雜的數(shù)據(jù)庫應(yīng)用,情況就完全不同了。頻繁的建立、關(guān)閉連接,會極大的減低系統(tǒng)的性能,因為對于連接的使用成了系統(tǒng)性能的瓶頸。

使用連接池的好處

  1. 連接復(fù)用。通過建立一個數(shù)據(jù)庫連接池以及一套連接使用管理策略,使得一個數(shù)據(jù)庫連接可以得到高效、安全的復(fù)用,避免了數(shù)據(jù)庫連接頻繁建立、關(guān)閉的開銷。

  2. 對于共享資源,有一個很著名的設(shè)計模式:資源池。該模式正是為了解決資源頻繁分配、釋放所造成的問題的。把該模式應(yīng)用到數(shù)據(jù)庫連接管理領(lǐng)域,就是建立一個數(shù)據(jù)庫連接池,提供一套高效的連接分配、使用策略,最終目標(biāo)是實現(xiàn)連接的高效、安全的復(fù)用。

連接池的實現(xiàn)

數(shù)據(jù)庫連接池的基本原理是在內(nèi)部對象池中維護一定數(shù)量的數(shù)據(jù)庫連接,并對外暴露數(shù)據(jù)庫連接獲取和返回方法。

外部使用者可通過 getConnection 方法獲取連接,使用完畢后再通過 close 方法將連接返回,注意此時連接并沒有關(guān)閉,而是由連接池管理器回收,并為下一次使用做好準(zhǔn)備。

Java 中有一個 DataSource 接口, 數(shù)據(jù)庫連接池就是 DataSource 的一個實現(xiàn)

下面我們自己實現(xiàn)一個數(shù)據(jù)庫連接池:

首先實現(xiàn) DataSource, 這里使用 BlockingQueue 作為池 (只保留了關(guān)鍵代碼)

import javax.sql.DataSource;import java.io.PrintWriter;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.sql.SQLFeatureNotSupportedException;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;import java.util.logging.Logger;public class MyDataSource implements DataSource {
    static {
        try {
            Class.forName("com.MySQL.jdbc.Driver");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //這里有個坑
    //MySQL用的是5.5的
    //驅(qū)動用的是最新的
    //連接的時候會報The server time zone value '?й???????'
    // is unrecognized or represents more than one time zone
    //解決方法:
    //1.在連接串中加入?serverTimezone=UTC
    //2.在mysql中設(shè)置時區(qū),默認(rèn)為SYSTEM
    //set global time_zone='+8:00'
    private String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC";
    private String user = "root";
    private String password = "123456";
    private BlockingQueue<Connection> pool = new ArrayBlockingQueue<>(3);
    public MyDataSource() {
        initPool();
    }
    private void initPool() {
        try {
            for (int i = 0; i < 3; i++) {
                pool.add(new MyConnection(
                        DriverManager.getConnection(url, user, password), this));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /*
    從池中獲取連接
     */
    @Override
    public synchronized Connection getConnection() throws SQLException {
        try {
            return pool.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        throw new RuntimeException("get connection failed!");
    }
    public BlockingQueue<Connection> getPool() {
        return pool;
    }
    public void setPool(BlockingQueue<Connection> pool) {
        this.pool = pool;
    }}

實現(xiàn)自己的連接, 對原生連接進行封裝, 調(diào)用 close 方法的時候?qū)⑦B接放回到池中

import java.sql.*; import java.util.Map; import java.util.Properties; importjava.util.concurrent.Executor; public class MyConnection implements Connection { //包裝的連接private Connection conn; private MyDataSource dataSource; public MyConnection(Connection conn, MyDataSource dataSource) { this.conn = conn; this.dataSource = dataSource; } @Overridepublic Statement createStatement() throws SQLException { return conn.createStatement(); }@Override public PreparedStatement prepareStatement(String sql) throws SQLException { returnconn.prepareStatement(sql); } @Override public boolean getAutoCommit() throws SQLException {return conn.getAutoCommit(); } @Override public void setAutoCommit(boolean autoCommit) throwsSQLException { conn.setAutoCommit(autoCommit); } @Override public void commit() throwsSQLException { conn.commit(); } @Override public void rollback() throws SQLException { conn.rollback(); } @Override public void close() throws SQLException { //解決重復(fù)關(guān)閉問題 if(!isClosed()) { dataSource.getPool().add(this); } } @Override public boolean isClosed()throws SQLException { return dataSource.getPool().contains(this); } }

main 方法

import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException;import java.sql.Statement; public class Main { public static void main(String[] args) { DataSource source = new MyDataSource(); try { Connection conn = source.getConnection(); Statement st = conn.createStatement(); st.execute("INSERT INTO USER (name,age) values('bob',12)"); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }

數(shù)據(jù)源連接池的原理及Tomcat中的應(yīng)用

在Java Web開發(fā)過程中,會廣泛使用到數(shù)據(jù)源。
我們基本的使用方式,是通過Spring使用類似如下的配置,來聲明一個數(shù)據(jù)源:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="maxActive" value="100" />
        <property name="maxIdle" value="20" />
        <property name="validationQuery" value="SELECT 1 from dual" />
        <property name="testOnBorrow" value="true" />
    </bean>

在之后應(yīng)用里對于數(shù)據(jù)庫的操作,都基于這個數(shù)據(jù)源,但這個數(shù)據(jù)源連接池的創(chuàng)建、銷毀、管理,對于用戶都是近乎透明的,甚至數(shù)據(jù)庫連接的獲取,我們都看不到Connection對象了。
這種方式是應(yīng)用自身的數(shù)據(jù)庫連接池,各個應(yīng)用之間互相獨立。

在類似于Tomcat這樣的應(yīng)用服務(wù)器內(nèi)部,也有提供數(shù)據(jù)源的能力,這時的數(shù)據(jù)源,可以為多個應(yīng)用提供服務(wù)。

這一點類似于以前寫過關(guān)于Tomcat內(nèi)部的Connector對于線程池的使用,可以各個Connector獨立使用線程池,也可以共用配置的Executor。( Tomcat的Connector組件 )

那么,在Tomcat中,怎么樣配置和使用數(shù)據(jù)源呢?

  1. 先將對應(yīng)要使用的數(shù)據(jù)庫的驅(qū)動文件xx.jar放到TOMCAT_HOME/lib目錄下。

  2. 編輯TOMCAT_HOME/conf/context.xml文件,增加類似于下面的內(nèi)容:

<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
          maxTotal="100" maxIdle="30" maxWaitMillis="10000"
          username="root" password="pwd" driverClassName="com.mysql.jdbc.Driver"
          url="jdbc:mysql://localhost:3306/test"/>
  1. 需要提供數(shù)據(jù)源的應(yīng)用內(nèi),使用JNDI的方式獲取

Context initContext = new InitialContext();Context envContext  = (Context)initContext.lookup("java:/comp/env");DataSource ds = (DataSource)envContext.lookup("jdbc/TestDB");Connection conn = ds.getConnection();
  1. 愉快的開始使用數(shù)據(jù)庫…

我們看,整個過程也并不比使用Spring等框架進行配置復(fù)雜,在應(yīng)用內(nèi)獲取連接也很容易。多個應(yīng)用都可以通過第3步的方式獲取數(shù)據(jù)源,這使得同時提供多個應(yīng)用共享數(shù)據(jù)源很容易。

這背后的是怎么實現(xiàn)的呢?

這個容器的連接池是怎么工作的呢,我們一起來看一看。

在根據(jù)context.xml中配置的Resouce初始化的時候,會調(diào)用具體DataSource對應(yīng)的實現(xiàn)類,Tomcat內(nèi)部默認(rèn)使用的BasicDataSource,在類初始化的時候,會執(zhí)行這樣一行代碼DriverManager.getDrivers(),其對應(yīng)的內(nèi)容如下,主要作用是使用 java.sql.DriverManager實現(xiàn)的Service Provider機制,所有jar文件包含META-INF/services/java.sql.Driver文件的,會被自動發(fā)現(xiàn)、加載和注冊,不需要在需要獲取連接的時候,再手動的加載和注冊。

public static java.util.Enumeration<Driver> getDrivers() {
        java.util.Vector<Driver> result = new java.util.Vector<>();
        for(DriverInfo aDriver : registeredDrivers) {
            if(isDriverAllowed(aDriver.driver, callerClass)) {
                result.addElement(aDriver.driver);
            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }
        }
        return (result.elements());
    }

之后DataSourceFactory會讀取Resouce中指定的數(shù)據(jù)源的屬性,創(chuàng)建數(shù)據(jù)源。

在我們的應(yīng)用內(nèi)getConnection的時候,使用ConnectionFactory創(chuàng)建Connection, 注意在創(chuàng)建Connection的時候,重點代碼是這個樣子:

public PooledObject<PoolableConnection> makeObject() throws Exception {
        Connection conn = _connFactory.createConnection();
        initializeConnection(conn);
        PoolableConnection pc = new PoolableConnection(conn,_pool, connJmxName);
        return new DefaultPooledObject<>(pc);

這里的_pool是GenericObjectPool,連接的獲取是通過其進行的。

public Connection getConnection() throws SQLException {
        C conn = _pool.borrowObject();
}

在整個pool中包含幾個隊列,其中比較關(guān)鍵的一個定義如下:

private final LinkedBlockingDeque<PooledObject<T>> idleObjects;

我們再看連接的關(guān)閉,

public void close() throws SQLException {
    if (getDelegateInternal() != null) {
        super.close();
        super.setDelegate(null);
    }
}

這里的關(guān)閉,并不會真的調(diào)用到Connection的close方法,我們通過上面的代碼已經(jīng)看到,Connection返回的時候,其實是Connection的Wrapper類。在close的時候,真實的會調(diào)用到下面的代碼

// Normal close: underlying connection is still open, so we
           // simply need to return this proxy to the pool
           try {
               _pool.returnObject(this);
           } catch(IllegalStateException e) {}

所謂的return,是把連接放回到上面我們提到的idleObjects隊列中。整個連接是放在一個LIFO的隊列中,所以如果沒有關(guān)閉或者超過最大空閑連接,就會加到隊列中。而允許外的連接才會真實的銷毀destory。

int maxIdleSave = getMaxIdle();
        if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) {
            try {
                destroy(p);
            } catch (Exception e) {
                swallowException(e);
            }
        } else {
            if (getLifo()) {
                idleObjects.addFirst(p); // 這里。
            } else {
                idleObjects.addLast(p);
            }
            if (isClosed()) {
                // Pool closed while object was being added to idle objects.
                // Make sure the returned object is destroyed rather than left                // in the idle object pool (which would effectively be a leak)
                clear();
            }
        }

“JDBC數(shù)據(jù)庫連接池 怎么實現(xiàn)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

網(wǎng)站標(biāo)題:JDBC數(shù)據(jù)庫連接池怎么實現(xiàn)
瀏覽地址:http://muchs.cn/article30/gppdpo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供標(biāo)簽優(yōu)化品牌網(wǎng)站設(shè)計、品牌網(wǎng)站制作虛擬主機、軟件開發(fā)企業(yè)網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站優(yōu)化排名