1. 程式人生 > 實用技巧 >【JDBC核心】資料庫連線池

【JDBC核心】資料庫連線池

資料庫連線池

傳統模式

使用資料庫的傳統模式:

  • 在主程式(servlet、beans等)中建立資料庫連線;
  • 進行 SQL 操作;
  • 斷開資料庫連線。

這種模式存在的問題:

  • JDBC 連線資料庫的方式(四個步驟:載入配置、讀取配置、載入驅動、獲取連線),會消耗大量的資源和時間,且連線資源沒有得到很好的重複利用;
  • 獲取一次資料庫連線,使用完成後都得關閉連線;
  • 不能控制被建立的連線物件數。

資料庫連線池技術

基本思想:為資料庫連線建立一個“緩衝池”,預先在緩衝池中放入一定數量的連線,當需要建立資料庫連線是,只需要從“緩衝池”中取出一個,使用完畢之後再放回去。

資料庫連線池負責分配、管理和釋放資料庫連線,它允許應用程式重複利用一個現有的資料庫連線,而不是重新建立一個。

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

資料庫連線池的優點:

  • 資源重用;
  • 更快的響應速度和更少的資源消耗;
  • 統一的連線管理,避免資料庫連線洩露。

開源的資料庫連線池

JDBC 的資料庫連線池使用 javax.sql.DataSource 來表示,DataSource 只是一個介面,該介面通常由伺服器(Weblogic, WebSphere, Tomcat)提供實現,也有一些開源組織

提供實現。

DataSource 通常被稱為資料來源,它包含連線池和連線池管理兩個部分。

DataSource 用來取代 DriverManager 來獲取 Connection,獲取速度快,同時可以大幅度提高資料庫訪問速度。

注意

  • 資料來源和資料庫連線不同,資料來源無需建立多個,它是產生資料庫連線的工廠,因此整個應用只需要一個數據源即可
  • 當資料庫訪問結束後,程式還是像以前一樣關閉資料庫連線:connnection.close(),但它並沒有關閉資料庫的物理連線,它僅僅把資料庫連線釋放,歸還給了資料庫連線池。

開源的資料庫連線池:

  • DBCP 是Apache提供的資料庫連線池。tomcat 伺服器自帶 dbcp 資料庫連線池。速度相對 c3p0 較快
    ,但因自身存在 BUG,Hibernate3 已不再提供支援。
  • C3P0 是一個開源組織提供的一個數據庫連線池,速度相對較慢,穩定性還可以 。hibernate 官方推薦使用。
  • Proxool 是sourceforge下的一個開源專案資料庫連線池,有監控連線池狀態的功能,穩定性較 c3p0 差一點
  • BoneCP 是一個開源組織提供的資料庫連線池,速度快。
  • Druid 是阿里提供的資料庫連線池,據說是 集DBCP 、C3P0 、Proxool 優點於一身的資料庫連線池。

package cn.parzulpan.jdbc.ch08;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;
import org.junit.Test;

import java.sql.Connection;

/**
 * @Author : parzulpan
 * @Time : 2020-12-01
 * @Desc : c3p0 資料庫連線池
 */

public class C3P0Test {


    // 連線方式一:不推薦
    @Test
    public void testGetConnection1() throws Exception{
        // 獲取 c3p0 資料庫連線池
        ComboPooledDataSource cpds = new ComboPooledDataSource();
        cpds.setDriverClass("com.mysql.jdbc.Driver");
        cpds.setJdbcUrl("jdbc:mysql://localhost:3306/test?useSSL=false");
        cpds.setUser("parzulpan");
        cpds.setPassword("parzulpan");

        //
        cpds.setInitialPoolSize(10);
        Connection connection = cpds.getConnection();
        System.out.println(connection);
    }

    // 連線方式二:使用配置檔案,推薦
    // 使用時,需要將 cpds 宣告為 static,然後用 static 程式碼塊為 cpds 賦值
    @Test
    public void testGetConnection2() throws Exception{
        ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");

        Connection connection = cpds.getConnection();
        System.out.println(connection);

        // 銷燬 c3p0 資料庫連線池,慎用
//        DataSources.destroy(cpds);
    }
}

<!-- c3p0配置檔案-->
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <named-config name="helloc3p0">
        <!-- 獲取連線的基本資訊-->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/test?useSSL=false</property>
        <property name="user">parzulpan</property>
        <property name="password">parzulpan</property>

        <!-- 資料庫連線池的管理的相關屬性的設定-->
        <!-- 若資料庫連線池中連線數不足時,一次向資料庫伺服器申請的連線數-->
        <property name="acquireIncrement">5</property>
        <!-- 初始化資料庫連線池時連線的數量-->
        <property name="initialPoolSize">5</property>
        <!-- 資料庫連線池中的最小的資料庫連線數-->
        <property name="minPoolSize">5</property>
        <!-- 資料庫連線池中的最大的資料庫連線數-->
        <property name="maxPoolSize">10</property>
        <!-- 資料庫連線池可以維護的 Statement 的個數 -->
        <property name="maxStatements">20</property>
        <!-- 每個連線同時可以使用的 Statement 物件的個數 -->
        <property name="maxStatementsPerConnection">5</property>
    </named-config>
</c3p0-config>

package cn.parzulpan.jdbc.ch08;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;

/**
 * @Author : parzulpan
 * @Time : 2020-12-01
 * @Desc : dbcp 資料庫連線池
 */

public class DBCPTest {

    // 連線方式一:不推薦
    @Test
    public void testGetConnection1() throws Exception{
        BasicDataSource bds = new BasicDataSource();
        bds.setDriverClassName("com.mysql.jdbc.Driver");
        bds.setUrl("jdbc:mysql://localhost:3306/test?useSSL=false");
        bds.setUsername("parzulpan");
        bds.setPassword("parzulpan");

        bds.setInitialSize(10);

        Connection connection = bds.getConnection();
        System.out.println(connection);
    }

    // 連線方式二:使用配置檔案,推薦
    // 使用時,需要將 dataSource 宣告為 static,然後用 static 程式碼塊為 dataSource 賦值
    @Test
    public void testGetConnection2() throws Exception{
        Properties properties = new Properties();
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties");
        properties.load(is);

        DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
    }
}
# dbcp 配置檔案
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?useSSL=false
username=parzulpan
password=parzulpan

initialSize=10

package cn.parzulpan.jdbc.ch08;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.junit.Test;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;

/**
 * @Author : parzulpan
 * @Time : 2020-12-01
 * @Desc : Druid 資料庫連線池
 */

public class DruidTest {

    // 連線方式一:使用配置檔案,推薦
    // 使用時,需要將 dataSource 宣告為 static,然後用 static 程式碼塊為 dataSource 賦值
    @Test
    public void testGetConnection1() throws Exception{
        Properties properties = new Properties();
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
        properties.load(is);

        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
    }
}
# Druid 配置檔案
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?useSSL=false
username=parzulpan
password=parzulpan

initialSize=10
maxActive=20
maxWait=1000
filters=wall

練習和總結