5.數據連接方式解耦
5.1 BaseDao的問題
BaseDao這個JAVA類,包含如下java代碼,與mysql數據庫進行鏈接。
當我們的項目部署到客戶服務器上的時候,客戶的user,password,news(dbname)有變化,可能也有多次更改,不可能由程序員再多次更改我們的java代碼。這是不合理的。怎麼辦?
思路:
將這些需要配置的內容,放置到外部的配置文件
static String driver="com.mysql.cj.jdbc.Driver";
static String url="jdbc:mysql://localhost:3306/news?useTimezone=true&serverTimezone=CTT&useUnicode=true&characterEncoding=utf8&useSSL=false";
static String user="root";
static String password="root";
5.2 使用外部的db.properties文件
在src下根目錄下,建立db.properties文件,這種文件和xml文件不同,裏面放置的是key=value,格式的字符串
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/news? useTimezone=true&serverTimezone=CTT&useUnicode=true&characterEncoding=utf8&useSSL=false
user=root
password=root
5.3 使用Properties類
我們這時候,已經使用了外部的配置文件,讀取這個外部的配置文件。
Properties(Java.util.Properties),該類主要用於讀取Java的配置文件,不同的編程語言有自己所支持的配置文件,配置文件中很多變量是經常改變的,爲了方便用戶的配置,能讓用戶夠脫離程序本身去修改相關的變量設置。就像在Java中,其配置文件常爲.properties文件,是以鍵值對的形式進行參數配置的。
//讀取外部的配置文件,直接寫,使用類加載機制,讀取;將讀取的文件,轉換爲輸入字節流對象
InputStream is=BaseDaoTwo.class.getClassLoader().getResourceAsStream("db.properties");
load(InputStream inStream) 從輸入字節流中讀取屬性列表(鍵和元素對)。
getProperty(String key) 在此屬性列表中搜索具有指定鍵的屬性。
static Properties properties=new Properties();
//讀取外部的配置文件,直接寫,使用類加載機制,讀取;將讀取的文件,轉換爲輸入字節流對象
static InputStream is=BaseDaoTwo.class.getClassLoader().getResourceAsStream("db.properties");
//如何讀取,讀取外部4個key
//定義四個靜態的String變量;
static String driver,url,user,password;
//1.獲取公共的靜態連接資源;
public static Connection getConn(){
try {
//加載到properties對象裏面
properties.load(is);
//利用getProperty()得到key信息
driver=properties.getProperty("driver");
url=properties.getProperty("url");
user=properties.getProperty("user");
password=properties.getProperty("password");
//加載驅動
Class.forName(driver);
//返回連接;
conn= DriverManager.getConnection(url,user,password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
完畢之後,大家自行測試以下即可,發現可以實現與之前一樣的連接功能。
好處:
是將之前在BaseDao java類裏面寫的數據庫連接代碼,配置信息,放到了外部文件。注意,以後,配置文件基本上都會存儲數據庫配置信息。
6.連接池技術
6.1什麼情況下使用連接池?
對於一個簡單的數據庫應用,由於對於數據庫的訪問不是很頻繁。這時可以簡單地在需要訪問數據庫時,就新創建一個連接,用完後就關閉它,這樣做也不會帶來什麼明顯的性能上的開銷。如果程序出現異常而未能關閉,將會導致數據庫系統中的內存泄漏,最終將導致重啓數據庫。
但是對於一個複雜的數據庫應用,有若干個數據庫連接,比如說10000個,情況就完全不同了。數據庫連接頻繁的建立、關閉連接,會極大的減低系統的性能,因爲對於連接的使用成了系統性能的瓶頸。連接過多,也可能導致內存泄漏,服務器崩潰。
6.2連接池的思想
爲解決傳統開發中的數據庫連接問題,可以採用數據庫連接池技術。
數據庫連接池的基本思想就是爲數據庫連接建立一個“緩衝池”。預先在緩衝池中放入一定數量的連接,當需要建立數據庫連接時,只需從“緩衝池”中取出一個,使用完畢之後再放回去。
數據庫連接池負責分配、管理和釋放數據庫連接,它允許應用程序重複使用一個現有的數據庫連接,而不是重新建立一個。
數據庫連接池在初始化時將創建一定數量的數據庫連接放到連接池中,這些數據庫連接的數量是由最小數據庫連接數來設定的。無論這些數據庫連接是否被使用,連接池都將一直保證至少擁有這麼多的連接數量。連接池的最大數據庫連接數量限定了這個連接池能佔有的最大連接數,當應用程序向連接池請求的連接數超過最大連接數量時,這些請求將被加入到等待隊列中。
用戶每次請求都需要向數據庫獲得鏈接,而數據庫創建連接通常需要消耗相對較大的資源,創建時間也較長。假設網站一天10萬訪問量,數據庫服務器就需要創建10萬次連接,極大的浪費數據庫的資源,並且極易造成數據庫服務器內存溢出、拓機。如下圖所示:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-lU2YuOky-1591174555879)(E:\政通路\課堂筆記\S2\Spring\assets\image-20200602094216914.png)]
數據庫連接是一種關鍵的有限的昂貴的資源,這一點在多用戶的網頁應用程序中體現的尤爲突出.對數據庫連接的管理能顯著影響到整個應用程序的伸縮性和健壯性,影響到程序的性能指標.數據庫連接池正式針對這個問題提出來的.數據庫連接池負責分配,管理和釋放數據庫連接,它允許應用程序重複使用一個現有的數據庫連接,而不是重新建立一個。如下圖所示:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Fr02X5Kf-1591174555883)(E:\政通路\課堂筆記\S2\Spring\assets\image-20200602094248738.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-c77wYKr5-1591174555894)(E:\政通路\課堂筆記\S2\Spring\assets\image-20200603160044219.png)]
數據庫連接池的最小連接數和最大連接數的設置要考慮到以下幾個因素:
- 最小連接數:是連接池一直保持的數據庫連接,所以如果應用程序對數據庫連接的使用量不大,將會有大量的數據庫連接資源被浪費.
- 最大連接數:是連接池能申請的最大連接數,如果數據庫連接請求超過次數,後面的數據庫連接請求將被加入到等待隊列中,這會影響以後的數據庫操作
- 如果最小連接數與最大連接數相差很大:那麼最先連接請求將會獲利,之後超過最小連接數量的連接請求等價於建立一個新的數據庫連接.不過,這些大於最小連接數的數據庫連接在使用完不會馬上被釋放,他將被放到連接池中等待重複使用或是空間超時後被釋放.
6.3數據庫連接池技術的優點
1.資源重用
由於數據庫連接得以重用,避免了頻繁創建,釋放連接引起的大量性能開銷。在減少系統消耗的基礎上,另一方面也增加了系統運行環境的平穩性。
2.更快的系統反應速度
數據庫連接池在初始化過程中,往往已經創建了若干數據庫連接置於連接池中備用。此時連接的初始化工作均已完成。對於業務請求處理而言,直接利用現有可用連接,避免了數據庫連接初始化和釋放過程的時間開銷,從而減少了系統的響應時間
3.新的資源分配手段
對於多應用共享同一數據庫的系統而言,可在應用層通過數據庫連接池的配置,實現某一應用最大可用數據庫連接數的限制,避免某一應用獨佔所有的數據庫資源
4.統一的連接管理,避免數據庫連接泄露
在較爲完善的數據庫連接池實現中,可根據預先的佔用超時設定,強制回收被佔用連接,從而避免了常規數據庫連接操作中可能出現的資源泄露
6.4連接池的實現
c3p0 dhcp
數據庫連接池的基本原理是在內部對象池中維護一定數量的數據庫連接,並對外暴露數據庫連接獲取和返回方法。
外部使用者可通過 getConnection 方法獲取連接,使用完畢後再通過 close 方法將連接返回,注意此時連接並沒有關閉,而是由連接池管理器回收,併爲下一次使用做好準備。
Java 中有一個 DataSource 接口, 數據庫連接池就是 DataSource 的一個實現。
Druid(德魯伊)是阿里巴巴開發的號稱爲監控而生的數據庫連接池,Druid是目前最好的數據庫連接池。在功能、性能、擴展性方面,都超過其他數據庫連接池,同時加入了日誌監控,可以很好的監控DB池連接和SQL的執行情況。Druid已經在阿里巴巴部署了超過600個應用,經過一年多生產環境大規模部署的嚴苛考驗。
屬性 | 說明 | 建議值 |
---|---|---|
url | 數據庫的jdbc連接地址。一般爲連接oracle/mysql。示例如下: | |
mysql : jdbc:mysql://ip:port/dbname?option1&option2&… | ||
oracle : jdbc:oracle:thin:@ip:port:oracle_sid | ||
username | 登錄數據庫的用戶名 | |
password | 登錄數據庫的用戶密碼 | |
initialSize | 啓動程序時,在連接池中初始化多少個連接 | 10-50已足夠 |
maxActive | 連接池中最多支持多少個活動會話 | |
maxWait | 程序向連接池中請求連接時,超過maxWait的值後,認爲本次請求失敗,即連接池 | 100 |
沒有可用連接,單位毫秒,設置-1時表示無限等待 | ||
minEvictableIdleTimeMillis | 池中某個連接的空閒時長達到 N 毫秒後, 連接池在下次檢查空閒連接時,將 | 見說明部分 |
回收該連接,要小於防火牆超時設置 | ||
net.netfilter.nf_conntrack_tcp_timeout_established的設置 | ||
timeBetweenEvictionRunsMillis | 檢查空閒連接的頻率,單位毫秒, 非正整數時表示不進行檢查 | |
keepAlive | 程序沒有close連接且空閒時長超過 minEvictableIdleTimeMillis,則會執 | true |
行validationQuery指定的SQL,以保證該程序連接不會池kill掉,其範圍不超 | ||
過minIdle指定的連接個數。 | ||
minIdle | 回收空閒連接時,將保證至少有minIdle個連接. | 與initialSize相同 |
removeAbandoned | 要求程序從池中get到連接後, N 秒後必須close,否則druid 會強制回收該 | false,當發現程序有未 |
連接,不管該連接中是活動還是空閒, 以防止進程不會進行close而霸佔連接。 | 正常close連接時設置爲true | |
removeAbandonedTimeout | 設置druid 強制回收連接的時限,當程序從池中get到連接開始算起,超過此 | 應大於業務運行最長時間 |
值後,druid將強制回收該連接,單位秒。 | ||
logAbandoned | 當druid強制回收連接後,是否將stack trace 記錄到日誌中 | true |
testWhileIdle | 當程序請求連接,池在分配連接時,是否先檢查該連接是否有效。(高效) | true |
validationQuery | 檢查池中的連接是否仍可用的 SQL 語句,drui會連接到數據庫執行該SQL, 如果 | |
正常返回,則表示連接可用,否則表示連接不可用 | ||
testOnBorrow | 程序 申請 連接時,進行連接有效性檢查(低效,影響性能) | false |
testOnReturn | 程序 返還 連接時,進行連接有效性檢查(低效,影響性能) | false |
poolPreparedStatements | 緩存通過以下兩個方法發起的SQL: | true |
public PreparedStatement prepareStatement(String sql) | ||
public PreparedStatement prepareStatement(String sql, | ||
int resultSetType, int resultSetConcurrency) | ||
maxPoolPrepareStatementPerConnectionSize | 每個連接最多緩存多少個SQL | 20 |
filters | 這裏配置的是插件,常用的插件有: | stat,wall,slf4j |
監控統計: filter:stat | ||
日誌監控: filter:log4j 或者 slf4j | ||
防禦SQL注入: filter:wall | ||
connectProperties | 連接屬性。比如設置一些連接池統計方面的配置。 | |
druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 | ||
比如設置一些數據庫連接屬性: |
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Io6LyXR2-1591174555899)(E:\政通路\課堂筆記\S2\Spring\assets\image-20200603161443399.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-7dEA2WjY-1591174555902)(E:\政通路\課堂筆記\S2\Spring\assets\image-20200603161646924.png)]
步驟:
1.在src下新建一個druid.properties 連接池配置文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/news?useTimezone=true&serverTimezone=CTT&useUnicode=true&characterEncoding=utf8&useSSL=false
user=root
password=root
initialSize=5
maxActive=10
|
| | 比如設置一些數據庫連接屬性: | |
[外鏈圖片轉存中…(img-Io6LyXR2-1591174555899)]
[外鏈圖片轉存中…(img-7dEA2WjY-1591174555902)]
步驟:
1.在src下新建一個druid.properties 連接池配置文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/news?useTimezone=true&serverTimezone=CTT&useUnicode=true&characterEncoding=utf8&useSSL=false
user=root
password=root
initialSize=5
maxActive=10