Data Source與數據庫連接池簡介(JDBC簡介)

DataSource是作為DriverManager的替代品而推出的,DataSource 對象是獲取連接的首選方法。

起源

為何放棄DriverManager

DriverManager負責管理驅動程序,並且使用已註冊的驅動程序進行連接。

//1、註冊驅動
Class.forName("com.mysql.jdbc.Driver");
//數據庫連接所需參數
String user = "root";
String password = "123456";
String url = "jdbc:mysql://localhost:3306/sampledb?useUnicode=true&characterEncoding=utf-8";
//2、獲取連接對象
Connection conn = DriverManager.getConnection(url, user, password);

使用DriverManager的一般形式如上面代碼所示

直接使用DriverManager的這種形式,通常需要將驅動程序硬編碼到項目中(JDBC4.0後可以自動註冊驅動程序)

而且最重要的是DriverManager的getConnection方法獲取的連接,是建立與數據庫的連接,是建立與數據庫的連接,是建立與數據庫的連接。

但是建立與數據庫的連接是一項較耗資源的工作,頻繁的進行數據庫連接建立操作會產生較大的系統開銷。

隨著企業級應用復雜度的提升以及對性能要求的提高,這一點是難以接受的。

連接池

既然每次使用時都重新建立與數據庫之間的連接,會產生較大的系統開銷

是否可以事先創建一些連接備用,當需要時,從這些連接中選擇一個提供出去;當連接使用完畢後,並不是真正的關閉,而是將這些數據狀態還原,然後繼續等待下一個人使用?

比如滑雪場會租賃雪具滑雪服等,如果你不是資深玩傢,你沒有必要浪費錢買,即使你不差錢,每次去滑雪場都不能輕裝上陣,每次都要攜帶很多裝備,也是一件麻煩事。

這種沒必要的花費或者麻煩其實都是一種開銷。

連接池的核心與租用的理念有類似的點,重復使用可以提高連接的利用率,減少開銷(當然連接池的使用並不需要你花費一筆租金)

連接的持有是消耗空間的,但是現在絕大多數場景下,磁盤空間並沒有那麼金貴,我們更關心的是性能,所以空間換取時間,連接池的邏輯被廣泛應用。

數據源

DriverManager隻是建立與數據庫之間的連接,如何才能將連接池的概念應用其中?

一種很自然的方式就是提供一個薄層的封裝,建立一個中間層,這個中間層將DriverManager生成的連接,組織到連接池中,然後從池中提供連接。

Data Source就是DriverManager的一種替代角色,對外呈現就類似於一個DriverManager,擁有對外提供連接的能力直接使用DriverManager,驅動程序與管理器是“服務者—管理者”的形式,借助於管理者才能提供服務。Data Source將驅動程序的概念淡化瞭,突出驅動程序能夠提供的服務與能力,將驅動程序提供的服務與能力抽象為Data Source數據源這一角色。

DataSource中獲取的連接來自於連接池中,而池中的連接根本也還是從DriverManager獲取而來

有瞭數據源這一中間層,就可以實現連接池和分佈式事務的管理。

對外呈現DataSource就是類似於DriverManager的一個存在。

DataSource的形式是JNDI (Java Naming Directory Interface)

DataSource是JNDI資源的一種,那麼到底什麼是JNDI呢

此處不過多解釋,可以簡單認為JNDI是類似這樣一個東西:

一個哈希表,類型為<String,Object>

JNDI的兩個最主要操作:bind和lookup。bind操作負責往哈希表裡存對象,lookup則根據這個鍵值字符串往外取對象。

開發人員可以使用鍵值——也就是一個字符串名稱——來獲取某個對象。

簡言之就是可以給一個對象命名,然後可以通過名稱找到這個對象。

數據源的概念在應用程序與數據庫連接之間插入瞭一個中間層,進而可以實現連接池以及事務管理,並且以JNDI的形式,也能夠以非常方便的形式使用。

實現

核心架構

關於數據源有以下幾個核心的接口

CommonDataSource接口定義瞭 DataSource、XADataSource 和 ConnectionPoolDataSource 之間公用的方法。

DataSource 是 官方定義的獲取 connection 的接口, ConnectionPoolDataSource 是官方定義的從 connection pool 中拿 connection 的接口,XADataSource是定義的用來獲取分佈式事務連接的接口

也就是分為瞭三個方向,基本實現,連接池,事務

對於ConnectionPoolDataSource的使用方案應該是下面所示

對於Connection Pool的實現,借助於ConnectionPoolDataSource,進而獲取PooledConnection ,然後獲取連接,這是一種標準做法

但是有的時候 事情的發展或許並不一定如規劃的那般發展

很多的工具類僅僅實現DataSource瞭,也一並實現連接池以及事務的能力,接口就在那裡,我直接實現一個強大的實現類,也沒什麼問題

DataSource

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

作為 DriverManager 工具的替代項,DataSource 對象是獲取連接的首選方法。

實現 DataSource 接口的對象通常在基於 JavaTM Naming and Directory Interface (JNDI) API 的命名服務中註冊。

DataSource 接口由驅動程序供應商實現。共有三種類型的實現:

  • 基本實現 – 生成標準的 Connection 對象
  • 連接池實現 – 生成自動參與連接池的 Connection 對象。此實現與中間層連接池管理器一起使用。
  • 分佈式事務實現 – 生成一個 Connection 對象,該對象可用於分佈式事務,大多數情況下總是參與連接池。此實現與中間層事務管理器一起使用,大多數情況下總是與連接池管理器一起使用。

DataSource 對象的屬性在必要時可以修改。

例如,如果將數據源移動到另一個服務器,則可更改與服務器相關的屬性。其優點在於,由於可以更改數據源的屬性,所以任何訪問該數據源的代碼都無需更改。

通過 DataSource 對象訪問的驅動程序本身不會向 DriverManager 註冊。

通過lookup操作獲取 DataSource 對象,然後使用該對象創建 Connection 對象。

使用基本的實現,通過 DataSource 對象獲取的連接與通過 DriverManager 設施獲取的連接相同。

數據源的實現必須提供public的無參的構造函數。

API

DataSource隻有兩個方法(確切的說是一個方法的兩個重載版本),用於建立與此 DataSource 對象所表示的數據源的連接。

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

小結

DriverManager用於管理驅動程序並且提供數據庫的直連,頻繁的創建和消耗連接增加系統大量開銷,並且將數據庫連接直接暴露。

數據源的概念就是為瞭在應用程序和DriverManager創建的數據庫直接連接之間插入一個中間層

借助於中間層,應用程序與數據庫的連接兩者之間完成瞭解耦,也能夠對數據庫的真實連接進行隱藏;

一旦解耦,通過中間層間接調用,類似代理模式,就可以添加更多的服務—連接池以及分佈式事務。

數據源相關接口有三個,但是很多是僅僅實現瞭DataSource接口

而對於連接池本質就是一個容器,負責管理創建好的數據庫連接。

連接池與數據源邏輯上是兩回事,但是在實現層面的代碼中DataSource的實現類往往都具有瞭連接池以及連接池管理方面的功能。

所以有些時候,DataSource到底是理解成數據源?還是javax.sql.DataSource?還是指的一個實現?還是一個實現瞭數據庫連接池的實現?(經常一個實現瞭DataSource的並且提供瞭連接池功能的實現,會被叫做數據庫連接池)

應用

Java作為一種廣泛使用的開發語言,自然不需要我們自己實現DataSource,一些大廠已經幫我們實現瞭

比如:DBCP ,C3P0 ,druid

下面的三張圖展示瞭類繼承結構,可以看得出來他們實現的接口

目前推薦使用ALI的Druid,http://druid.io/

maven中央倉庫: http://central.maven.org/maven2/com/alibaba/druid/ 

Druid是一個開源項目,源碼托管在github上,源代碼倉庫地址是 https://github.com/alibaba/druid。

Wiki首頁:

https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98

與其他主流對比

https://github.com/alibaba/druid/wiki/%E5%90%84%E7%A7%8D%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0%E5%AF%B9%E6%AF%94

數據庫連接池示例

如下一個簡單的演示

package jdbc;
import com.alibaba.druid.pool.DruidDataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import org.apache.commons.dbcp2.BasicDataSource;
public class MyDataSource {
  public static void main(String[] args) throws Exception {
      String user = "root";
      String password = "123456";
      String url = "jdbc:mysql://localhost:3306/sampledb?useUnicode=true&characterEncoding=utf-8";
      //1.獲取連接
      // Connection conn = getDHCPConnection(user,password,url);
      //Connection conn = getC3P0Connection(user,password,url);
      Connection conn = getDruidConnection(user, password, url);
      String sql = "select * from student limit 0,10";
      //2、獲得sql語句執行對象
      Statement stmt = conn.createStatement();
      //3、執行並保存結果集
      ResultSet rs = stmt.executeQuery(sql);
      //4、處理結果集
      while (rs.next()) {
      System.out.print("id:" + rs.getInt(1));
      System.out.print(",姓名:" + rs.getString(2));
      System.out.print(",年齡:" + rs.getInt(3));
      System.out.println(",性別:" + rs.getString(4));
      }
      conn.close();
      stmt.close();
      rs.close();
}
public static Connection getDruidConnection(String user, String password, String url)
throws Exception {
    DruidDataSource ds = new DruidDataSource();
    ds.setUsername(user);
    ds.setPassword(password);
    ds.setUrl(url);
    ds.setDriverClassName("com.mysql.jdbc.Driver");
    return ds.getConnection();
    }
public static Connection getC3P0Connection(String user, String password, String url)
throws Exception {
    ComboPooledDataSource cpds = new ComboPooledDataSource();
    cpds.setUser(user);
    cpds.setPassword(password);
    cpds.setJdbcUrl(url);
    cpds.setDriverClass("com.mysql.jdbc.Driver");
    return cpds.getConnection();
    }
public static Connection getDHCPConnection(String user, String password, String url)
throws Exception {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setUsername(user);
        dataSource.setPassword(password);
        dataSource.setUrl(url);
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        Connection connection = dataSource.getConnection();
        return connection;
    }
}

總結

數據源作為DriverManager的替代者,用於獲取數據庫連接,你應該總是使用DataSource

DataSource是應用程序與數據庫連接的一個抽象的中間層,是一個接口

對於DataSource已經有瞭很多優秀的實現,其中較為突出的為Druid,建議使用,Druid不僅僅提供瞭連接池的功能還提供瞭其他比如監控等功能,非常強大。

對於數據源的應用,除瞭用戶名密碼url還有其他的一些屬性信息,比如最大連接數,建立連接的最大等待時間等,不同的連接池略微有出入,可以查看手冊。

對於DataSource的一些實現,經常被叫做數據庫連接池,比如Druid官方文檔中說“Druid是Java語言中最好的數據庫連接池“,本質核心就是DataSource的一個實現類,作為中間層使用,並且基本上都提供瞭附帶的其他的服務,也就是說不僅僅實現瞭核心建築,也基於核心之上構建瞭很多的外圍建設。

到此這篇關於Data Source與數據庫連接池簡介(JDBC簡介)的文章就介紹到這瞭,更多相關數據庫連接池Data Source內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: