数据库事务以及JDBC实现事务
一、数据库事务
数据库事务(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时发生异常,也不会提交事务