事務的概念
事務是訪問並更新資料庫中各個資料項的一個程式執行單元;在事務操作中,要麼都做修改,要麼什麼都不做。
MySQL中只有使用了Innodb資料庫引擎的資料庫或表才支援事務;事務用來管理insert,update,delete語句;
事務的型別資料庫事務型別,有本地事務和分散式事務:
1、本地事務:就是普通事務,能保證單臺數據庫上的操作的ACID,被限定在一臺資料庫上;
2、分散式事務:涉及兩個或多個數據庫源的事務,即跨越多臺同類或異類資料庫的事務(由每臺數據庫的本地事務組成的),分散式事務旨在保證這些本地事務的所有操作的ACID,使事務可以跨越多臺資料庫;
Java的事務型別,有JDBC事務和JTA事務:
1、JDBC事務:資料庫事務型別中的本地事務,透過Connection物件的控制來管理事務;
2、JTA事務:JTA指Java事務API(Java Transaction API),是Java EE資料庫事務規範, JTA只提供了事務管理介面,由應用程式伺服器廠商(如:WebSphere Application Server)提供實現,JTA事務比JDBC更強大,支援分散式事務。
Java EE事務型別有本地事務和全域性事務:
1、本地事務:使用JDBC程式設計實現事務;
2、全域性事務:由應用程式伺服器提供,使用JTA事務;
Java EE按是否透過程式設計實現事務有宣告式事務和程式設計式事務:
1、宣告式事務: 透過註解或XML配置檔案指定事務資訊;
2、程式設計式事務:透過編寫程式碼實現事務;
事務具有ACID四個特性分別是:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、永續性(Durability):
原子性:一個事務(transaction)中的所有操作,要麼全部完成,要麼全部不完成,不會結束在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
一致性:在事務開始之前和事務結束以後,資料庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預設規則,這包含資料的精確度、串聯性以及後續資料庫可以自發性地完成預定的工作。
隔離性:允許多個併發事務同時對其資料進行讀寫和修改的能力,隔離性可以防止多個事務併發執行時由於交叉執行而導致資料的不一致。事務隔離分為不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)和序列化(Serializable)。
永續性:事務處理結束後,對資料的修改就是永久的,即便系統故障也不會丟失。
MySQL事務處理主要有兩種方法:1、用BEGIN, ROLLBACK, COMMIT來實現BEGIN 開始一個事務ROLLBACK 事務回滾COMMIT 事務確認2、直接用SET來改變MySQ的自動提交模式SET AUTOCOMMIT=0 禁止自動提交SET AUTOCOMMIT=1 開啟自動提交
事務隔離級別
第一種隔離級別:Read uncommitted(讀未提交),併發時存在問題:幻讀、不可重複讀、髒讀
如果一個事務已經開始寫資料,則另外一個事務不允許同時進行寫操作,但允許其他事務讀此行資料,該隔離級別可以透過“排他寫鎖”,但是不排斥讀執行緒實現。這樣就避免了更新丟失,卻可能出現髒讀,也就是說事務B讀取到了事務A未提交的資料。
第二種隔離級別:Read committed(讀提交),併發時存在問題:幻讀、不可重複讀
如果是一個讀事務(執行緒),則允許其他事務讀寫,如果是寫事務將會禁止其他事務訪問該行資料,該隔離級別避免了髒讀,但是可能出現不可重複讀。事務A事先讀取了資料,事務B緊接著更新了資料,並提交了事務,而事務A再次讀取該資料時,資料已經發生了改變。
第三種隔離級別:Repeatable read(可重複讀取),併發時存在問題:幻讀
可重複讀取是指在一個事務內,多次讀同一個資料,在這個事務還沒結束時,其他事務不能訪問該資料(包括了讀寫),這樣就可以在同一個事務內兩次讀到的資料是一樣的,因此稱為是可重複讀隔離級別,讀取資料的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務(包括了讀寫),這樣避免了不可重複讀和髒讀,但是有時可能會出現幻讀。(讀取資料的事務)可以透過“共享讀鏡”和“排他寫鎖”實現。
第四種隔離級別:Serializable(可序化)
提供嚴格的事務隔離,它要求事務序列化執行,事務只能一個接著一個地執行,但不能併發執行,如果僅僅透過“行級鎖”是無法實現序列化的,必須透過其他機制保證新插入的資料不會被執行查詢操作的事務訪問到。序列化是最高的事務隔離級別,同時代價也是最高的,效能很低,一般很少使用,在該級別下,事務順序執行,不僅可以避免髒讀、不可重複讀,還避免了幻讀;
MySQL預設事務的隔離級別:REPEATABLE-READMySQL檢視當前事務的隔離級別:SELECT @@tx_isolation;
JDBC事務隔離級別設定:
import com.what21.boot.jdbc.operate.JDBCHelper;import java.sql.Connection;import java.sql.SQLException;/** * 事務隔離(isolation)級別 */public class JDBCTransactionLevel { /** * 隔離(isolation)級別 * * @param connection * @throws SQLException */ public static int isolationLevel(Connection connection) throws SQLException { int isolationLevel = connection.getTransactionIsolation(); if (isolationLevel == Connection.TRANSACTION_NONE) { System.out.println("無事務,級別: " + isolationLevel + "=TRANSACTION_NONE"); } else if (isolationLevel == Connection.TRANSACTION_READ_UNCOMMITTED) { System.out.println("讀未提交,級別: " + isolationLevel + "=TRANSACTION_READ_UNCOMMITTED"); } else if (isolationLevel == Connection.TRANSACTION_READ_COMMITTED) { System.out.println("讀已提交,級別:" + isolationLevel + "=TRANSACTION_READ_COMMITTED"); } else if (isolationLevel == Connection.TRANSACTION_REPEATABLE_READ) { System.out.println("可重複讀,級別:" + isolationLevel + "=TRANSACTION_REPEATABLE_READ"); } else if (isolationLevel == Connection.TRANSACTION_SERIALIZABLE) { System.out.println("可序列化,級別:" + isolationLevel + "=TRANSACTION_SERIALIZABLE"); } return isolationLevel; } public static void main(String[] args) { // SELECT @@tx_isolation; // REPEATABLE-READ Connection connection = JDBCHelper.createConnection(); try { System.out.println(isolationLevel(connection)); } catch (SQLException e) { e.printStackTrace(); } JDBCHelper.close(connection); }}
JDBC的事務支援1、自動提交模式(Auto-commit mode)
2、事務隔離級別(Transaction Isolation Levels)
3、儲存點(SavePoint)
JDBC定義了SavePoint介面,提供在一個更細粒度的事務控制機制。當設定了一個儲存點後,可以rollback到該儲存點處的狀態,而不是rollback整個事務。
程式碼案例:
import java.sql.Connection;import java.sql.SQLException;import java.sql.Savepoint;import java.sql.Statement;public class JDBCTransactionSupportDemo { /** * 自動提交模式(Auto-commit mode) */ public static void supportMethod1() { Connection connection = JDBCHelper.createConnection(); try { connection.setAutoCommit(true); insert(connection); } catch (SQLException e) { e.printStackTrace(); } JDBCHelper.close(connection); } /** * @param connection */ public static void insert(Connection connection) throws SQLException { Statement stat = connection.createStatement(); String sql = "insert into users(name,email,phone,mobile) value('name','email','phone','mobile')"; stat.execute(sql); JDBCHelper.close(stat); } /** * 事務隔離級別(Transaction Isolation Levels) */ public static void supportMethod2() { Connection connection = JDBCHelper.createConnection(); try { connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); connection.setAutoCommit(false); insert(connection); } catch (SQLException e) { e.printStackTrace(); try { connection.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } finally { try { connection.commit(); } catch (SQLException e) { e.printStackTrace(); } } JDBCHelper.close(connection); } /** * 儲存點(SavePoint) */ public static void supportMethod3() { Connection connection = JDBCHelper.createConnection(); Savepoint oneSavepoint = null; Savepoint twoSavepoint = null; Savepoint threeSavepoint = null; try { connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); connection.setAutoCommit(false); // 設定儲存點 oneSavepoint = connection.setSavepoint("one"); insert(connection); twoSavepoint = connection.setSavepoint("two"); insert(connection); // 釋放儲存點 connection.releaseSavepoint(oneSavepoint); threeSavepoint = connection.setSavepoint("three"); insert(connection); } catch (RuntimeException e) { e.printStackTrace(); try { connection.rollback(threeSavepoint); } catch (SQLException ex) { ex.printStackTrace(); } } catch (SQLException e) { e.printStackTrace(); try { connection.rollback(twoSavepoint); } catch (SQLException ex) { ex.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); try { connection.rollback(oneSavepoint); } catch (SQLException ex) { ex.printStackTrace(); } } finally { try { connection.commit(); } catch (SQLException e) { e.printStackTrace(); } } JDBCHelper.close(connection); } public static void main(String[] args) { supportMethod1(); supportMethod2(); supportMethod3(); }}