數據庫連接池和線程池類似,主要是,Connection 相當浪費時間和內存(比新建線程慢多了)。
一、連接池的背景
1. jdbc的背景
(1)成立背景:每個數據庫廠商都有自己的數據庫驅動,爲了實現我們可以方便地連接到到不同數據庫,(比如你的賬戶信息在MySQL數據庫,頭像圖片在Oracle數據庫),
(2)jdbc定義:jdbc是一個接口規範,數據庫驅動(Driver)由數據庫廠商自己來完成。關係如圖所示。
(3)回顧連接的步驟:啓動Driver,連接獲得connection,獲取statement,使用statement,增刪改,或者查詢(獲得resultSet)
2. jdbc鏈接池的概念:
**類似線程池:**一個容器,程序加載就自動生成幾個Connection ,然後用的時候比較方便,而且用完了不是關閉Connection,而是歸還。
3.爲什麼要使用連接池?
(1)連接數據庫的Connection十分浪費資源,(比如獲得connection連接需要 900 毫秒,獲得statement只需30毫秒,獲得 ResultSet只需要 3毫秒,)
(2)數據池技術:準備一些一些預先的資源,過來就連接準備好的預先的資源。
(3)增加同時執行SQL的數量:,你最高的statement總數爲 (最高負載):Connection數 * 每個Connection產生的statement數
二、連接池的實現(不用框架)
1.myJdbcPool.java實現步驟
1.在static批量生產配置的Connection放到Victor裏面(Victor線程安全)
2.在getConnection()方法裏,
- 1.在集合victors取出一個Connection,
- 2.並使用動態代理技術修改close()方法,讓其變成歸還connection對象到集合裏。
2.myJdbcPool.java 代碼
import javax.sql.DataSource;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.Properties;
import java.util.Vector;
import java.util.logging.Logger;
/**
* JdbcPool 數據池(數據源)
* #### 一.myJdbcPool.java實現步驟
* 1.在loadConfiguration()批量生產配置的Connection放到Victor裏面(Victor線程安全)
* 2.在getConnection()方法裏,
* * (1).在集合victors取出一個Connection,
* * (2).並使用[動態代理技術]()修改close()方法,讓其變成歸還connection對象到集合裏。
*/
public class JdbcPool implements DataSource {
public static Vector<Connection> victors = new Vector<>();
/**
* 1.在loadConfiguration()批量生產配置的Connection
* 放到Victor裏面(Victor線程安全)
*/
private static void loadConfiguration() {
try {
String configPath = "jdbcPoolConf.properties";
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(configPath));
Properties properties = new Properties();
//將輸入流的數據載入到Properties對象裏,inputSteam都可以?
properties.load(bufferedInputStream);
//用properties對象獲得配置文件的信息
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
int jdbcPoolInitSize = Integer.parseInt(properties.getProperty("jdbcPoolInitSize"));
//驅動
Class.forName(properties.getProperty("driver"));
//如果沒有初始化就初始化,
if (victors.size() == 0){
for (int i = 0; i < jdbcPoolInitSize; i++) {
Connection connection = DriverManager.getConnection(url,user,password);
System.out.println("日誌--獲取到鏈接:"+configPath);
victors.addElement(connection);
}
}
} catch (Exception e) {
System.out.println("日誌--創建失敗!");
e.printStackTrace();
}
}
/**
* * 2.在getConnection()方法裏,
* (1).在集合victors取出一個Connection,
* (2).並使用[動態代理技術]()修改close()方法,
* 讓其變成歸還connection對象到集合裏。
* @return Connection,close()被動態代理修改了
*
*/
@Override
public Connection getConnection() {
if (victors.size() == 0){
JdbcPool.loadConfiguration();
}
if (victors.size() > 0){
//取出一個 Connection,並給final?
final Connection connection = victors.remove(0);
//關鍵的動態代理技術,實現修改connection的close方法
return (Connection) Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), connection.getClass().getInterfaces(), new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(!method.getName().equals("close")){
return method.invoke(connection, args);
}else{
//如果調用的是Connection對象的close方法,就把conn還給數據庫連接池
victors.addElement(connection);
System.out.println(connection + "被還給Connections數據庫連接池了!!");
System.out.println("Connections數據庫連接池大小爲" + victors.size());
return null;
}
}
});
}else {
System.out.println("日誌--鏈接超時!");
return null;
}
}
public static void release(Connection connection, Statement statement,ResultSet resultSet){
//先開後關
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//===========================實現的方法========================
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
3.配置文件
配置文件:jdbcPoolConf.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/bmft_blog?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=UTC
user=root
password=123456
jdbcPoolInitSize=10
4.效果
Run:(每個人數據庫不一樣,測試類請自己書寫一個,這裏測試類就不展示了)
三、常用的開源數據庫連接池
(這裏列一個綱領,具體的搜索我的博客)
1,DBCP
併發量低下,性能不要適用於小型系統,Tomcat內置
2.C3P0
單線程,性能差,適用於小型系統
3.Druid(阿里巴巴)
最好用的數據庫連接池,高性能,高併發,高拓展,分析監控設計,快速交互查詢,高可用(遇到一些情況,冗機還能運行)
四、如何使用Tomcat配置數據連接池
(這裏列一個綱領,具體的搜索我的博客)