天天看點

JDBC(9)—事務(Transaction)

  1. 資料庫事務:在資料庫中所謂事務是指一組邏輯操作單元,使資料從一種狀态轉換到另一種狀态。為確定資料庫中的資料的一緻性,資料的操縱應當是離散的成組的邏輯單元:當它全部完成時,資料的一緻性可以保持,而當這些單元中一部分操作失敗,整個事務全部視為錯誤,所有從起始點開始以後的操作應全部回退到開始狀态。
  2. 事務的操作:先定義開始一個事務,然後對資料進行修改操作,這時如果送出(commit),這些修改就永久的儲存下來,

    *如果回退(rollback)資料庫管理系統将放棄所做的所有修改操作,而回到開始事務時的狀态。

  3. 事務的(ACID)屬性:
    • ——原子性(Atomicity):是指事務是一個不可分割的工作機關,事務中的操作要麼都發生,要麼都不發生。
    • ——一緻性(Consistency):事務必須使資料庫從一個一緻性狀态轉變成另外一個一緻性狀态。
    • ——隔離性(Lsolation):指一個事務的執行不能被其他事務幹擾,即一個事務内部的操作及使用的資料對并發的其他事務是隔離的并發執行的各個事務不能互相幹擾。
    • ——持久性(Durability):指一個事務一旦被送出,它對資料庫中的資料的改變就是永久性的接下來的其他操作和資料庫的故障不應該對其有任何影響。
  4. JDBC的事務:指構成單個邏輯工作單元的操作集合。
  5. 事務處理:保證所有的事務都作為一個工作單元來執行,即使出現了故障都不能改變這種執行方式,當在一個事務中執行多個操作時要麼所有的事務都被送出,要麼整個事務復原到最初的狀态。
  6. 當一個連接配接對象被建立時預設情況下是自動送出事務,每次執行一個SQL語句時,如果執行成功,就會向資料庫自動送出,而不能復原,為了讓多個SQL語句作為一個事務執行,可使用一下步驟:
    • ——調用Connection對象的setAutoCommit(false),以取消自動送出事務。
    • ——在所有的SQL語句都成功執行後,調用commit方法送出事務。
    • ——在出現異常時,調用rollback方法,事務復原。
    • ——若此時Conneciton沒有關閉,則需要恢複其自動送出狀态。

      注意:若使用的資料庫引擎不是innodb,則事務無法復原。而mysql預設的資料庫引擎是MyISAM,是以第一次使用事務則需要更改資料表的引擎。

  7. 事務的隔離級别:對于同時運作的多個事務,當這些事務通路資料庫中相同的資料時,如果沒有采取必要的隔離機制,就會導緻各種并發問題:
    • ——髒讀:對于兩個事務T1、T2,T1讀取的已經被T2更新但沒有送出的字段之後,若T2復原,T1讀取的内容就是臨時且無效的。
    • ——不可重複讀:對于兩個事務T1、T2,T1讀取的一個字段,T2更新了該字段之後,T1再次讀取同一個字段,值就不一樣了。
    • ——幻讀:對于兩個事務T1、T2,T1從表中讀取一個字段,然後T2在表中插入一些行,之後T1再次讀取同一張表,就會多出幾行。
  8. 資料庫事務的隔離性:資料庫系統必須具有隔離并發運作各個事務的能力,使他們不會相會影響,避免各種并發問題。
  9. 隔離級别:一個事務和其他事務隔離的程度。隔離級别越高,資料一緻性就越好,但并發性越弱。
  10. 資料庫提供的四種事務隔離級别。
    • ——read uncommitted(讀未送出資料)
    • ——read committed(讀已送出資料)
    • ——repeatable read(可重複讀)
    • ——serialized(串行化)
    • Oracle支援2種事務隔離級别read uncommitted、serialized,預設是read uncommitted
    • Mysql支援4種隔離級别,預設repeatable read。
  11. 設定事務隔離級别:
    • 使用sql語句:select @@tx_isolation檢視目前隔離級别
    • ——設定目前的mysql連接配接的隔離級别:set transaction isolation level read commited;
    • ——設定資料庫系統的全局隔離級别:set global transaction isolation level read commited;
    • 使用代碼:

      conn.setTransactionIsolation(conn.TRANSACTION_READ_COMMITTED);

代碼執行個體

public class Transaction_10 {

    /**
     * 1.Tom給Jerry彙款500元
     *注意:如果多個操作,每個操作使用的都是自己單獨的連接配接,則無法保證事務。
     */
    @Test
    public void testTransaction(){

        DAO1_7 dao = new DAO1_7();

        String sql = "update user set  balance = balance - 500 where id = 1";
        dao.update(sql);
        //出現異常時,Tom的錢被扣掉500,但是Jerry的錢沒有增加500,
        int number =  / ;
        System.out.println(number);

        sql = "update user set  balance = balance + 500 where id = 2";
        dao.update(sql);
    }
    /**
     * 2.修改以上(1)方法,使用同一個連接配接
     * @param conn
     * @param sql
     * @param objects
     */
    public void update(Connection conn, String sql, Object ...objects){
        PreparedStatement preparedstatement = null;
        try {
            preparedstatement = conn.prepareStatement(sql);
            for(int i = ; i < objects.length; i++){
                preparedstatement.setObject(i + , objects[i]);
            }
            preparedstatement.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            TestTools.release(preparedstatement,null);
        }
    }
    /**
     * 2_1.測試以上方法
     */
    @Test
    public void test(){
        Connection conn = null;
        try {
            conn = TestTools.getConnection();
            //開始事務,在開始之前要關閉資料庫預設自動送出
            conn.setAutoCommit(false);

            String sql = "update users set  balance = balance - 500 where id = 1";
            update(conn,sql);
            //出現異常時,事務復原到之前的狀态
            int number =  / ;
            System.out.println(number);

            sql = "update users set  balance = balance + 500 where id = 2";
            update(conn,sql);
            //結束,就送出事務
            conn.commit();

        } catch (Exception e) {
            e.printStackTrace();
            //發生異常,事務復原
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }finally{
            //設定事務送出方式為自動送出:
            try {
                conn.setAutoCommit(true);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            //關閉連接配接
            TestTools.release(null, conn);
        }
    }

    /**
     * 3.傳回某條記錄的某一個字段的值或一個統計的值(一共有多少條記錄等。),即:确定的某一行的某個字段的值,
     *或者計算的值,3.、3_1.方法處理資料并設定隔離級别,3_2.方法擷取資料以測試不同的隔離級别下擷取的資料的差異
     * @param sql
     * @param objects
     * @return
     */
    public <E> E getForValue(String sql, Object ... objects){
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //1.得到結果集:該結果集應該隻有一行,且隻有一列
            conn = TestTools.getConnection();
            //設定隔離級别:讀未送出,該隔離級别即使事務未送出也可獲得更新但未送出的的資料。擷取資料結果:500
            //conn.setTransactionIsolation(conn.TRANSACTION_READ_UNCOMMITTED);
            //設定隔離級别:讀已送出,該隔離級别若事務未送出則擷取更新之前的資料,擷取資料結果:1000
            conn.setTransactionIsolation(conn.TRANSACTION_READ_COMMITTED);

            ps = conn.prepareStatement(sql);
            for(int i = ; i < objects.length; i++){
                ps.setObject(i + , objects[i]);
            }
            rs = ps.executeQuery();
            if(rs.next()){
                //2.取得結果
                return (E)rs.getObject();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            TestTools.release(rs, ps, conn);
        }
        return null;
    }
    /**
     * 3_1.測試以上方法設定事務的隔離級别,使用update方法更新資料,使balance資料減500
     */
    @Test
    public void testTransactionIsolationUpdate(){

        Connection conn = null;
        try {
            conn = TestTools.getConnection();
            //關閉自動送出
            conn.setAutoCommit(false);
            String sql = "update users set  balance = balance - 500 where id = 1";
            update(conn, sql);

            //送出事務
            conn.commit();


        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            TestTools.release(null, conn);
        }

    }
    /**
     * 3_2.使用getForValue()方法,查詢被更新但未送出的資料
     */
    @Test
    public void testTransactionIosationRead(){
        String sql = "select balance from users where id = 1";
        Integer balance = getForValue(sql);
        System.out.println(balance);
    }

}
           

轉載于:https://www.cnblogs.com/tengpengfei/p/10454013.html