擴充閱讀
第一節 從零開始手寫 mybatis(一)MVP 版本。
第二節 從零開始手寫 mybatis(二)mybatis interceptor 外掛機制詳解
第三節 從零開始手寫 mybatis(三)jdbc pool 從零實現資料庫連線池
第四節 從零開始手寫 mybatis(四)- mybatis 事務管理機制詳解
連線池的作用
資源重用
由於資料庫連線得到重用,避免了頻繁建立、釋放連線引起的大量效能開銷。在減少系統消耗的基礎上,
另一方面也增進了系統執行環境的平穩性(減少記憶體碎片以及資料庫臨時程序/執行緒的數量)。
更快的系統響應速度
資料庫連線池在初始化過程中,往往已經建立了若干資料庫連線置於池中備用。此時連線的初始化工作均已完成。
對於業務請求處理而言,直接利用現有可用連線,避免了資料庫連線初始化和釋放過程的時間開銷,從而縮減了系統整體響應時間。
新的資源分配手段
對於多應用共享同一資料庫的系統而言,可在應用層透過資料庫連線的配置,使用資料庫連線池技術。
設定某一應用最大可用資料庫連線數,避免某一應用獨佔所有資料庫資源。
統一的連線管理,避免資料庫連線洩漏
在較為完備的資料庫連線池實現中,可根據預先設定的連線佔用超時時間,強制收回被超時佔用的連線。
從而避免了常規資料庫連線操作中可能出現的資源洩漏(當程式存在缺陷時,申請的連線忘記關閉,這時候,就存在連線洩漏了)。
中介軟體
常見實現對比
參考網上資料Druid > TomcatJDBC > DBCP > C3P0,BoneCP 的效能方面沒有深入比較,應該和 Tomcat Jdbc 差不多。
對於小型的系統,併發壓力不大時,選擇哪一種資料庫連線池差別不會很大,主要考慮的應該是連線池的穩定性。
當併發量較高時,一般不會選擇使用 DBCP 和C3P0,選 Druid 是較好的。
手動實現
自己實現一個簡化版,便於理解原理。
- 連線池介面
public interface IPool {
/**
* 獲取新的資料庫連結
* @return 資料庫連結
*/
PoolConnection getPoolConnection();
}
其中 PoolConnection 如下:
public class PoolConnection {
/**
* 是否繁忙
*/
private volatile boolean isBusy;
/**
* 資料庫連結資訊
*/
private Connection connection;
}
- 核心實現
public class PoolImpl implements IPool {
/**
* 資料庫驅動
*/
private final String jdbcDriver;
/**
* 資料庫連線
*/
private final String jdbcUrl;
/**
* 資料庫使用者名稱
*/
private final String username;
/**
* 資料庫密碼
*/
private final String passowrd;
/**
* 連線池大小
*/
private final int size;
/**
* 資料庫連線池列表
*/
private List<PoolConnection> poolConnections = new ArrayList<>();
public PoolImpl(String jdbcDriver, String jdbcUrl, String username, String passowrd, int size) {
this.jdbcDriver = jdbcDriver;
this.jdbcUrl = jdbcUrl;
this.username = username;
this.passowrd = passowrd;
this.size = size;
init();
}
private void init() {
try {
//1. 註冊資料庫連線資訊
Driver sqlDriver = (Driver) Class.forName(jdbcDriver).newInstance();
DriverManager.registerDriver(sqlDriver);
//2. 初始化連線池
initConnectionPool();
} catch (InstantiationException | IllegalAccessException | SQLException | ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 初始化連結
* @throws SQLException sql 異常
*/
private void initConnectionPool() throws SQLException {
for(int i = 0; i < size; i++) {
Connection connection = DriverManager.getConnection(jdbcUrl, username, passowrd);
PoolConnection poolConnection = new PoolConnection(false, connection);
poolConnections.add(poolConnection);
}
}
@Override
public PoolConnection getPoolConnection() {
if(poolConnections.size() <= 0) {
return null;
}
PoolConnection poolConnection = getRealConnection();
while (poolConnection == null) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
poolConnection = getRealConnection();
}
return poolConnection;
}
/**
* 獲取資料庫連結物件
* @return 資料庫連結物件
*/
private synchronized PoolConnection getRealConnection() {
for(PoolConnection poolConnection : poolConnections) {
// 尋找不處於繁忙狀態的連線
if(!poolConnection.isBusy()) {
Connection connection = poolConnection.getConnection();
// 測試當前連線是否有效
try {
if(!connection.isValid(5000)) {
Connection validConnection = DriverManager.getConnection(jdbcUrl, username, passowrd);
poolConnection.setConnection(validConnection);
}
} catch (SQLException e) {
e.printStackTrace();
}
// 設定為繁忙
poolConnection.setBusy(true);
return poolConnection;
}
}
return null;
}
}
- 執行緒池管理類
使用單例
public class PoolManager {
/**
* 連線池持有類
*/
private static class PoolHolder {
private static String url = "";
private static String driver = "";
private static String username = "";
private static String password = "";
private static int size = 10;
private static IPool poolImpl = new PoolImpl(driver, url, username, password, size);
}
/**
* 內部類單利模式產生使用物件
* @return 單例
*/
public static IPool getInstance() {
return PoolHolder.poolImpl;
}
}