數據庫連接池——C3P0&Druid

數據庫連接池基本介紹



1. 基本概念

  數據庫連接池的本質上是一個容器(集合),存放數據庫連接的容器,當系統初始化好後,容器被創建,容器中會申請一些連接對象,當用戶來訪問數據庫時,從容器中獲取連接對象,用戶訪問完後,會將連接對象歸還給容器。百度百科介紹
  使用數據庫連接池可以:節約資源、提高訪問效率。



2. 數據源標準接口 —— javax.sql.DataSource

  DataSource 是一個連接物理數據源的工廠,用於連接到此 DataSource 對象表示的物理數據源 。

  除了用 DriverManager 對象獲取數據庫連接外,DataSource 對象是獲得數據庫連接的首選方法。

  DataSource 的 API 介紹


2.1 DataSource 的三種實現類型

實現類型 描述
基本實現 創建一個標準的 Connection 對象。
連接池實現 創建一個 Connection 對象,該對象將自動參與連接池。
分佈式事務實現 創建一個 Connection 可用於分佈式事務的對象

2.2 部分相關的方法及描述

方法名 描述
Connection getConnection() 嘗試與數據源建立連接,返回連接對象。
Connection getConnection​(String username, String password) 嘗試與數據源建立連接,返回連接對象。
void close() 歸還該 Connection 對象的數據庫 和 JDBC資源。
PrintWriter getLogWriter() 檢索此 DataSource 對象的日誌編寫器。


2.3 C3P0 數據庫連接池

  C3P0是一個開源的JDBC連接池,目前使用它的開源項目有Hibernate、Spring等。

  C3P0 百度百科

  C3P0 API 文檔


2.3.1 使用步驟

  1. 導入jar包

    • C3P0 jar包下載地址
    • mysql-connector-java-8.0.17.jar : 注意與自己裝的數據庫版本對應。
    • c3p0-0.9.5.2.jar
    • mchange-commons-java-0.2.11.jar
  2. 定義配置文件

    • 命名固定:c3p0.properties 或 c3p0-config.xml
    • 放置路徑:src 路徑下
    • 配置內容可以查看百度百科,後文會簡單介紹幾個。
  3. 創建核心對象

    • ComboPooledDataSource
  4. 獲取連接對象

    • 方法:getConnection
  5. 獲取連接對象後就可以進行其他JDBC相關的操作了,這裏不再演示。


2.3.2 配置文件

<?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_test?serverTimezone=UTC</property>
		<property name="user">root</property>
		<property name="password">root</property>

        <!--連接池參數-->
        <!--初始化申請的連接數量-->
		<property name="initialPoolSize">10</property>
        <!--最大的連接池數量-->
		<property name="maxPoolSize">10</property>
        <!--超時時間,單位爲毫秒-->
		<property name="checkoutTimeout">3000</property>
	</default-config>

	<!-- 命名配置 -->
	<named-config name="otherc3p0">
		<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql://localhost:3306/demo_test?serverTimezone=UTC</property>
		<property name="user">root</property>
		<property name="password">root</property>

		<property name="initialPoolSize">5</property>
		<property name="maxPoolSize">8</property>
		<property name="checkoutTimeout">3000</property>
	</named-config>
</c3p0-config>

注意數據庫參數配置:

  • mysql 5.0 的驅動路徑爲:com.mysql.jdbc.Driver
  • MySQL 8.0 的驅動路徑爲:com.mysql.cj.jdbc.Driver
  • MySQL 8.0 之後必須加 : ?serverTimezone=UTC
    • 如:jdbc:mysql://localhost:3306/demo_test?serverTimezone=UTC
    • 否則會報錯時區錯誤。

2.3.3 Java代碼基本演示:獲取連接對象並打印到控制檯

package com.base.datasource.c3p0;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class C3P0Demo1 {
    public static void main(String[] args) throws SQLException {
        //1.1 獲取DataSource,使用默認配置
        // DataSource ds = new ComboPooledDataSource();
        //1.2 獲取連接對象
        // Connection conn = ds.getConnection();
        //3. 打印
       // System.out.println(conn);

        // 2.1 獲取DataSource,使用指定名稱的配置
        DataSource ds = new ComboPooledDataSource("otherc3p0");
        for (int i=1;i<=10;i++){
            // 2.2 獲取連接對象
            Connection conn = ds.getConnection();
            // 2.2 打印
            System.out.println(i +" : "+ conn);
            if (i == 5){
                conn.close();
            }
        }
    }
}

運行結果:

1 : com.mchange.v2.c3p0.impl.NewProxyConnection@6a5fc7f7 [wrapping: com.mysql.cj.jdbc.ConnectionImpl@3b6eb2ec]
2 : com.mchange.v2.c3p0.impl.NewProxyConnection@6e8dacdf [wrapping: com.mysql.cj.jdbc.ConnectionImpl@7a79be86]
3 : com.mchange.v2.c3p0.impl.NewProxyConnection@b684286 [wrapping: com.mysql.cj.jdbc.ConnectionImpl@880ec60]
4 : com.mchange.v2.c3p0.impl.NewProxyConnection@7f63425a [wrapping: com.mysql.cj.jdbc.ConnectionImpl@36d64342]
5 : com.mchange.v2.c3p0.impl.NewProxyConnection@511baa65 [wrapping: com.mysql.cj.jdbc.ConnectionImpl@340f438e]
6 : com.mchange.v2.c3p0.impl.NewProxyConnection@19dfb72a [wrapping: com.mysql.cj.jdbc.ConnectionImpl@17c68925]
7 : com.mchange.v2.c3p0.impl.NewProxyConnection@3d24753a [wrapping: com.mysql.cj.jdbc.ConnectionImpl@340f438e]
8 : com.mchange.v2.c3p0.impl.NewProxyConnection@7a0ac6e3 [wrapping: com.mysql.cj.jdbc.ConnectionImpl@71be98f5]
9 : com.mchange.v2.c3p0.impl.NewProxyConnection@17f6480 [wrapping: com.mysql.cj.jdbc.ConnectionImpl@2d6e8792]

Exception in thread "main" java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.

注意:

  • 配置文件裏面配置的初始化創建的連接池對象是5個,最大連接池對象是8個。
  • 代碼展示中一共創建了10個連接對象,但是隻成功創建了9個連接對象,還剩一個出現超時異常(3秒左右)
  • 能夠創建9個連接對象的原因是,我們歸還了第五個連接池對象,這個連接池對象被第七個連接對象獲取了,通過後面的哈希值可以判斷出這兩個連接池對象是同一個。
  • 最後一個連接對象沒有創建成功的原因是,連接對象已經達到我們設定的最大連接數量 8個,如果沒有歸還的話,其他連接對象就不能被創建出來。


2.4 Druid 數據庫連接池

  Apache Druid 是一個分佈式內存實時分析系統,用於解決如何在大規模數據集下進行快速的、交互式的查詢和分析。

  阿里雲開元組件介紹——Druid


2.4.1 使用步驟

  1. 導入jar包

  2. 配置文件

    • 命名不固定。
    • 是 properties 形式的。
    • 可以放在任意路徑下。
  3. 加載配置文件

    • 類:Properties
    • 方法:
      • getClassLoader()
      • getResourceAsStream()
      • load()
  4. 獲取數據庫連接池對象

    • 通過工廠類獲取:DruidDataSourceFactory
  5. 獲取連接

    • 獲取方法:getConnection()

2.4.2 配置文件

  因爲命名沒有限制,這裏取名爲:druid.properties,內容如下:

# 數據庫連接參數信息
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/demo_test?serverTimezone=UTC
username=root
password=root

# 初始化連接數量
initialSize=5
# 最大連接數
maxActive=10
# 最大等待時間
maxWait=3000

2.4.3 Java代碼基本演示:獲取並打印連接對象

package com.base.datasource.druid;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;

/**
 * Druid連接池基本演示
 */
public class DruidDemo {
    public static void main(String[] args) throws Exception {
        //1. 加載配置文件,類加載器
        Properties pro = new Properties();
        InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
        pro.load(is);
        //2. 獲取連接池對象
        DataSource ds = DruidDataSourceFactory.createDataSource(pro);
        //3. 獲取連接對象
        Connection conn = ds.getConnection();
        //4. 打印連接對象
        System.out.println(conn);
    }
}

運行結果:

com.mysql.cj.jdbc.ConnectionImpl@62e136d3

2.4.4 Druid 工具類編寫

  1. 編寫工具類 JDBCUtils
package com.base.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JDBCUtils {
    //1. 定義成員變量 DataSource
    private static DataSource ds;

    static {
        try {
            //1. 加載配置文件
            Properties pro = new Properties();
            pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
            //2. 獲取 DataSource
            ds = DruidDataSourceFactory.createDataSource(pro);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 獲取連接
     * @return Connection 對象
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }

    /**
     * 釋放資源
     * @param stmt Statement對象
     * @param conn Connection對象
     */
    public static void close(Statement stmt, Connection conn) {

        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 釋放資源方法重載
     * @param rs ResultSet對象
     * @param stmt Statement 對象
     * @param conn Connection 對象
     */
    public static void close(ResultSet rs, Statement stmt, Connection conn) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 獲取連接池對象
     * @return ds 連接池對象
     */
    public static DataSource getDataSource() {
        return ds;
    }
}
  1. 使用工具類創建連接,並執行簡單數據庫操作。
package com.base.datasource.druid;

import com.base.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * 使用JDBC工具類
 * 完成添加操作,給account添加一條記錄。
 */

public class DruidDemo2 {
    public static void main(String[] args) throws Exception {
     
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            //1. 獲取連接
            conn = JDBCUtils.getConnection();
            //2. 定義 SQL
            String sql = "insert into account values(null,?,?)";
            //3. 獲取 pstmt 對象
            pstmt = conn.prepareStatement(sql);
            //4. 給佔位符(?)賦值
            pstmt.setString(1, "張三");
            pstmt.setDouble(2, 2000);
            //5. 執行 sql
            int count = pstmt.executeUpdate();
            System.out.println(count);

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 釋放資源
            JDBCUtils.close(pstmt, conn);
        }
    }
}

運行結果:

十月 31, 2019 4:24:03 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
1

注意:沒有數據庫和對應的表需要提前創建一個,下面提供SQL代碼。

-- 創建數據庫
CREATE DATABASE IF NOT EXISTS demo_test CHARACTER SET utf8 ;

USE demo_test;

CREATE TABLE account(
	id INT PRIMARY KEY AUTO_INCREMENT,
	name VARCHAR(32) UNIQUE NOT NULL,
	money VARCHAR(32) NOT NULL
);


時間:2019年10月31日15:59:13


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章