1. 程式人生 > >MySql事務(JDBC手動控制事務 事務的特性 事務的隔離級別)

MySql事務(JDBC手動控制事務 事務的特性 事務的隔離級別)

MySql事務

事務的概述:

事務指邏輯上的一組操作,組成這組操作的各個單元,要麼全部成功,要麼全部不成功。
事務案例:
A–B轉賬,有以下兩條sql語句:
update count set money=money+100 where name=’aaa’;
update count set money=money-100 where name=’bbb’;
其中一條sql語句失敗,則在此事務內的其他sql語句全部失敗,要麼一起成功,要麼一起失敗

Mysql中的事務

a、mysql引擎是支援事務的
b、mysql預設自動提交事務。每條語句都處在單獨的事務中。
c、手動控制事務


開啟事務:start transaction | begin conn.setAutoCommit(false);
提交事務: commit conn.commit();
回滾事務: rollback conn.rollback();

JDBC事務

JDBC事務是由Connection物件所控制的,它提供了兩種事務模式:自己主動提交和手動提交,預設是自己主動提交。

   自己主動提交就是:在JDBC中。在一個連線物件Connection中。預設把每一個SQL語句的執行都當做是一個事務(即每次執行完SQL語句都會馬上將操作更新到資料庫)。

   手動提交就是:當須要一次性執行多個SQL語句,將多個SQL語句組成一個事務(即要麼都成功,要麼回滾全部的操作)時,就得手動提交。

在命令列中觀察主動提交:

進入mysql:

mysql -u 使用者名稱 -p 密碼

這裡寫圖片描述
進入holiday資料庫:

use holiday;

這裡寫圖片描述
在mysql直接寫一個sql語句(所以是預設自動提交的):
執行前

這裡寫圖片描述
這裡寫圖片描述

執行後:
這裡寫圖片描述

在命令列中觀察手動提交:

開啟事務:

start transaction|begin

這裡寫圖片描述
這裡寫圖片描述

因為開啟事務後並沒有commit(提交),所以即使記憶體中資料變化了,但是沒有提交,並沒有存檔,所以表的資料依然沒有變化!
這裡寫圖片描述

在JDBC中實現事務:

程式碼:
沒有手動控制事務(執行事務前):
這裡寫圖片描述
程式碼:

package
commons; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class TestTransaction { public static void main(String[] args){ Connection conn = null; PreparedStatement ps = null; try { conn = DBCPUtils.openConnection(); ps = conn.prepareStatement("update count set money=money-100 where name='aaa'"); ps.execute(); int i=10/0;//在兩條sql語句中間出現異常 ps = conn.prepareStatement("update count set money=money+100 where name='bbb'"); ps.execute(); } catch (SQLException e) { e.printStackTrace(); }finally { DBCPUtils.closeConnection(conn,ps,null); } } }

執行結果:
aaa執行了sql語句,bbb沒執行
aaa給bbb轉賬100元,aaa少錢了,bbb的錢確沒多!
這裡寫圖片描述

修改後,手動控制事務,一個事務中的所有sql語句失敗一起失敗,成功一起成功

conn.setAutoCommit(false);//相當於開啟事務 begin
 conn.commit();//相當於 提交事務
conn.rollback();//回滾事務  rollback

程式碼:

package commons;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestTransaction {
    public static void main(String[] args){
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = DBCPUtils.openConnection();
            conn.setAutoCommit(false);//相當於開啟事務 begin
            ps = conn.prepareStatement("update count set money=money-100 where name='aaa'");
            ps.execute();
            int i=10/0;//在兩條sql語句中間出現異常
            ps = conn.prepareStatement("update count set money=money+100 where name='bbb'");
            ps.execute();
            conn.commit();//相當於 提交事務
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                conn.rollback();//只要出現異常  回滾事務  rollback
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }finally {
            DBCPUtils.closeConnection(conn,ps,null);
        }
    }

}

事務的特性(面試題)

原子性:事務是一個整體,事務中的操作要麼都發生,要麼都不發生;
一致性:事務必須要使資料庫從一個一致性狀態轉換到另外一個一致性狀態,即轉賬的總金額不變;
隔離性:多個使用者併發訪問資料庫時,多個併發事務之間要相互隔離,誰也別影響誰;
永續性:指一個事務一旦被提交,它對資料庫的改變就是永久的,即使資料庫發生故障也不應該對其有任何影響。即提交後必須存檔。

事務的隔離級別

1.髒讀:使用者當前操作的事務讀到了另一個事務未提交的資料

例子:
我現在正在取錢,賬戶裡有500,我沒有提交取錢的這個事務的時候,一哥們正好現在要給我轉賬,他給我轉300,但他也沒有提交,就給我打了電話,老王我給你轉賬了300,我一看賬戶當前餘額800,我說好的。然後這哥們啪,rollback了,我一看賬戶500……
這就是讀到了別人沒有提交的髒資料

2.不可重複讀和虛讀(幻讀)很類似,一個是由update導致的,一個是由insert導致的

2.1 不可重複讀:我當前操作的事務還沒完成提交,就讀取到了另一個事務提交到的資料。(update)
2.2 虛讀(幻讀):我當前操作的事務還沒完成提交,就讀取到了另一個事務插入提交的資料。(insert)

資料庫通過設定事務的隔離級別防止以上情況的發生:(有可能發生,不是一定發生
* 1、READ UNCOMMITTED: 贓讀、不可重複讀、虛讀都有可能發生。
* 2、READ COMMITTED: 避免贓讀。不可重複讀、虛讀都有可能發生。(oracle預設的)
* 4、REPEATABLE READ:避免贓讀、不可重複讀。虛讀有可能發生。(mysql預設)
* 8、SERIALIZABLE: 避免贓讀、不可重複讀、虛讀。
級別越高,效能越低,資料越安全

mysql中:
檢視當前的事務隔離級別:SELECT @@TX_ISOLATION;
更改當前的事務隔離級別:SET TRANSACTION ISOLATION LEVEL 四個級別之一。
設定隔離級別必須在事務之前

一、事務隔離級別為read uncommitted

髒讀,不可重複讀,虛讀都有可能發生
檢視mysql預設事務隔離級別:
這裡寫圖片描述
設定當前事務隔離級別 (在開啟事務之前設定) read uncommmitted(髒讀 不可重複讀 虛讀都有可能發生;因為讀取到了沒有提交的資料):
這裡寫圖片描述

進入資料庫,讀取當前資料:
這裡寫圖片描述

開啟另一個事務(執行緒)操作同一個資料,但是沒有提交:
這裡寫圖片描述

結果讀取到髒資料,沒有提交,另一個事務卻讀取到了提交後的資料:
這裡寫圖片描述

哥們我轉賬給你了啊,卻沒提交,rollback後,一臉懵逼:
這裡寫圖片描述

二、事務隔離級別為read committed

避免了髒讀,但是不可重複讀和虛讀都有可能發生

發生不可重複讀:
設定事務隔離級別為read committed後
開啟事務A,查詢count表
再開啟一個事務update B,並commit
事務A還沒commit就可查詢到事務B提交的資料
會導致事務A的查詢的資料一致變化,干擾到了事務A
這裡寫圖片描述

發生虛讀:
設定事務隔離級別為read committed後
開啟事務A,查詢count表
再開啟一個事務Binsert,並commit
事務A還沒commit就可查詢到事務B提交的資料
會導致事務A的查詢的資料一致變化,干擾到了事務A
這裡寫圖片描述

三、事務隔離級別為repeatable read

避免髒讀 不可重複讀 虛讀可能發生
這裡寫圖片描述

四、事務隔離級別為serializable

避免髒讀 不可重讀讀 虛讀 (最高級別)
只要當前事務不commit ,其他事務都不會執行
這裡寫圖片描述

JDBC控制事務的隔離級別

這裡寫圖片描述
READ UNCOMMITTED 等級: 1
READ COMMITTED 等級:2
REPEATABLE READ 等級:4
SERIALIZABLE 等級:8

如何在java程式中控制事務的隔離級別;
在開啟事務前設定:
Connection.setTransactionIsolation(int level);

但是一般不用寫
因為Oracle mysql已經自動設定好了級別