事務。MySQL事務是對于更新(插入,修改,删除)來說的,查詢好像沒有事務不事務一說。
目錄
一:JDBC事務介紹
1.事務及事務區:
(1)【commit送出】
(2)【rollback復原】
2.JDBC兩種事務模式:
(1)自動送出事務模式:
(2)手動送出事務模式:
二:事務案例
1.沒有使用【手動送出事務】模式時
(1)TransactionSample類編寫
(2)運作結果
(3)原因分析
2.使用【手動送出事務】模式時
(1)TransactionSample類編寫(出錯的時候)
(2)運作結果:(出錯的時候)
(3)程式沒有出錯,正常執行
一:JDBC事務介紹
MySQL之是以能在實際中使用,就是因為其有事務機制。MySQL,Oracle這樣的關系型資料庫都是支援事務的;也有很多小衆的資料庫是不支援事務的。
1.事務及事務區:
(1)【commit送出】
● 應用程式在寫入資料時,并不是直接把資料放入到表中,而是先把資料放在事務區中;
● 事務區是MySQL自帶的;如果資料很小的時候,事務區中的資料放在記憶體中,以加快處理速度;如果資料比較大的時候,其會用一塊硬碟空間作為事務緩沖;
以A借給B100元為例:
首先,程式對A的餘額作了減法操作,這是一個寫操作,但是其不會直接反映到資料表中,而是先把這個操作的中間結果放在事務區中;
然後,程式對B的餘額作了加法的操作,同樣會把處理的中間結果放在事務區中
最後,當程式對這兩個更新操作全部執行完以後,會主動的向MySQL發起一個commit送出的指令,這個commit送出指令會直接作用到MySQL事務區上;當MySQL看到程式進行commit事務送出後,于是MySQL的事務區會将剛才的加和減兩個操作一次性的寫入到資料表中;
即,進行程式的寫操作的時候,兩次的記錄更新都是面向事務區的;隻有當進行commit送出的時候,才由事務區真正的反映到資料表中(啰嗦一下:對于真正寫入到資料表中的操作叫做“commit送出”);
當送出成功後,事務區中的資料的資料就沒有意義了,由MySQL自動的把這個事務區清空,等待下一次應用程式再向事務區中進行新資料的寫入。
……………………………………………………
(2)【rollback復原】
以A借給B100元為例:
程式【對A的餘額作了減法操作】操作正常;但是,由于某種原因程式【對B的餘額作了加法的操作】的時候,程式報錯了;那麼JDBC的應用程式會向事務區發起一個【rollback復原指令】;MySQL收到了這個復原指令以後,其會直接将在事務區中原本已經處理好的【對A的餘額作了減法操作】給清空;
即程式一旦向MySQL送出了【rollback復原指令】以後,無論之前做了多少資料的前置處理,MySQL都會直接将事務區清空,最終的資料表中不會産生任何實質的寫入操作;
事務總結一句話:作為事務來說,要麼一次性全部完成;要麼将之前所有已經做的事情通過復原全部撤銷。
實際的Java代碼,主要是通過控制【commit送出】和【rollback復原】指令的時機,隻有應用程式發起了這樣的指令,MySQL才可以執行對應的操作。
……………………………………………………
2.JDBC兩種事務模式:
(1)自動送出事務模式:
自動送出事務模式是JDBC的預設模式。在每一次執行寫操作(新增、修改、删除)的時候,每完成一次SQL語句都會自動的送出事務;
如果在程式中沒有顯式的寫【conn.setAutoCommit(true)】,其預設就是自動送出事務模式;
因為每一次執行寫操作(新增、修改、删除)的以後,其都會立即送出;;是以,複雜的業務(需要多條寫操作一起)的情況下,無法保證多資料的一緻性;
……………………………………………………
(2)手動送出事務模式:
commit()方法和rollback()方法都是Connection接口中定義的;
話句話說,事務設定為手動送出,事務的的送出和事務復原,都是需要通過Connection連接配接來調用方法實作的。很顯然,因為事務本來就是對于一次資料庫連接配接來說的嘛!!!!!
二:事務案例
比如,一個公司入職1000個員工,這1000個人要麼一次性全部入職,要麼一個都不入職。像這種要麼全做,要麼什麼都不做的事情,基于資料庫的事務進行處理,是非常合适的。
、
1.沒有使用【手動送出事務】模式時
(1)TransactionSample類編寫
TransactionSample類:
(1)沒有使用事務;(2)預期要求是,1000個人要麼全部入職,要麼一個也不入職;(3)為了模拟過程中出現的程式報錯終端,使用了一個抛出異常(沒有對這個抛出的異常捕獲啦,目的就是使程式中斷執行);
package com.imooc.jdbc.sample;
import com.imooc.jdbc.common.DbUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TransactionSample {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = DbUtils.getConnection();
String sql = "insert into employee(eno,ename,salary,dname) values(?,?,?,?)";
for (int i = 4000; i < 5000; i++) {
if (i == 4005) {
throw new RuntimeException("插入失敗");
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, i);
pstmt.setString(2, "員工" + i);
pstmt.setFloat(3, 4000);
pstmt.setString(4, "市場部");
pstmt.executeUpdate();
}
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
DbUtils.closeConnection(null,pstmt,conn);
}
System.out.println("你好啊。");
}
}
(2)運作結果
運作結果:運作結果不符合預期(1000個人要麼全部入職,要麼一個都不要入職);
額外說明一點:因為上面程式中是抛出的【RuntimeException】,而在catch塊中沒有對【RuntimeException】的捕獲,是以異常發生的時候,程式會中斷,是以最後的【System.out.println("你好啊。");】沒有執行。
(3)原因分析
失敗原因分析:
● 預設情況下,JDBC會使用自動送出的模式;
● 自動送出:在前面執行的過程中,每執行一次update,即每執行一次【pstmt.executeUpdate();】,就立馬對資料進行送出。是以,上面會出現【程式中斷,但資料表中卻出現了5條資料】的情況。
為了解決這個問題,需要在得到資料庫連接配接對象之後,對其進行設定,以采用【手動送出事務】模式;
2.使用【手動送出事務】模式時
(1)TransactionSample類編寫(出錯的時候)
事務的設定,都是針對Connection對象來的!!!
package com.imooc.jdbc.sample;
import com.imooc.jdbc.common.DbUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TransactionSample {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = DbUtils.getConnection();
conn.setAutoCommit(false); // 關閉自動送出;
String sql = "insert into employee(eno,ename,salary,dname) values(?,?,?,?)";
for (int i = 4000; i < 5000; i++) {
if (i == 4005) {
throw new RuntimeException("插入失敗");
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, i);
pstmt.setString(2, "員工" + i);
pstmt.setFloat(3, 4000);
pstmt.setString(4, "市場部");
pstmt.executeUpdate();
}
conn.commit();//送出資料
}catch (Exception e) { //一旦上面在執行的過程中,抛出裡異常;在catch塊中,必須要對其進行捕捉;
e.printStackTrace();
try {
if (conn != null && conn.isClosed() == false) {
conn.rollback(); //資料復原
}
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
DbUtils.closeConnection(null, pstmt, conn);
}
System.out.println("你好啊。");
}
}
(1)當【conn.setAutoCommit(false); 】關閉自動送出後,每一次執行【pstmt.executeUpdate();】的時候,所産生的中間資料會被放入到事務區中;
(2)在程式報錯,捕捉異常,然後進行復原的時候,catch塊隻保留了最大的【catch (Exception e)】;;;;;然後把復原的操作放在了catch塊中,這或許也是為什麼隻留最大的Exception的原因吧,。
(3)異常被捕獲後,程式不會中中斷,會繼續執行;
(2)運作結果:(出錯的時候)
(3)程式沒有出錯,正常執行
當程式正常執行的時候:
把這個異常抛出給注釋掉,這個過程會正常執行;
運作效果:
在實際開發中,大多數的應用都是需要手動事務控制的。尤其是在金融項目中,資料強一緻性的業務,必須要基于資料庫的事務,而且要手動控制事務。
自我感覺,在實際中,某個業務所需的全部SQL操作,需要寫在一個資料庫連接配接(Connection對象)中,技術上是可以操作的,隻是見得情況不多,沒有感性的認識。