JDBC事務

原文鏈接:https://www.cnblogs.com/gdwkong/p/7633016.html

一、什麼是事務?

  在人員管理系統中,你刪除一個人員,你即需要刪除人員的基本資料,也要刪除和該人員相關的信息,如信箱,文章等等,這樣,這些數據庫操作語句就構成一個事務!

      一個事務是由一條或多條對數據庫操作的SQL語句所組成的一個不可分割的工作單元,只有當事務中的所有操作都正常執行完了,整個事務纔可以被提交給數據庫。

二、事務是必須滿足4個條件(ACID)

  • 事務的原子性( Atomicity):一組事務,要麼成功;要麼撤回。
  • 一致性 (Consistency):事務執行後,數據庫狀態與其他業務規則保持一致。比如A向B轉賬,不可能A扣了錢,B卻沒收到。
  • 隔離性(Isolation):事務獨立運行。一個事務處理後的結果,影響了其他事務,那麼其他事務會撤回。事務的100%隔離,需要犧牲速度。比如A正在從一張銀行卡中取錢,在A取錢結束前,B不能向這張卡轉賬。
  • 持久性(Durability):事務完成後,事務對數據庫的所有更新將被保存到數據庫,不能回滾。

三、MySQL中的事務

    在默認情況下,MySQL每執行一條SQL語句,都是一個單獨的事務。如果需要在一個事務中包含多條SQL語句,那麼需要開啓事務和結束事務。

  • 開啓事務:start transaction
  • 結束事務:commit或rollback

     在執行SQL語句之前,先執行start transaction,這就開啓了一個事務(事務的起點),然後可以去執行多條SQL語句,最後要結束事務,commit表示提交,即事務中的多條SQL語句所作出的影響會持久到數據庫中,或者rollback,表示回滾到事務的起點,之前做的所有操作都被撤銷了。

mysql> SELECT * FROM account;
+----+------+---------+
| id | NAME | balance |
+----+------+---------+
|  1 | zs   | 1000.00 |
|  2 | ls   | 1000.00 |
|  3 | ww   | 1000.00 |
+----+------+---------+
3 rows in set (0.00 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE account SET balance=900 WHERE name = 'zs';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT * FROM account;
+----+------+---------+
| id | NAME | balance |
+----+------+---------+
|  1 | zs   |  900.00 |
|  2 | ls   | 1000.00 |
|  3 | ww   | 1000.00 |
+----+------+---------+
3 rows in set (0.00 sec)

mysql> UPDATE account SET balance=1100 WHERE name = 'ls';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT * FROM account;
+----+------+---------+
| id | NAME | balance |
+----+------+---------+
|  1 | zs   |  900.00 |
|  2 | ls   | 1100.00 |
|  3 | ww   | 1000.00 |
+----+------+---------+
3 rows in set (0.00 sec)

mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM account;
+----+------+---------+
| id | NAME | balance |
+----+------+---------+
|  1 | zs   | 1000.00 |
|  2 | ls   | 1000.00 |
|  3 | ww   | 1000.00 |
+----+------+---------+
3 rows in set (0.00 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE account SET balance=balance-100 WHERE name = 'zs';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT * FROM account;
+----+------+---------+
| id | NAME | balance |
+----+------+---------+
|  1 | zs   |  900.00 |
|  2 | ls   | 1000.00 |
|  3 | ww   | 1000.00 |
+----+------+---------+
3 rows in set (0.00 sec)

mysql> UPDATE account SET balance=balance+100 WHERE name = 'ls';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT * FROM account;
+----+------+---------+
| id | NAME | balance |
+----+------+---------+
|  1 | zs   |  900.00 |
|  2 | ls   | 1100.00 |
|  3 | ww   | 1000.00 |
+----+------+---------+
3 rows in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.02 sec)

mysql> SELECT * FROM account;
+----+------+---------+
| id | NAME | balance |
+----+------+---------+
|  1 | zs   |  900.00 |
|  2 | ls   | 1100.00 |
|  3 | ww   | 1000.00 |
+----+------+---------+
3 rows in set (0.00 sec)

 

四、JDBC事務

在JDBC中處理事務,都是通過Connection完成的。

同一事務中所有的操作,都在使用同一個Connection對象。

①JDBC中的事務    

Connection的三個方法與事務有關:

  • setAutoCommit(boolean):設置是否爲自動提交事務,如果true(默認值爲true)表示自動提交,也就是每條執行的SQL語句都是一個單獨的事務,如果設置爲false,表示開啓事務,禁止自動提交;con.setAutoCommit(false) 表示開啓事務。
  • commit():提交結束事務。
  • rollback():回滾結束事務。

JDBC處理事務的代碼格式:

try{
     con.setAutoCommit(false);//開啓事務
     ......
     con.commit();//try的最後提交事務      
} catch() {
    con.rollback();//回滾事務
}

 

示例:

public class AccountDao {
    /*
    * 修改指定用戶的餘額
    * */
    public void updateBalance(Connection con, String name,double balance) {
        try {
            String sql = "UPDATE account SET balance=balance+? WHERE name=?";
            PreparedStatement pstmt = con.prepareStatement(sql);
            pstmt.setDouble(1,balance);
            pstmt.setString(2,name);
            pstmt.executeUpdate();
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

 

 

import cn.itcast.jdbc.JdbcUtils;
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;

public class Demo1 {
    /*
    * 演示轉賬方法
    * 所有對Connect的操作都在Service層進行的處理
    * 把所有connection的操作隱藏起來,這需要使用自定義的小工具(day19_1)
    * */
    public void transferAccounts(String from,String to,double money) {
        //對事務的操作
        Connection con = null;
        try{
            con = JdbcUtils.getConnection();
            con.setAutoCommit(false);  //開啓事務,禁止自動提交
            AccountDao dao = new AccountDao();
            dao.updateBalance(con,from,-money);//給from減去相應金額
            if (true){
                throw new RuntimeException("不好意思,轉賬失敗");
            }
            dao.updateBalance(con,to,+money);//給to加上相應金額
            //提交事務
            con.commit();

        } catch (Exception e) {
            try {
                con.rollback();
            } catch (SQLException e1) {
                e.printStackTrace();
            }
            throw new RuntimeException(e);
        }
    }
    @Test
    public void fun1() {
        transferAccounts("zs","ls",100);
    }
}

五、事務隔離級別

1、事務的併發讀問題

  • 髒讀:讀取到另外一個事務未提交數據(不允許出來的事);
  • 不可重複讀:兩次讀取不一致;
  • 幻讀(虛讀):讀到另一事務已提交數據。

2、併發事務問題

因爲併發事務導致的問題大致有5類,其中兩類是更新問題三類是讀問題。

  • 髒讀(dirty read):讀到另一個事務的未提交新數據,即讀取到了髒數據;

例如當事務A與事務B併發執行時,當事務A更新後,事務B查詢讀到A尚未提交的數據,此時事務A回滾,則事務B讀到的數據是無效的“髒”數據。(事務A讀取了事務B更新的數據,然後B回滾操作,那麼A讀取到的數據是髒數據)

  • 不可重複讀(unrepeatable):對同一記錄的兩次讀取不一致,因爲另一事務對該記錄做了修改;

例如事務A與事務B併發執行時,當事務B查詢讀取數據後,事務A更新操作更改事務B查詢到的數據,此時事務B再次讀該數據,發現前後兩次數據不一樣。

  • 幻讀(虛讀)(phantom read):對同一張表的兩次查詢不一致,因爲另一事務插入了一條記錄。

例如當事務A與事務B併發執行時,當事務B查詢讀取數據後,事務A新增或刪除了一條滿足事務A的查詢條件的記錄,此時事務B再次查詢,發現查詢到前次不存在的記錄,或者前次的某個記錄不見了。

3、四大隔離級別

    4個等級的事務隔離級別,在相同的數據環境下,使用相同的輸入,執行相同的工作,根據不同的隔離級別,可以導致不同的結果。不同事務隔離級別能夠解決的數據併發問題的能力是不同的。

1、SERIALIZABLE(串行化)

  • 不會出現任何併發問題,因爲它是對同一數據的訪問是串行的,非併發訪問的;
  • 性能最差

2、REPEATABLE READ(可重複讀)(MySQL)

  • 防止髒讀和不可重複讀,不能處理幻讀
  • 性能比SERIALIZABLE好

3、READ COMMITTED(讀已提交數據)(Oracle)

  • 防止髒讀,不能處理不可重複讀和幻讀;
  • 性能比REPEATABLE READ好

4、READ UNCOMMITTED(讀未提交數據)

  • 可能出現任何事物併發問題,什麼都不處理。
  • 性能最好

六、MySQL隔離級別

MySQL的默認隔離級別爲Repeatable read,可以通過下面語句查看:

SELECT @@`TX_ISOLATION`;

也可以通過下面語句來設置當前連接的隔離級別:

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ ;//[4選1]

七、JDBC設置隔離級別

con.setTransactionIsolation(int level) :參數可選值如下:

  • Connection.TRANSACTION_NONE JDB;不支持事務
  • Connection.TRANSACTION_READ_UNCOMMITTED;未提交讀。說明在提交前一個事務可以看到另一個事務的變化,這樣讀“髒”數據、不可重複讀或者虛讀都是允許的。
  • Connection.TRANSACTION_READ_COMMITTED;已提交讀。說明讀取未提交的數據是不允許的。這個級別仍然允許不可重複讀和虛讀產生。
  • Connection.TRANSACTION_REPEATABLE_READ;可重複讀。說明事務保證能夠再次讀取相同的數據而不會失敗,但虛讀仍然會出現。
  • Connection.TRANSACTION_READ_SERIALIZABLE。可序列化。是最高的事務級別,它防止讀“髒”數據、不可重複讀和虛讀。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章