一、什么是数据库

数据库是用于存储和管理数据的软件系统。
例如在一个医疗系统中,可能需要保存:

  • 患者基本信息
  • 筛查记录
  • 检查结果
  • 用户账号
  • 操作日志

常见的关系型数据库包括:

  • MySQL
  • PostgreSQL
  • Oracle
  • SQL Server

在 Java 后端开发中,MySQL 是比较常见的数据库之一。


二、什么是事务

事务可以理解为:将多个数据库操作作为一个整体执行,要么全部成功,要么全部失败。

例如银行转账
张三向李四转账 100 元,需要执行两条 SQL:

sql
UPDATE account
SET balance = balance - 100
WHERE id = 1;

UPDATE account
SET balance = balance + 100
WHERE id = 2;

分别表示:

正常情况下,两条 SQL 都应该执行成功。
如果第一条 SQL 执行成功,而第二条 SQL 执行失败,就会出现:

这就导致了数据错误。
因此,需要使用事务把这两个操作作为一个整体执行。
事务成功时:

如果中间发生错误:

这就是事务最核心的作用:
防止一个业务只执行了一部分。


三、事务的基本操作

数据库事务中最常见的三个操作是:

3.1 开启事务

sql
START TRANSACTION;

表示开始一个事务。

3.2 提交事务

sql
COMMIT;

表示事务中的所有操作执行成功,将修改正式保存到数据库。

3.3 回滚事务

sql
ROLLBACK;

表示事务执行失败,撤销当前事务中已经执行的修改。
可以简单记:

例如:

sql
START TRANSACTION;

UPDATE account
SET balance = balance - 100
WHERE id = 1;

UPDATE account
SET balance = balance + 100
WHERE id = 2;

COMMIT;

如果中间发生错误,则执行:

sql
ROLLBACK;

撤销当前事务中的修改。


四、事务的 ACID 特性

数据库事务通常具有四个重要特性,简称 ACID
分别是:

  • Atomicity:原子性
  • Consistency:一致性
  • Isolation:隔离性
  • Durability:持久性

1. 原子性 Atomicity

原子性表示:

一个事务中的操作要么全部成功,要么全部失败。

例如转账操作中:

这两个操作必须作为一个整体。
不能出现:


2. 一致性 Consistency

一致性表示:

事务执行前后,数据库中的数据都应该符合业务规则。

例如转账前:

张三向李四转账 100 元后:

虽然两个人的余额发生了变化,但是总金额仍然保持正确。


3. 隔离性 Isolation

隔离性表示:

多个事务同时执行时,事务之间应该尽量避免相互产生错误影响。

例如:

如果两个事务同时操作同一条数据,就可能产生并发问题。
事务隔离进一步会涉及:

  • 脏读
  • 不可重复读
  • 幻读
  • 事务隔离级别
    这些都是后续学习事务时的重要内容。

4. 持久性 Durability

持久性表示:

一个事务一旦提交成功,修改的数据就应该被永久保存。

也就是说,当执行:

sql
COMMIT;

之后,即使数据库随后:

  • 重启
  • 服务器异常
  • 程序崩溃
    已经成功提交的数据也应该被保存下来。

五、为什么需要事务

在实际软件开发中,一个业务操作通常不只是执行一条 SQL。
例如在医疗系统中,新建一条筛查记录可能需要:

假设执行到第三步时发生异常:

如果没有事务,数据库中就会出现一部分数据已经保存、另一部分数据没有保存的情况。
这会导致业务数据不完整。
使用事务后:

如果任意一步失败:

因此,事务非常适合处理:

一个业务需要同时修改多条数据,并且这些修改必须保持一致的场景。


六、Java 中的事务

在 Java 的 Spring Boot 项目中,一般不会直接在业务代码中手动编写:

sql
START TRANSACTION;
COMMIT;
ROLLBACK;

Spring 提供了 @Transactional 注解来帮助我们管理事务。
例如:

java
@Service
public class TransferService {

    @Transactional
    public void transfer() {

        accountMapper.subtract(1, 100);

        accountMapper.add(2, 100);
    }
}

这里:

java
@Transactional

表示 transfer() 方法中的数据库操作需要作为一个事务执行。
Spring 会帮你管理:

所以在 Java 项目中看到:

java
@Transactional

可以先简单理解为:
这个方法中的数据库操作需要作为一个整体执行。


七、@Transactional 需要注意什么

虽然 @Transactional 使用起来比较简单,但它并不是加上注解就一定能正确回滚。
例如:

java
@Transactional
public void transfer() {
    accountMapper.subtract(1, 100);

    int number = 10 / 0;

    accountMapper.add(2, 100);
}

执行到:

java
int number = 10 / 0;

时会发生异常。
此时 Spring 会回滚事务,前面已经执行的扣款操作也会被撤销。
最终数据不会停留在:

这种错误状态。
不过 Spring 事务还有很多需要进一步学习的内容,例如:

  • 哪些异常会触发回滚
  • rollbackFor
  • 方法内部调用导致事务失效
  • private 方法上的事务问题
  • 事务传播行为
  • 事务隔离级别

这些可以在后续学习 @Transactional 时再深入了解。


八、事务和普通 SQL 的区别

没有事务时,多条 SQL 基本可以理解为:

前面的修改可能已经保存,无法自动撤销。
使用事务后:

事务把原本独立的多条 SQL 组合成了一个完整的业务单元。