什么是事务
事务是数据库管理系统(DBMS)执行过程中的一个逻辑单位(不可再分割),由一个有限的数据库操作序列构成(多个DML语句,select 语句不包含事务),要么全部成功,要么全部失败。
一组有限个操作的集合,要么全部成功,要么全部失败。
事务特性
事务应该具有四个属性:原子性、一致性、隔离性、持久性(ACID)
原子性(atomicity)一致性(consistency)隔离性(isolation)持久性(durability)
我们用一个银行转账的例子来说明。
特性解释:
- 原子性(Atomicity):事务里的操作要么全部成功,要么全部失败回滚。就像一个不可分割的整体,不能只执行其中一部分操作。
例如,在银行转账操作中,从一个账号扣款和向另一个账号这两个操作必须一起完成,若其中一个操作失败,整个转账事务就会回滚。
- 一致性(consistency):事务执行前后,数据库从一个一致性状态转变到另一个一致性状态。一致性是由原子性、隔离性和持久性共同保证的。
例如,在转账过程中,无论是否成功,所有账户的总金额应该保持不变。
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不会被其他事务干扰,每个事务都感觉不到其他事务在并发执行。
例如,在多个用户同时进行转账操作时,每个用户的转账事务应该相互隔离,不会互相影响。
- 持久性(Durability):事务一旦提交,它对数据库的改变就是永久性的,即便系统崩溃也不会丢失。
例如,当银行转账事务提交后,即使数据库服务突然断电,转账的结果也不会丢失。
package com.pgs.test;
import java.sql.*;
public class MySQLTransactionExample {
private static final String DB_URL = "jdbc:mysql://localhost:3306/testdemo";
private static final String DB_USER = "root";
private static final String DB_PASSWORD = "1234";
public static void main(String[] args) {
Connection conn = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
//关闭自动提交事务
conn.setAutoCommit(false);
//创建示例表
createAccountsTable(conn);
//插入初始数据
insertInitialData(conn);
//模拟转账操作
transferMoney(conn, 1, 2, 500);
//提交事务
conn.commit();
System.out.println("转账成功!");
//查询账户余额
printAccountBalance(conn);
} catch (Exception e) {
e.printStackTrace();
}finally {
// 关闭数据库连接
if (conn != null) {
try {
conn.setAutoCommit(true);
conn.close();
System.out.println("数据库连接已关闭。");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
// 打印账户余额
private static void printAccountBalance(Connection conn) throws SQLException {
String selectSQL = "SELECT id, balance FROM accounts";
try (PreparedStatement preparedStatement = conn.prepareStatement(selectSQL);
ResultSet resultSet = preparedStatement.executeQuery()) {
while (resultSet.next()) {
int id = resultSet.getInt("id");
double balance = resultSet.getDouble("balance");
System.out.printf("账户 ID: %d, 余额: %.2f%n", id, balance);
}
}
}
// 转账操作
private static void transferMoney(Connection connection, int senderId, int receiverId, double amount) throws SQLException {
// 从发送者账户扣款
String deductSQL = "UPDATE accounts SET balance = balance - ? WHERE id = ?";
try (PreparedStatement deductStatement = connection.prepareStatement(deductSQL)) {
deductStatement.setDouble(1, amount);
deductStatement.setInt(2, senderId);
deductStatement.executeUpdate();
}
// 向接收者账户存款
String depositSQL = "UPDATE accounts SET balance = balance + ? WHERE id = ?";
try (PreparedStatement depositStatement = connection.prepareStatement(depositSQL)) {
depositStatement.setDouble(1, amount);
depositStatement.setInt(2, receiverId);
depositStatement.executeUpdate();
}
}
// 插入初始数据
private static void insertInitialData(Connection conn) throws SQLException {
String insertSQL = "INSERT INTO accounts (balance) VALUES (1000), (2000)";
try (PreparedStatement preparedStatement = conn.prepareStatement(insertSQL)) {
preparedStatement.executeUpdate();
}
}
// 创建示例表
private static void createAccountsTable(Connection conn) throws SQLException {
String createTableSQL = "CREATE TABLE IF NOT EXISTS accounts (" +
"id INT AUTO_INCREMENT PRIMARY KEY, " +
"balance DECIMAL(10, 2))";
try (PreparedStatement preparedStatement = conn.prepareStatement(createTableSQL)) {
preparedStatement.executeUpdate();
}
}
}