事务

88 阅读4分钟

事务(Transaction)是数据库管理系统(DBMS)中用于管理并确保操作可靠性的一种机制。事务的主要目的是保证数据的一致性,即一组数据库操作要么全部执行成功,要么全部回滚到操作前的状态,没有中间状态。

事务的四大特性(ACID)

事务具有四个主要特性,通常简称为 ACID:

  1. 原子性(Atomicity)

    • 事务中的所有操作是一个不可分割的整体,要么全部成功,要么全部失败。保证在发生故障时要么什么都不做,要么撤销到事务开始前的状态。
  2. 一致性(Consistency)

    • 事务在开始和结束时,数据库必须处于一致的状态,即事务的执行不能破坏数据库的完整性约束。
  3. 隔离性(Isolation)

    • 事务的执行是相互隔离的,一个事务的操作对其他事务是不可见的,直到该事务提交为止。隔离性可以通过不同的隔离级别来实现。
  4. 持久性(Durability)

    • 一旦事务提交,其对数据库的改变应该是永久性的,即使系统发生故障也能保持。

事务的使用场景

事务广泛应用于需要保证数据一致性的场景,如银行转账、预定系统、订单处理系统等。

Spring 中的事务管理

Spring 提供了强大的事务管理功能,支持声明式事务管理和编程式事务管理。最常用的是声明式事务管理,通过注解 @Transactional 实现。

声明式事务管理

1. 配置事务管理器

首先,配置一个事务管理器。常用的是 DataSourceTransactionManager 或者 JPA 的 JpaTransactionManager

配置示例

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import javax.persistence.EntityManagerFactory;

@Configuration
@EnableTransactionManagement
public class TransactionManagerConfig implements TransactionManagementConfigurer {

    private final EntityManagerFactory entityManagerFactory;

    public TransactionManagerConfig(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new JpaTransactionManager(entityManagerFactory);
    }

    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return transactionManager();
    }
}

2. 使用 @Transactional 注解

在需要事务管理的方法或类上使用 @Transactional 注解。

示例

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class BankService {

    @Autowired
    private BankAccountRepository bankAccountRepository;

    /**
     * 转账方法,保证两个账户的资金变动是一个事务
     */
    @Transactional
    public void transfer(Long fromAccountId, Long toAccountId, Double amount) {
        BankAccount fromAccount = bankAccountRepository.findById(fromAccountId).orElseThrow();
        BankAccount toAccount = bankAccountRepository.findById(toAccountId).orElseThrow();

        if (fromAccount.getBalance() < amount) {
            throw new InsufficientFundsException("余额不足");
        }

        fromAccount.setBalance(fromAccount.getBalance() - amount);
        toAccount.setBalance(toAccount.getBalance() + amount);

        bankAccountRepository.save(fromAccount);
        bankAccountRepository.save(toAccount);
    }
}

事务传播行为

Spring 中的 @Transactional 注解还支持设置事务的传播行为(Propagation),用于指定当前事务方法的执行行为。

常用的传播行为:

  • REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

  • SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式继续运行。

  • MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

  • REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则将当前事务挂起。

  • NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则将当前事务挂起。

  • NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

  • NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则按 REQUIRED 属性执行。

示例

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateAccount(Long accountId, Double amount) {
    // 更新账户余额
}

事务隔离级别

事务隔离级别用于解决并发时的读写冲突问题,常用的隔离级别有:

  • READ_UNCOMMITTED:允许读取未提交的数据,可能导致脏读、不可重复读和幻读。

  • READ_COMMITTED:只能读取已提交的数据,可以避免脏读,但可能导致不可重复读和幻读。

  • REPEATABLE_READ:在事务开始时锁定读取的数据,避免脏读和不可重复读,但可能导致幻读。

  • SERIALIZABLE:最严格的隔离级别,通过加锁避免脏读、不可重复读和幻读,但性能最差。

示例

@Transactional(isolation = Isolation.REPEATABLE_READ)
public void performTransaction() {
    // 执行业务逻辑
}

事务超时和只读

  • 超时(timeout) :设置事务的超时时间,超时后事务将回滚。

  • 只读(readOnly) :优化事务操作,告知数据库当前事务是只读的。

示例

@Transactional(timeout = 30, readOnly = true)
public List<Account> getAllAccounts() {
    // 查询所有账户信息
}

总结

  • 事务的四大特性(ACID) :原子性、一致性、隔离性和持久性。

  • Spring 中的事务管理:通过 @Transactional 注解实现声明式事务管理。

  • 传播行为:指定事务方法的执行行为,如 REQUIREDREQUIRES_NEW 等。

  • 隔离级别:解决并发时的读写冲突,如 READ_COMMITTEDSERIALIZABLE 等。

  • 事务超时和只读:优化事务操作,设置超时时间和只读属性。