1. 程式人生 > >事務例項--銀行轉賬

事務例項--銀行轉賬

首先,舉個例子:
銀行(bank)中有兩個客戶(name)張三和李四

我們需要將張三的1000元存款(sal)轉到李四的賬戶上

我們需要怎要通過sql語句來實現這個過程
update bank set sal = sal - 1000 where name = '張三';
update bank set sal = sal + 1000 where name = '李四';

但是萬一出現一些錯誤,比如將欄位名稱打錯了,沒有檢查就執行了這個兩個語句,比如
update bank set sal = sal - 1000 where name = '張三';
update bank set sale = sale + 1000 where name = '李四';
我們通過查詢資料庫會發現 張三依然減少了1000元但是李四卻沒有加錢

如果可以有一種方法使得sql語句要麼都執行,要麼裡面有一句沒有執行,就全部不執行,

這時候就要引入事務這個概念
事務是一個最小的、不可分割的工作單元,不論成功與否都作為一個整體進行工作。

事物具有哪些特性
事務都應該具備ACID特徵。所謂ACID是Atomic(原子性),Consistent(一致性),Isolated(隔離性),Durable(永續性)四個詞的首字母所寫.

使用銀行轉賬來解釋一下每種特性的含義:
原子性:組成事務處理的語句形成了一個邏輯單元,不能只執行其中的一部分。換句話說,事務是不可分割的最小單元。所以:銀行轉帳過程中,必須同時從一個帳戶減去轉帳金額,並加到另一個帳戶中,只改變一個帳戶是不合理的。

一致性:在事務處理執行前後,MySQL資料庫是一致的。也就是說,事務應該正確的轉換系統狀態。所以:銀行轉帳過程中,要麼轉帳金額從一個帳戶轉入另一個帳戶(

在不考慮轉賬費用的情況下,轉賬方減少的金額與收賬方的增加的金額應該是相等),要麼兩個帳戶都不變,沒有其他的情況。

隔離性:一個事務處理對另一個事務處理沒有影響。就是說任何事務都不可能看到一個處在不完整狀態下的事務。比如說,銀行轉帳過程中,在轉帳事務沒有提交之前,另一個轉帳事務只能處於等待狀態。

永續性:事務處理的效果能夠被永久儲存下來。轉賬結果能夠在無論發生什麼情況下都能儲存下來.

Mysql支援事務的儲存引擎有:BDB、InnoDB,如果我需要使用儲存引擎則資料庫資料使用的儲存引擎應該是以上兩種。

mysql中如何使用事務


BEGIN 開始一個事務
ROLLBACK 回滾會結束使用者的事務,並撤銷正在進行的所有未提交的修改
COMMIT
提交事務,並使已對資料庫進行的所有修改成為永久性的

模擬銀行轉賬
public boolean transfrom(int fromId, int toId, double amount)
    {
        Connection con = JDBCUtil.getConnection();
        String sql1 = "update tb_account set balance = balance - ? where userId = ?;";
        String sql2 = "update tb_account set balance = balance + ? where userId = ?;";
        String sql3 = "select balance  from tb_account where userId = ?;";
        PreparedStatement ps1 = null;
        PreparedStatement ps2 = null;
        PreparedStatement ps3 = null;
        ResultSet rs = null;
        try
        {
            //開啟事務
            con.setAutoCommit(false);
            //轉賬時涉及的兩個賬戶以及各賬戶的金額變動
            ps1 = con.prepareStatement(sql1);
            ps1.setDouble(1, amount);
            ps1.setInt(2, fromId);
            ps1.executeUpdate();

            ps2 = con.prepareStatement(sql2);
            ps2.setDouble(1, amount);
            ps2.setInt(2, toId);
            ps2.executeUpdate();

            //檢查轉出方賬戶的餘額是否足夠支援此次轉賬金額;如果餘額不足,則丟擲“餘額不足”異常,並回滾
            ps3 = con.prepareStatement(sql3);
            ps3.setInt(1, fromId);
            rs = ps3.executeQuery();
            Double balance = 0.0;
            if(rs.next())
            {
                balance = rs.getDouble("balance");
            }
            if(balance < 0)
            {
                throw new Exception("賬戶餘額不足");
            }
            con.commit();
            return true;
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        try
        {
            con.rollback();
        } catch (SQLException e)
        {
            e.printStackTrace();
        }finally{
            try
            {
                rs.close();
                ps1.close();
                ps2.close();
                ps3.close();
                con.close();
            } catch (SQLException e)
            {
                e.printStackTrace();
            }
        }
        return false;
    }



SET TRANSACTION:用來設定事務的隔離級別,主要用於解決sql中的併發問題.InnoDB儲存引擎提供事務的隔離級別有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE

具體介紹四個隔離級別
1.READ UNCOMMITTED(未提交讀)。在RU的隔離級別下,事務A對資料做的修改,即使沒有提交,對於事務B來說也是可見的,這種問題叫髒讀。這是隔離程度較低的一種隔離級別,在實際運用中會引起很多問題,因此一般不常用。
2.READ COMMITTED(提交讀),一個事務從開始直到提交之前,所做的任何修改對其他事務都是不可見的。這個級別有時候也叫做不可重複讀,因為兩次執行相同的查詢,可能會得到不一樣的結果。因為在這兩次讀之間可能有其他事務更改這個資料,每次讀到的資料都是已經提交的。
3.REPEATABLE READ(可重複讀),解決了髒讀,也保證了在同一個事務中多次讀取同樣記錄的結果是一致的。但是理論上,可重複讀隔離級別還是無法解決另外一個幻讀的問題,指的是當某個事務在讀取某個範圍內的記錄時,另外一個事務也在該範圍內插入了新的記錄,當之前的事務再次讀取該範圍內的記錄時,會產生幻行。
4.SERIALIZABLE(可序列化),它通過強制事務序列執行,避免了前面說的幻讀的問題,但由於讀取的每行資料都加鎖,會導致大量的鎖徵用問題,因此效能也最差。
直觀地理解參考https://www.cnblogs.com/huanongying/p/7021555.html.