天天看點

JDBC連接配接池原理作用實作方式主要參數工作步驟自定義連接配接池DBCP連接配接池C3P0連接配接池☆Druid連接配接池

文章目錄

  • 原理
  • 作用
  • 實作方式
  • 主要參數
  • 工作步驟
  • 自定義連接配接池
    • 基本思想
    • 代碼實作
      • 1. 普通連接配接池
      • 2. 規範實作連接配接池
  • DBCP連接配接池
    • 需要的jar包
    • 代碼實作
  • C3P0連接配接池
    • 需要的jar包
    • 實作方式
    • 代碼實作
  • ☆Druid連接配接池
    • 特點
      • 1. 亞秒級查詢
      • 2. 實時資料注入
      • 3. 可擴充的PB級存儲(存儲量大)
      • 4. 多環境部署
      • 5. 豐富的社群
    • 需要的jar包
    • 代碼實作

JDBC操作中,連接配接資料庫是必備的,可以通過連接配接池提前建立資料庫連接配接,友善後續調用

原理

資料庫連接配接池負責配置設定、管理和釋放資料庫連接配接,它允許應用程式重複使用一個現有的資料庫連接配接,而不是再重建立立一個

第一次通路時需要建立連接配接,但是之後的通路均會複用之前建立的連接配接,直接執行SQL語句

作用

  1. 較少了網絡開銷
  2. 實作了系統性能實質的提升
  3. 無TIME_WAIT狀态

實作方式

  1. 自定義連接配接池
  2. DBCP連接配接池
  3. C3P0連接配接池
  4. Druid連接配接池

tips:

我們可以通過自定義的方式實作連接配接池,分析連接配接池類應該包含特定的屬性和方法

DBCP連接配接池、C3P0連接配接池、Druid連接配接池可以直接使用

主要參數

  • 初始化連接配接數

    連接配接池啟動時建立的初始化資料庫連接配接數量

  • 最小連接配接數

    連接配接池一直保持的資料庫連接配接

    如果應用程式對資料庫連接配接的使用量不大,将會大量浪費資料庫連接配接資源

  • 最大連接配接數

    連接配接池能申請的最大連接配接數

    如果資料庫連接配接請求超過次數,後面的資料庫連接配接請求将被加入到等待隊列中,這會影響以後的資料庫操作

  • 最大空閑時間/最大等待時間

    當沒有可用連接配接時,連接配接池等待連接配接被歸還的最大時間

    超過時間抛出異常,可設定參數為0或者負數使得無限等待(根據不同連接配接池配置)

  • 擷取連接配接逾時時間
  • 逾時重試連接配接次數

已經定義好的連接配接池的主要參數:

DBCP C3P0 Druid
初始化連接配接數 initialSize(0) initialPoolSize(3) initialSize(0)
最小連接配接數 minldle(0) minPoolSize(3) minldle(0)
最大連接配接數 maxTotal(8) maxPoolSize(15) maxActive(8)
最大等待時間 maxWaitMillis(毫秒) maxIdelTime(0秒) maxWait(毫秒)
  • tips

    DBCP連接配接池的配置中,有一個maxIdle屬性,表示最大空閑連接配接數,超過的空閑連接配接将被釋放,預設值為8

    maxIdle屬性在Druid連接配接池已不再使用,配置了也沒有效果

    c3p0連接配接池沒有maxIdle屬性

工作步驟

  1. 連接配接池的建立
  2. 連接配接池中連接配接的使用管理
  3. 連接配接池的關閉
  • tips

    1、資料庫連接配接池在初始化的時候會建立initialSize個連接配接,當有資料庫操作時,會從池中取出一個連接配接

    2、目前池中正在使用的連接配接數等于maxActive,則會等待一段時間,等待其他操作釋放掉某一個連接配接,如果這個等待時間超過了maxWait,則會報錯

    3、如果目前正在使用的連接配接數沒有達到maxActive,則判斷目前是否空閑連接配接,如果有則直接使用空閑連接配接,如果沒有則建立立一個連接配接

    4、連接配接使用完畢後,不是将其實體連接配接關閉,而是将其放入池中等待其他操作複用

自定義連接配接池

了解思路,代碼無需自己實作

基本思想

系統初始化時,将資料庫連接配接作為對象存儲在記憶體中,當使用者需要通路資料庫時,并非建立一個新的連接配接,而是從連接配接池中取出一個已建立的空閑連接配接對象

使用完畢後,使用者并非将連接配接關閉,而是将連接配接放回連接配接池中,以供下一個請求通路使用

連接配接的建立、斷開都由連接配接池自身來管理

↓↓↓↓↓↓

可以通過設定連接配接池的參數來控制連接配接池中的初始連接配接數、連接配接的上下限數以及每個連接配接的最大使用次數、最大空閑時間等,也可以通過其自身的管理機制來監視資料庫連接配接的數量、使用情況等

代碼實作

将連接配接資料庫的資訊放到集合中

屬性: 集合 放置Connection

方法:擷取連接配接方法、回收連接配接方法

1. 普通連接配接池

  • 定義集合,通過list鍊式存儲使得資料有序
  • 靜态代碼塊,初始化10個連接配接
static{
        for (int i = 0; i < 10; i++) {
            //JDBCUtils類是自定義類,封裝了連接配接資料庫的資訊代碼
            Connection connection = JDBCUtils.newInstance().getConnection();
            list.add(connection);
        }
    }

           
  • 從連接配接池子中擷取連接配接的方式
public static Connection getConnection(){
        if (list.isEmpty()) {
            //JDBCUtils類是自定義類,封裝了連接配接資料庫的資訊代碼
            Connection connection = JDBCUtils.newInstance().getConnection();
            list.addLast(connection);
        }
        Connection conn = list.removeFirst();//取出第一個
        return conn;
    }
           
  • 傳回到連接配接池子中
public static void addBack(Connection conn){
        if (list.size() >= 10) {//超過連接配接數
            try {
                conn.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }else{
            list.addLast(conn); //添加
        }
    }
           
  • 擷取連接配接池子中連接配接數量
public static int getSize(){
        return list.size();
    }
           

2. 規範實作連接配接池

Java為連接配接池實作提供了一個規範(接口):DataSource接口

實作DataSource接口沒有提供回收連結方法,是以使用裝飾者模式

  • 将被裝飾者導入
public class MyConnection implements Connection {
    private Connection conn;
    private LinkedList<Connection> list;
    public MyConnection(Connection conn, LinkedList<Connection> list) {
        super();
        this.conn = conn;
        this.list = list;
    }
    
    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return conn.unwrap(iface);
    }
    
    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return conn.isWrapperFor(iface);
    }
    
    @Override
    public Statement createStatement() throws SQLException {
        return conn.createStatement();
    }
    
    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return conn.prepareStatement(sql);
    }
    
    @Override
    public CallableStatement prepareCall(String sql) throws SQLException {
        return null;
    }
    
    @Override
    public String nativeSQL(String sql) throws SQLException {
        return null;
    }
    
    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {
    }
    
    @Override
    public boolean getAutoCommit() throws SQLException {
        return false;
    }
    
    @Override
    public void commit() throws SQLException {
        conn.commit();
    }
    
    @Override
    public void rollback() throws SQLException {
        conn.rollback();
    }
    
    @Override
    public void close() throws SQLException {
        list.addLast(conn);
    }
    
}
           
  • 建立基于規範實作的連接配接池
public class DataSourcePool implements DataSource {
    static LinkedList<Connection> list = new LinkedList<Connection>();
    
    static{
        for (int i = 0; i < 10; i++) {
            Connection connection = JDBCUtils.newInstance().getConnection();
            list.add(connection);
        }
    }
    
    public static int getSize(){
        return list.size();
    }
    
    @Override
    public Connection getConnection() throws SQLException {
        Connection conn = list.removeFirst();
        MyConnection conn1 = new MyConnection(conn, list);
        return conn1;
    }
    
    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }
    
    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
    }
    
    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
    }
    
    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }
    
    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
    
    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }
    
    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
    
    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }
    
}
           

DBCP連接配接池

需要的jar包

mysql-jdbc.jar

commons-dbcp.jar

commons-pool.jar

DBCP是一個依賴Jakarta commons-pool對象池機制的資料庫連接配接池

DBCP可以直接的在應用程式中使用,Tomcat的資料源使用的為DBCP

代碼實作

以DBUtil類中封裝的方法為例:

//定義需要的工具類對象(變量定義)
    protected Connection connection = null;
    protected PreparedStatement pps = null;//後續都是用預狀态通道來實作
    protected ResultSet rs = null;//結果集
    protected int count = 0;//受影響的行數

    //登入的使用者名和密碼
    private static String username;
    private static String password;
    private static String url;
    private static String driverName;
    private static BasicDataSource basicDataSource = new BasicDataSource();

    //加載驅動
    static {
        /*
        try {
            ResourceBundle bundle = ResourceBundle.getBundle("db");//參數隻寫屬性檔案名,不需要寫字尾
            driverName = bundle.getString("driver");
            url = bundle.getString("url");
            username = bundle.getString("username");
            password = bundle.getString("password");

            Class.forName(driverName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        */

        //DBCP
        ResourceBundle bundle = ResourceBundle.getBundle("db");//參數隻寫屬性檔案名,不需要寫字尾
        driverName = bundle.getString("driver");
        url = bundle.getString("url");
        username = bundle.getString("username");
        password = bundle.getString("password");

        basicDataSource.setUsername(username);
        basicDataSource.setPassword(password);
        basicDataSource.setUrl(url);
        basicDataSource.setDriverClassName(driverName);

        //設定初始大小,初始化20個連接配接
        basicDataSource.setInitialSize(20);
    }


    //獲得連接配接
    protected Connection getConnection() {
        try {
        	/*
            connection = DriverManager.getConnection(url, username, password);
            */
            connection = basicDataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
           

C3P0連接配接池

c3p0是開源的JDBC連接配接池,在lib目錄中與Hibernate一起釋出,包括了實作jdbc3和jdbc2擴充規範說明的Connection和Statement池的DataSources對象

c3p0與dbcp差別:

c3p0 dbcp
有自動回收空閑連接配接功能 沒有自動回收空閑連接配接的功能
不需要手動設定 需要手動設定配置檔案

需要的jar包

c3p0-0.9.1.2.jar

mysql-connector-java-5.0.8.jar

實作方式

1、手動設定工具類ComboPooledDataSource

2、加載配置檔案

在src下建立檔案c3p0-config.xml

<?xml version="1.0" encoding="utf-8"?>
<c3p0-config>
    <!-- 預設配置,如果沒有指定則使用這個配置 -->
    <default-config>
        <!-- 基本配置 -->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT</property>
        <property name="user">root</property>
        <property name="password">123456</property>
        <!--擴充配置-->
        <!-- 連接配接超過30秒報錯-->
        <property name="checkoutTimeout">30000</property>
        <!--30秒檢查空閑連接配接 -->
        <property name="idleConnectionTestPeriod">30</property>
        <property name="initialPoolSize">10</property>
        <!-- 30秒不适用丢棄-->
        <property name="maxIdleTime">30</property>
        <property name="maxPoolSize">100</property>
        <property name="minPoolSize">10</property>
        <property name="maxStatements">200</property>
    </default-config>
</c3p0-config>
           

代碼實作

  • 建立對象
//定義需要的工具類對象(變量定義)
    protected Connection connection = null;
    protected PreparedStatement pps = null;//後續都是用預狀态通道來實作
    protected ResultSet rs = null;//結果集
    protected int count = 0;//受影響的行數

    //登入的使用者名和密碼
    private static String username;
    private static String password;
    private static String url;
    private static String driverName;
    
    //C3PO
    private static ComboPooledDataSource comboPooledDataSource =new ComboPooledDataSource();
           
  • 獲得連接配接
protected Connection getConnection() {
        try {
			//connection = DriverManager.getConnection(url, username, password);
			//connection = basicDataSource.getConnection();
            connection = comboPooledDataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
           

☆Druid連接配接池

德魯伊連接配接池,是目前比較流行的高性能的、分布式列存儲的OLAP架構(具體來說MOLAP)

阿裡出品,淘寶和支付寶專用資料庫連接配接池

Druid不僅僅是資料庫連接配接池,還包含一個ProxyDriver(代理驅動)、一系列内置的JDBC元件庫、一個SQL Parser(sql解析器),支援所有JDBC相容的資料庫,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等等

通過Druid提供的SQL Parser可以在JDBC層攔截SQL做相應處理,比如分庫分表、審計等,Druid防禦SQL注入攻擊的WallFilter就是通過Druid的SQL Parser分析語義實作的。

特點

1. 亞秒級查詢

Druid提供了快速的聚合能力以及亞秒級的OLAP查詢能力,多租戶的設計,是面向使用者分析應用的理想方式

2. 實時資料注入

Druid支援流資料的注入,并提供了資料的事件驅動,保證在實時和離線環境下事件的實效性和統一性

3. 可擴充的PB級存儲(存儲量大)

Druid叢集可以很友善的擴容到PB的資料量,每秒百萬級别的資料注入,即使在加大資料規模的情況下,也能保證時其效性

4. 多環境部署

Druid既可以運作在商業的硬體上,也可以運作在雲上,可以從多種資料系統中注入資料,包括hadoop、spark、kafka、storm、samza等

5. 豐富的社群

druid擁有豐富的社群,供交流學習

需要的jar包

druid-1.0.9.jar

代碼實作

tips:

在Druid連接配接池的配置中,driverClassName可配可不配,如果不配置會根據url自動識别dbType(資料庫類型),然後選擇相應的driverClassName

建立對象

protected Connection connection = null;
    protected PreparedStatement pps = null;//後續都是用預狀态通道來實作
    protected ResultSet rs = null;//結果集
    protected int count = 0;//受影響的行數

    //登入的使用者名和密碼
    private static String username;
    private static String password;
    private static String url;
    private static String driverName;
    //DBCP
    //private static BasicDataSource basicDataSource = new BasicDataSource();
    //C3PO
    //private static ComboPooledDataSource comboPooledDataSource =new ComboPooledDataSource();
    
    //Druid
    private static DruidDataSource druidDataSource = new DruidDataSource();

           

加載驅動

static {
        /*
        try {

//            優化方法一:
//            InputStream inputStream = DBUtils.class.getClassLoader().getResourceAsStream("db.properties");
//            Properties properties = new Properties();
//            properties.load(inputStream);
//            driverName = properties.getProperty("driver");
//            url = properties.getProperty("url");
//            username = properties.getProperty("username");
//            password = properties.getProperty("password");

            ResourceBundle bundle = ResourceBundle.getBundle("db");//參數隻寫屬性檔案名,不需要寫字尾
            driverName = bundle.getString("driver");
            url = bundle.getString("url");
            username = bundle.getString("username");
            password = bundle.getString("password");

            Class.forName(driverName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        */


        //DBCP
        /*
        ResourceBundle bundle = ResourceBundle.getBundle("db");//參數隻寫屬性檔案名,不需要寫字尾
        driverName = bundle.getString("driver");
        url = bundle.getString("url");
        username = bundle.getString("username");
        password = bundle.getString("password");

        basicDataSource.setUsername(username);
        basicDataSource.setPassword(password);
        basicDataSource.setUrl(url);
        basicDataSource.setDriverClassName(driverName);

        //設定初始大小,初始化20個連接配接
        basicDataSource.setInitialSize(20);
        */

        //C3P0

        //Druid
        ResourceBundle bundle = ResourceBundle.getBundle("db");//參數隻寫屬性檔案名,不需要寫字尾
        //加載屬性檔案
        driverName = bundle.getString("driver");
        url = bundle.getString("url");
        username = bundle.getString("username");
        password = bundle.getString("password");
        
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        druidDataSource.setUrl(url);
        druidDataSource.setDriverClassName(driverName);
    }
           

獲得連接配接

protected Connection getConnection() {
        try {
//            connection = DriverManager.getConnection(url, username, password);
//            connection = basicDataSource.getConnection();
//            connection = comboPooledDataSource.getConnection();
            connection = druidDataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }