DataSource是做爲DriverManager的替代品而推出的,DataSource 對象是獲取鏈接的首選方法。
起源
爲什麼放棄DriverManager
DriverManager負責管理驅動程序,而且使用已註冊的驅動程序進行鏈接。
//一、註冊驅動
Class.forName("com.mysql.jdbc.Driver");
//數據庫鏈接所需參數
String user = "root";
String password = "123456";
String url = "jdbc:mysql://localhost:3306/sampledb?useUnicode=true&characterEncoding=utf-8";
//二、獲取鏈接對象
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%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";
//二、得到sql語句執行對象
Statement stmt = conn.createStatement();
//三、執行並保存結果集
ResultSet rs = stmt.executeQuery(sql);
//四、處理結果集
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的一個實現類,做爲中間層使用,而且基本上都提供了附帶的其餘的服務,也就是說不只僅實現了核心建築,也基於核心之上構建了不少的外圍建設。