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已經自動設定好了級別