1. 程式人生 > >JavaWeb 之 15.JDBC提高(事物和資料庫連線池)

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&amp;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。