一、數據庫事務
數據庫事務(Database Transaction)是數據庫管理系統中的一個核心概念,它代表一組操作的集合,這些操作要么全部執行成功,要么全部不執行,即操作數據的最小執行單元,保證數據庫的數據一致性、完整性和可靠性。
一條數據庫語句也是事務。
1、事務的ACID特性
1.原子性(Atomicity)
定義:事務中的操作要么全部完成,要么全部不做。
類比:就像你去銀行進行轉賬操作,銀行要么把錢從你賬戶轉到對方賬戶,要么不做任何改變。如果途中出錯,轉賬的整個過程會被“撤銷”,就像根本沒有進行過轉賬一樣。
2.一致性(Consistency)
定義:事務開始之前和結束之后,數據庫必須保持一致的狀態。事務的執行不能破壞數據庫的規則(如約束、觸發器等)。
類比:銀行的轉賬必須符合規則,比如賬戶的余額不能為負。即使你操作了很多次,只要操作成功,每次操作之后的賬戶余額都應該是合法的。
3.隔離性(Isolation)
定義:事務執行的過程中,其它事務不能看到中間的結果,直到事務完全提交。
類比:假設你和另一個人在銀行同時轉賬,你的轉賬操作在完成前不會對對方的轉賬產生任何影響。如果沒有隔離性,兩個操作可能會影響對方的賬戶余額。
4.持久性(Durability)
定義:一旦事務提交,數據的修改是永久性的,即使系統崩潰,也不會丟失。
類比:轉賬一旦完成,銀行系統保證這筆轉賬數據會被保留下來,哪怕系統發生故障,數據依然能夠恢復。
2、事務的典型示例
假設你要從賬戶A轉賬100元到賬戶B,整個過程可能包括以下幾個操作:
從賬戶A扣款100元
向賬戶B存款100元
如果這兩個操作都成功完成,轉賬就完成了。但如果在第一個操作完成后,第二個操作失敗了(比如系統崩潰),那么賬戶A的100元已經被扣除了,但是賬戶B并沒有收到這100元。這種情況是不符合“原子性”的。
為了解決這個問題,數據庫使用事務來確保:
原子性:要么兩個操作都執行成功(100元從A扣除,B收到100元),要么兩個操作都不執行(賬戶A和賬戶B保持原狀)。
一致性:如果轉賬開始前的數據庫狀態是合法的(如余額不為負),那么轉賬完成后,數據庫狀態依然是合法的(A賬戶不會有負數,B賬戶的余額增加了100元)。
隔離性:在你完成轉賬前,別人無法看到你的轉賬結果,確保數據不會受到并發操作的影響。
持久性:一旦你提交了轉賬,無論系統發生什么故障,轉賬的數據都會保存下來。
3、MySQL客戶端演示事務
1.準備數據集
建表:
CREATE TABLE t_account (
id INT PRIMARY KEY auto_increment,
username VARCHAR ( 20 ),
money DOUBLE
);
插入測試數據:
INSERT INTO account
VALUES( 1, '美美', 10000 );
INSERT INTO account
VALUES( 2, '冠希', 10000 );
INSERT INTO account
VALUES( 3, '小鳳', 10000 );
INSERT INTO account
VALUES( 4, '熊大', 10000 );
INSERT INTO account
VALUES( 5, '熊二', 10000 );
數據集準備完成。
2.實現事務的兩種方式
(1)使用start transaction命令
熊大給小鳳轉賬1000塊
此時還未提交或者回滾,表明事務還沒結束。如果此時發生異常,兩個賬戶中的money會回到事務開啟前的狀態。
進行回滾
看到數據庫是回到的事物開啟前的狀態。
此時進行了提交,看到數據庫中數據已被改變
(2)設置MySQL事務默認不提交
MySQL數據庫的事務是默認提交的
單獨執行
update account set money=money-1000 where username ="熊大";
MySQL將這視為一個事務,并默認提交
使用命令
set autocommit = off;
或
set autocommit = 0;
取消MySQL的默認提交事務
如果不手動提交事務或回滾事務,數據庫中數據不做修改。
二、JDBC實現事務
JDBC實現事務只需要關閉MySQL的自動提交,并實現手動提交,即
// 關閉自動提交
conn.setAutoCommit(false);
// 手動提交事務
conn.commit();
當前數據庫信息:
模擬轉賬Java操作數據庫:
package com.goose;import com.goose.utils.JDBCUtils;import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;/*** @Author: Goose* @Description: TODO* @Date: 2025/5/11 11:19* @Version: 1.0*/public class JDBCShiWu {public static void main(String[] args) {Connection conn = JDBCUtils.getConnection();Statement stmt = null;try {// 關閉自動提交conn.setAutoCommit(false);stmt = conn.createStatement();String sql1 = "UPDATE account SET money = money - 1000 WHERE username = '熊大'";String sql2 = "UPDATE account SET money = money + 1000 WHERE username = '小鳳'";int i = stmt.executeUpdate(sql1);// int a = 10/0;int i2 = stmt.executeUpdate(sql2);System.out.println("i = "+i+", i2 = "+ i2);// 手動提交事務// conn.commit();} catch (SQLException e) {e.printStackTrace();}finally{JDBCUtils.close(conn,stmt);}}
}
執行結果:
關閉自動提交后,不進行手動提交數據庫信息不發生變化。
開啟手動提交,數據庫才能完成轉賬業務實現。
開啟事務后,如果在執行SQL時發生異常,也不會提交事務