Spring:深入理解 Spring 事务原理

Spring 事务

  Spring 框架支持两种主要的事务管理方式:编程式事务(Programmatic Transaction Management)和声明式事务(Declarative Transaction Management)。

1.编程式事务
  编程式事务管理指的是在代码中显式地控制事务的边界和生命周期。这通常意味着你需要手动调用事务管理的 API 来开始、提交或回滚事务。Spring 提供了 PlatformTransactionManager 接口作为编程式事务管理的基础。开发者需要手动编写代码来调用这个接口的方法,例如 getTransaction(), commit(), rollback() 等。
  不过,由于编程式事务管理需要开发者在代码中显式地管理事务,这增加了代码的复杂性,并且可能导致错误(比如忘记提交或回滚事务)。因此,编程式事务管理在 Spring 应用中不太常用。

编程式事务示例

@Service  
public class MyService {  
  
    private final PlatformTransactionManager transactionManager;  
  
    @Autowired  
    public MyService(PlatformTransactionManager transactionManager) {  
        this.transactionManager = transactionManager;  
    }  
  
    public void executeInTransaction() {  
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();  
        // 设置隔离级别、传播行为等(如果需要)  
        // def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
  
        TransactionStatus status = transactionManager.getTransaction(def);  
        try {  
            // 在这里执行数据库操作...  
            // 如果需要回滚,可以调用 transactionManager.rollback(status);  
            // 如果一切顺利,提交事务 transactionManager.commit(status);  
        } catch (Exception ex) {  
            transactionManager.rollback(status);  
            throw ex;  
        }  
    }  
}

在使用编程式事务处理的过程中,利用DefaultTransactionDefinition对象来持有事务处理属性。

同时,在创建事务的过程中得到一个TransactionStatus对象,然后通过直接调用transactionManager对象 的commit()rollback()方法来完成事务处理。

2.声明式事务
  声明式事务管理则是一种更为方便和推荐的方式。开发者通过注解(如 @Transactional)或 XML 配置来声明哪些方法需要事务支持,而不需要在代码中显式地管理事务。Spring 容器会自动为这些方法添加事务管理逻辑。
  在 Spring 中,声明式事务管理通常是通过 AOP(面向切面编程)来实现的。Spring 容器在运行时为需要事务支持的方法创建一个代理对象,当这些方法被调用时,代理对象会自动添加事务管理逻辑。
  使用 @Transactional 注解是最常见的声明式事务管理方式。你可以将 @Transactional 注解添加到类、接口或方法上,以指定哪些方法需要事务支持。此外,你还可以使用 @EnableTransactionManagement 注解来启用声明式事务管理,并通过配置事务管理器(如 DataSourceTransactionManager 或 JpaTransactionManager)来定义事务的属性(如传播行为、隔离级别等)。

@Configuration  
public class AppConfig {  
  
    @Autowired  
    private DataSource dataSource; // 假设已经定义了一个DataSource bean  
  
    @Bean  
    public PlatformTransactionManager transactionManager() {  
        return new DataSourceTransactionManager(dataSource);  
    }  
}

Spring事务传播行为

以下是Spring中七种事务传播行为的详细解释:

①.PROPAGATION_REQUIRED(默认)
1.如果当前没有事务,就新建一个事务。
2.如果当前存在事务,就加入这个事务。
这是最常见的选择。

@Service  
public class UserService {  
  
    @Autowired  
    private UserRepository userRepository;  
  
    @Transactional  
    public void createUser(User user) {  
        userRepository.save(user);  
        // 其他业务逻辑...  
    }  
}

②.PROPAGATION_SUPPORTS
1.如果当前没有事务,就以非事务方式执行。
2.如果当前存在事务,就加入这个事务。
  也就是说,方法是否在一个事务中运行是无关紧要的,如果方法运行在事务上下文中,那么它就使用这个事务;否则,方法就作为非事务性代码来执行

@Service  
public class UserService {  
  
    @Autowired  
    private UserRepository userRepository;  
  
    @Transactional(propagation = Propagation.SUPPORTS)  
    public void createUserIfNotInTransaction(User user) {  
        userRepository.save(user);  
        // 其他业务逻辑...  
    }  
}

③.PROPAGATION_MANDATORY
1.如果当前没有事务,就抛出异常。
2.如果当前存在事务,就加入这个事务。
该设置使得事务性上下文必须已经存在,否则,一个事务性异常将被抛出。

@Service  
public class UserService {  
  
    @Autowired  
    private UserRepository userRepository;  
  
    @Transactional(propagation = Propagation.MANDATORY)  
    public void createUserMandatory(User user) {  
        userRepository.save(user);  
        // 其他业务逻辑...  
    }  
}

④.PROPAGATION_REQUIRES_NEW
1.如果当前没有事务,就新建一个事务。
2.如果当前存在事务,把当前事务挂起,并且自己创建一个新的事务给自己使用。
  这个设置总是新建一个事务,并且如果当前已经存在一个事务,就把这个存在的事务挂起起来。也就是说,不管外部方法是否开启事务,Propagation.REQUIRES_NEW修饰的内部方法会起一个新的事务。

@Service  
public class UserService {  
  
    @Autowired  
    private UserRepository userRepository;  
  
    @Transactional(propagation = Propagation.REQUIRES_NEW)  
    public void createUserInNewTransaction(User user) {  
        userRepository.save(user);  
        // 其他业务逻辑...  
    }  
}

⑤.PROPAGATION_NOT_SUPPORTED
1.以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
总是以非事务性方式执行,并挂起任何存在的事务。

@Service  
public class UserService {  
  
    @Autowired  
    private UserRepository userRepository;  
  
    @Transactional(propagation = Propagation.NOT_SUPPORTED)  
    public void createUserWithoutTransaction(User user) {  
        userRepository.save(user);  
        // 其他业务逻辑...  
    }  
}

⑥.PROPAGATION_NEVER
1.以非事务方式执行,如果当前存在事务,则抛出异常。
  这是Propagation.NOT_SUPPORTED的反面设置,它总是以非事务性方式执行,如果存在一个活动事务,则抛出异常。

@Service  
public class UserService {  
  
    @Autowired  
    private UserRepository userRepository;  
  
    @Transactional(propagation = Propagation.NEVER)  
    public void createUserNever(User user) {  
        userRepository.save(user);  
        // 其他业务逻辑...  
    }  
}

⑦.PROPAGATION_NESTED
1.如果当前没有事务,就新建一个事务。
2.如果当前存在事务,则在嵌套事务内执行。
  如果一个活动的事务存在,则运行在一个嵌套的事务中。嵌套的事务可以独立于当前事务进行提交或回滚。如果嵌套事务失败,则它可以被回滚,而外部事务可以继续。

@Service  
public class UserService {  
  
    @Autowired  
    private UserRepository userRepository;  
  
    @Transactional(propagation = Propagation.NESTED)  
    public void createUserNested(User user) {  
        userRepository.save(user);  
        // 其他业务逻辑...  
    }  
}

Spring事务的隔离级别

先来了解一下数据库中的隔离级别
1.读未提交(Read Uncommitted):
值/等级:最低隔离级别。
特性:一个事务可以读取另一个事务未提交的数据。这可能导致脏读、不可重复读和幻读等问题。

2.读已提交(Read Committed)
值/等级:较读未提交更高的隔离级别。
特性:一个事务只能读取另一个事务已经提交的数据。这避免了脏读问题,但在并发情况下,可能会出现不可重复读和幻读。

3.可重复读(Repeatable Read)
值/等级:在某些数据库系统(如MySQL的InnoDB引擎)中是默认的隔离级别。
特性:一个事务在整个过程中可以多次读取同一数据,并且每次读取的结果都相同。这避免了不可重复读和脏读,但在并发情况下,可能会出现幻读。

4.序列化(Serializable)
值/等级:最高的隔离级别。
特性:事务串行执行,避免了脏读、不可重复读和幻读问题。但并发性能较差,通常只在特殊情况下使用。

脏读:一事务对数据进行了增删改,但未提交,另一事务可以读取到未提交的数据。如果第一个事务这时候回滚了,那么第二个事务就读到了脏数据。

不可重复读:一个事务中发生了两次读操作,第一次读操作和第二次操作之间,另外一个事务对数据进行了修改,这时候两次读取的数据是不一致的。

幻读:第一个事务对一定范围的数据进行批量修改,第二个事务在这个范围增加一条数据,这时候第一个事务就会丢失对新增数据的修改。

总结:
1.隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
2.大多数的数据库默认隔离级别为 Read Commited,比如 SqlServer、Oracle
3.少数数据库默认隔离级别为:Repeatable Read 比如:MySQL InnoDB

  在Spring框架中,事务的隔离级别定义了事务之间如何相互影响,特别是它们如何并发地访问数据库资源。Spring支持五个事务隔离级别,这些级别与JDBC规范中定义的隔离级别相对应。以下是这些隔离级别的详细说明:

①.ISOLATION_DEFAULT(默认隔离级别):
使用数据库默认的隔离级别。这取决于底层数据库系统。

②.ISOLATION_READ_UNCOMMITTED(读取未提交):
这是最低的隔离级别。它允许一个事务读取另一个事务尚未提交的数据。这可能导致几个问题,如脏读(dirty read)、不可重复读(non-repeatable read)和幻读(phantom read)。

③.ISOLATION_READ_COMMITTED(读取已提交):
这是大多数数据库系统的默认隔离级别(但不是所有)。它确保一个事务只能读取另一个事务已经提交的数据。这可以防止脏读,但在高并发情况下,可能会导致不可重复读和幻读。

④.ISOLATION_REPEATABLE_READ(可重复读):
这是MySQL的默认隔离级别(对于InnoDB存储引擎)。它确保在同一个事务中多次读取同一数据时,结果都是一致的。这解决了不可重复读的问题,但仍然可能出现幻读。

⑤.ISOLATION_SERIALIZABLE(可串行化):
这是最高的隔离级别。它通过强制事务串行执行来避免脏读、不可重复读和幻读。但这也意味着它会导致性能下降,因为事务必须等待其他事务完成才能开始。

在Spring中,可以使用@Transactional注解的isolation属性来指定事务的隔离级别。

@Service  
public class MyService {  
  
    @Transactional(isolation = Isolation.READ_COMMITTED)  
    public void myTransactionalMethod() {  
        // ... 方法体 ...  
    }  
}
在上面的例子中,myTransactionalMethod方法将在READ_COMMITTED隔离级别下运行。
  • 20
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值