MySQL事務(transaction)之手動控制sql事務及JDBC事務的開啓

1.概念

事務指邏輯上的一組操作,組成這組操作的各個單元,要麼全部成功,要麼全部不成功。

2.管理事務

2.1數據庫默認的事務

數據庫默認支持事務,默認的事務是:一條sql一個事務。

2.2手動控制事務

sql控制事務

開啓事務:start transaction;

開啓事務後,在這條語句之後的所有sql都將處在同一個事務中

提交事務:commit;

從開啓事務到提交事務之間的所有sql一起執行

回滾事務:rollback;

從開啓事務到回滾事務之間的所有sql一起失效

sql腳本示例如下:

說明:當開啓事務START TRANSACTION;後執行給a用戶扣款100,給b用戶加錢100,如果commit,那麼a被扣除100,b則增加100。如果執行rollback,那麼a和b的金額就會保持不變。

#slq控制事務,
#開啓事務
START TRANSACTION;
UPDATE myuser SET money= money-100 WHERE name='a';
UPDATE myuser SET money= money+100 WHERE name='b';
COMMIT;
#回滾會取消開啓到結束的所有sql
ROLLBACK;
#查看修改後的數據,驗證事務
SELECT* FROM myuser;

JDBC控制事務

開啓事務:conn.setAutoCommit(false);

jdbc中sql會自動提交,如果設置sql不自動提交,就需要我們手動提交,即提交前的所有sql在一個事務中

提交事務:conn.commit();

回滾事務:conn.rollback();

示例代碼如下:

說明:測試類TransactionDemo1用於測試JDBC的事務控制,其中會用到JdbcUtils工具類(代碼可參見:https://blog.csdn.net/qq_32224047/article/details/106722020

情況一:不開啓事務,在sql1和sql2中間加入異常int i = 1/0;

package cn.wy.jdbc.transaction;

import cn.wy.jdbc.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * JDBC 事務測試類
 * @author wyy
 * @date 2020/6/23 10:33
 */
public class TransactionDemo1 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            String sql1 = "UPDATE myuser SET money= money-100 WHERE name=?";
            ps = conn.prepareStatement(sql1);
            ps.setString(1,"a");
            ps.executeUpdate();
            //加入錯誤,驗證事務不開啓,jdbc自動提交(a被扣錢,b沒有加錢,因爲默認提交,上面的sql1執行,slq2 爲執行)
            int i = 1/0;

            String sql2 = "UPDATE myuser SET money= money+100 WHERE name=?";
            ps = conn.prepareStatement(sql2);
            ps.setString(1,"b");
            ps.executeUpdate();     

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            JdbcUtils.close(rs,conn,ps);
        }
    }
}

代碼執行前a和b用戶金額都是1000

執行上面代碼拋出異常

查詢數據庫a用戶被扣除100元,但是b用戶並沒有增加100元,因爲jdbc默認是開啓了事務管理,當執行完成sql1後,出現異常,無法在執行到sql2,所有sql1執行後被提交,之後的數據就不在執行,就出現了下面的查詢結果。

注意:在執行事務相關的操作時需要先檢查一下當前數據庫的隔離級別,MySQL默認隔離級別:repeatable read。可以用select @@tx_isolation;查詢當前的隔離級別。當前的測試都是在默認的隔離級別下執行。如果是其他的隔離級別可能無法得到該測試想要的結果。

隔離級別的檢查如下:

情況二:開啓事務,在sql1和sql2中間加入異常int i = 1/0;

package cn.wy.jdbc.transaction;

import cn.wy.jdbc.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * JDBC 事務測試類
 * @author wyy
 * @date 2020/6/23 10:33
 */
public class TransactionDemo1 {
    public static void main(String[] args) {
            //開啓事務
            conn.setAutoCommit(false);
            conn = JdbcUtils.getConnection();
            String sql1 = "UPDATE myuser SET money= money-100 WHERE name=?";
            ps = conn.prepareStatement(sql1);
            ps.setString(1,"a");
            ps.executeUpdate();
            //加入錯誤,驗證事務(a被扣錢,b沒有加錢,因爲默認提交,上面的sql1執行,slq2 爲執行)
            int i = 1/0;

            String sql2 = "UPDATE myuser SET money= money+100 WHERE name=?";
            ps = conn.prepareStatement(sql2);
            ps.setString(1,"b");
            ps.executeUpdate();

            //提交事務
            conn.commit();

        }catch (Exception e){
            e.printStackTrace();
            //如果出現任何異常則,執行回滾
            if (conn!=null){
                try {
                    conn.rollback();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }finally {
            JdbcUtils.close(rs,conn,ps);
        }
    }
}

執行代碼前先將a和b用戶的賬戶金額都還原爲1000

執行程序,拋出NullPointerException異常

查詢數據庫,結果並未改變,由於開啓了事務,當出現異常時,後在catch中捕獲後執行回滾(rollback),從而保證一個事務要麼全部成功,要麼全部不成功。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章