JavaWeb 之 15.JDBC提高(事物和資料庫連線池)
## 事務和資料庫連線池 ##
**事務**
1.什麼是事務:一組邏輯上的操作,要麼全都成功,要麼全都失敗。
2.模擬事務的操作
create database day18;
use day18;
create table t_account(
id int primary key auto_increment,
username varchar(30),
money double
);
insert into t_account values (null,'聰聰',10000);
insert into t_account values (null,'美美',10000);
insert into t_account values (null,'小鳳',10000);
insert into t_account values (null,'如花',10000);
insert into t_account values (null,'邦邦',10000);
**MySQL使用事務**
1.在MySQL使用命令
start transaction; -- 開啟一個事務
sql1...
sql2...
commit; -- 把事務提交了
rollback; -- 把事務回滾了
2.MySQL資料庫中,事務是預設提交的。
* show variables like '%commit%'; 可以通過該命令檢視資料庫事務是否是預設提交的。
* 設定MySQL不讓預設提交的 (set autocommit = off或者0;)
sql1..
sql2..
commit; -- 把事務提交了
rollback; -- 把事務回滾了
3.進行測試
* 第一種方式
* 開啟事務
* start transaction;
* update t_account set money = money - 1000 where username = '聰聰';
* update t_account set money = money + 1000 where username = '美美';
* 提交或者回滾
* commit; 或者 rollback;
* 測試第二種
* 設定MySQL不讓預設提交
* set autocommit = 0或者off;
* update t_account set money = money - 1000 where username = '聰聰';
* update t_account set money = money + 1000 where username = '美美';
* 提交或者回滾
* commit; 或者 rollback;
**JDBC程式碼中使用事務**
1.哪個物件可以操作事務?使用Connection介面操作事務。
* void setAutoCommit(boolean autoCommit) -- 設定預設不預設提交
* void commit() -- 提交事務
* void rollback() -- 回滾事務
**儲存點**
1.Savepoint sp = conn.setSavepoint();
2.conn.rollback(sp);
3.回滾到儲存點的位置,事務沒有結束,必須還要提交或者回滾事務才結束。
**事務的特性(面試)**
1.原子性 :強調事務的不可分隔特性,事務中的操作要麼都發生,要麼都不發生。
2.一致性 :強調資料的完整性儲存一致。
3.隔離性 :強調是一個事務的執行,不應該受到另外的事務的打擾。
4.永續性 :強調是事務結束(提交或者回滾),資料永久的儲存到資料庫中。
**不考慮隔離性會產生一些問題**
1.髒讀 :一個事務讀取到了另一個事務未提交的資料
2.不可重複讀: 一個事務讀取了(10000),另外一個事務做了修改的操作,提交了。第一個事務又做了一次查詢,這次的查詢結果變成了12000,產生了不可重複讀。只要是在一個事務中,多次的查詢結果必須是一致的。
3.虛讀(幻讀): 一個事務讀取了(10000),另外一個事務做了插入的操作,提交了。第一個事務又做了一次查詢,這次的查詢結果變成了12000,產生了不可重複讀。只要是在一個事務中,多次的查詢結果必須是一致的。
**設定事務的隔離級別**
1.read uncommitted :未提交讀 髒讀、不可重複讀、虛讀都可能發生
2.read committed :提交讀 避免髒讀,但是不可重複讀和虛讀有可能發生
3.repeatable read :可重複讀 避免髒讀和不可重複讀,但是虛讀有可能發生
4.serializable :避免髒讀、不可重複讀、虛讀
* 安全性:read uncommitted < read committed < repeatable read < serializable
* 效率:read uncommitted > read committed > repeatable read > serializable
* 在資料庫的設定隔離級別中,根據安全性和效率考慮,肯定不會用read uncommitted和serializable
* 在MySQL中預設的隔離級別:repeatable read
* 在Oracle中預設的隔離級別:read committed
**演示上述過程的發生和解決方案**
* 查詢資料庫的隔離級別 select @@tx_isolation
* 設定資料庫的隔離級別 set session transaction isolation level XXX; (XXX代表上述4種級別)
1.測試髒讀
2.開啟兩個視窗A視窗和B視窗
3.分別登陸MySQL資料庫,使用day18資料庫。
4.在A視窗中設定隔離級別
* set session transaction isolation level read uncommitted;
5.在AB兩個視窗中分別開啟事務
* start transaction;
6.在B視窗中完成一些操作(轉賬的操作)
* update t_account set money = money - 1000 where username = '聰聰';
* update t_account set money = money + 1000 where username = '美美';
* 提交了嗎?沒有提交!!
7.在A視窗做查詢的操作
* select * from t_account;
* 結果為:聰聰:9000 美美:11000
* 當在B視窗rollback後,再次在A視窗查詢
* 結果為:聰聰:10000 美美:10000
8.產生了髒讀
**避免髒讀和演示不可重複讀**
1.設定A視窗的隔離級別
* set session transaction isolation level read committed;
2.在AB兩個視窗中分別開啟事務
* start transaction;
3.在B視窗中完成一些操作(轉賬的操作)
* update t_account set money = money - 1000 where username = '聰聰';
* update t_account set money = money + 1000 where username = '美美';
4.在A視窗中
* select * from t_account;
* 結果為:聰聰:10000 美美:10000
* 當在B視窗commit後,再次在A視窗查詢
* 結果為:聰聰:9000 美美:11000
5.避免了髒讀,產生了不可重複讀
**避免不可重複讀**
1.設定A視窗的隔離級別
* set session transaction isolation level repeatable read;
2.在AB兩個視窗中分別開啟事務
* start transaction;
3.在B視窗中完成一些操作(轉賬的操作)
* update t_account set money = money - 1000 where username = '聰聰';
* update t_account set money = money + 1000 where username = '美美';
4.在A視窗中
* select * from t_account;
* 結果為:聰聰:10000 美美:10000
* 當在B視窗commit後,再次在A視窗查詢
* 結果為:聰聰:10000 美美:10000
* 然後再A視窗commit事務後再查詢
* 結果為:聰聰:9000 美美:11000
5.在A視窗中,結束事務,在做查詢
* 查詢的結果是最新的結果
6.總結:避免了髒讀和不可重複讀
**演示serializable隔離級別**
1.設定A視窗的隔離級別
* set session transaction isolation level serializable;
2.在AB兩個視窗中分別開啟事務
* start transaction;
3.在B視窗中新增一條資料
* insert into t_account values (null,'冠西',10000);
4.在A視窗中做查詢的操作
* select * from t_account;
* 什麼也不顯示,等待B視窗的操作
* 當B視窗commit後,A視窗同時查詢出來資料
**在JDBC中設定隔離級別(瞭解)**
* 在Connection介面中提供了常量,這寫常量分別代表不同的隔離級別
static int TRANSACTION_READ_UNCOMMITTED :指示可以發生髒讀 (dirty read)、不可重複讀和虛讀 (phantom read) 的常量。
static int TRANSACTION_READ_COMMITTED :指示不可以發生髒讀的常量;不可重複讀和虛讀可以發生。
static int TRANSACTION_REPEATABLE_READ :指示不可以發生髒讀和不可重複讀的常量;虛讀可以發生。
static int TRANSACTION_SERIALIZABLE :指示不可以發生髒讀、不可重複讀和虛讀的常量。
* 通過方法來進行設定,在Connection介面提供了一個方法
* void setTransactionIsolation(int level)
**資料庫連線池**
概述:連線池就是連結的池子,如果一次性建立多個連結,把連結放在連線池中(連線池在記憶體中),在記憶體中獲取連結肯定比建立連結要快。並且連結用完後也不需要銷燬,再把連線返回到連線池中。這樣就節省了建立和銷燬的時間。
1.為什麼要有連線池:建立和銷燬是消耗時間的,連線池初始化的時候建立幾個連線。
連線池池引數(開發自定義的連線池)
初始大小:10個
最小空閒連線數:3個
增量:一次建立的最小單位(5個)
最大空閒連線數:12個
最大連線數:20個
最大的等待時間:1000毫秒
2.使用連線池設定4個大引數(使用者給出)
* 驅動類的包名+類 com.mysql.jdbc.Driver
* url jdbc:mysql:///day18
* username root
* passowrd 123456
3.怎麼使用連線池物件
* 獲取連結的方法
**自定義資料庫連線池**
1.實現DataSource介面
2.重寫幾個方法。
* Connection getConnection()
* Connection getConnection(String username,String password)
3.自定義的資料庫的連線池存在哪些問題?
* 開源的連線池解決這一類的問題。
4.不能使用DataSource介面構造
5.歸還連結的方法
public void addBack(Connection conn){
// 新增到集合中
connlist.add(conn);
}
6.如果能把close()增強了,變成歸還的方法
**裝飾者模式(增強某個類中的方法)**
1.目的:增強某個類中的某個方法,有哪些方式?
* 繼承 :你需要知道它爹是誰?
* 裝飾者的模式(增強Connection介面的實現類(還不知道名字,起個名字 小聰),中的close()方法)
* 增強的類(自己來寫)和被增強的類(小聰)必須實現相同的介面。
* 在增強的類中能獲取到被增強類的引用。
* 動態代理
**開源資料庫連線池**
1.這些開源的連線池已經增強了close方法,全部都是歸還。
**DBCP連線池**
1.匯入jar包。
* commons-dbcp-1.4.jar
* commons-pool-1.5.6.jar
2.開發入門
* 需要手動設定連結的引數(必須得設定4大引數)
* BasicDataSource
* 讀取配置檔案的方式(properties檔案)
* BasicDataSourceFactory
3.其他的配置檔案(課後看)
**C3P0連線池**
1.匯入jar包
* c3p0-0.9.1.2.jar
2.使用
* ComboPooledDataSource
* 設定引數
3.C3P0預設讀取一個XML的配置檔案(c3p0-config.xml),檔案存放在src的目錄下。
4.在c3p0-config.xml檔案編寫配置(4大引數)
**Tomcat內建連線池(配置)**
1.介紹Tomcat內建在連線池。Tomcat管理連線池,只要配置資訊。
2.介紹<Context>的配置某個位置
tomcat/conf/context.xml -- 被tomcat下的所有的虛擬主機、虛擬路徑訪問.
tomcat/conf/Catalina/localhost/context.xml -- 被tomcat下的localhost虛擬主機下的所有虛擬路徑訪問.
工程下/META-INF/context.xml -- 被當前工程所訪問.
3.在context.xml需要配置什麼內容?
<Context>
<Resource
name="jdbc/EmployeeDB"
auth="Container"
type="javax.sql.DataSource"
username="root"
password="123456"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:jdbc:mysql:///day18?useUnicode=true&characterEncoding=UTF-8"
maxActive="8"
maxIdle="4"/>
</Context>
4.將mysql驅動包copy到tomcat/lib下(或者拷到WEB-INF/lib資料夾下都可以)
5.類必須是運作在Tomcat容器中才可以訪問.Servlet/JSP
**事務加在service或者DAO層**
1.事務的解決方案有兩種
* 通過引數的方式向下傳遞
* 把conn繫結到當前的執行緒中
2.ThreadLocal類底層維護Map集合。預設把當前的執行緒作為Map的key。