@Transactional注解分析

本文深入探讨了事务管理的基础知识以及如何使用@Transactional注解实现面向事务的编程。介绍了事务的ACID特性、注解的使用场景及参数配置,包括传播性、隔离性、只读性、超时设置等关键概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

事务具备ACID特性,即原子性、一致性、隔离性和持久性。

@Transactional注解是用来指定接口、类或方法必须拥有事务语义的元数据。
@Transactional注解就代表支持事务管理,如果这个注解在类上,那么表示该注解对于所有该类中的public方法都生效;如果注解出现在方法上,则代表该注解仅对该方法有效,会覆盖先前从类层次继承下来的注解。
一般情况下不要将这个注解加到接口和抽象类上,因为注解是不能被继承的。

@Transactional注解如果被你在protected、private、或者package-visible的方法上使用
@Transactional注解,它不会报错,但是这个被注解的方法将不会展示已配置的事务设置。
@Transactional注解不足以开启事务行为,它仅仅是一种元数据,能够被可以识别@Transactional注解
和配置适当的具有事务行为的beans所使用。
当在接口上使用@Transactional注解时,只有当你设置了基于接口的代理才能生效。因为
注解不能继承,意思就是如果你正在使用基于类的代理时,事务的设置将不能被基于类的代理所识别,
而且对象也将不会被事务代理所包装。因此Spring团队也建议并且在具体的类上用@Transactional注解。


1. propagation参数,Propagation类型(枚举),默认值为Propogation.REQUIRED,支持的值有REQUIRED、MANDATORY、NESTED、NEVER、NOT_SUPPORTED、REQUIRE_NEW、SUPPORTS。关于这个问题的详细说明将在以后的文章中展开。
2. isolation参数,Isolation类型(枚举),默认值为Isolation.DEFAULT,支持的值有DEFAULT、READ_COMMITTED、READ_UNCOMMITTED、REPEATABLE_READ、SERIALIZABLE。关于这个问题的详细说明将在以后的文章中展开。
3. timeout参数,int类型,事务的超时时间,默认值为-1,即不会超时。
4. readOnly参数,boolean类型,true表示事务为只读,默认值为false。
5. rollbackFor参数,Class<? extends Throwable>[]类型,默认为空数组。
6. rollbackForClassName参数,String[]类型,默认为空数组。
7. noRollbackFor参数,Class<? extends Throwable>[]类型,默认为空数组。
8. noRollbackForClassName参数,String[]类型,默认为空数组。


5、6、7、8与回滚有关,
一般不推荐使用rollbackForClassName和noRollbackForClassName两个参数,
因为使用字符串的缺点有:如果不是用类的完整路径,就可能导致回滚设置对位于不同包中的同名类都生效;且如果类名写错,也无法得到IDE的动态提示。

如果不配置任何与回滚有关的参数,不代表事务不会进行回滚,如果没有配置这四个选项,那么DefaultTransactionAttribute配置将会生效,具体的行为是,抛掷任何unchecked Exception都会触发回滚,当然包括所有的RuntimeException。

 属性 类型描述
传播性 枚举型:Propagation可选的传播性设置
 隔离性枚举型:Isolation可选的隔离性级别(默认值:ISOLATION_DEFAULT)
只读性布尔型读写型事务vs.只读型事务
超时int型(以秒为单位)事务超时
回滚异常类(rollbackFor)

一组Class类的实例,必须

是Throwable的子类

一组异常类,遇到时必须进行回滚。默认情况下checked exceptions不进行回滚,仅unckecked exceptions(即RuntimeException的子类)才进行事务回滚。
回滚异常类名(rollbackForClassname) 一组Class类的名字,必须

是Throwable的子类

一组异常类名,遇到时必须进行回滚
不回滚异常来(noRollbackFor) 一组Class类的实例,必须

是Throwable的子类

一组异常类,遇到时必须不回滚
不会滚异常类名(noRollbackForClassname)一组Class类的实例,必须

是Throwable的子类

一组异常类,遇到时必须不回滚


默认遇到throw new RuntimeException("...");会回滚
需要捕获的throw new Exception("...");不会回滚

RuntimeException运行期例外,默认回滚
Exception 非运行期例外,不回滚

例如:
// 指定回滚
@Transactional(rollbackFor=Exception.class)
    public void methodName() {
       throw new Exception("...");// 不会回滚
    }
//指定不回滚
@Transactional(noRollbackFor=Exception.class)
    public ItimDaoImpl getItemDaoImpl() {
        throw new RuntimeException("注释");// 会回滚
    }

 

 

// 如果有事务,那么加入事务,没有的话新建一个(不写的情况下)
@Transactional(propagation=Propagation.REQUIRED)
// 容器不为这个方法开启事务
@Transactional(propagation=Propagation.NOT_SUPPORTED)
// 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
// 必须在一个已有的事务中执行,否则抛出异常,不能自动创建。
@Transactional(propagation=Propagation.MANDATORY)
// 必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.NEVER)
// 如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
@Transactional(propagation=Propagation.SUPPORTS)

 


// readOnly=true只读,不能更新,删除
@Transactional (propagation = Propagation.REQUIRED,readOnly=true)
// 设置超时时间
@Transactional (propagation = Propagation.REQUIRED,timeout=30)
// 设置数据库隔离级别
@Transactional (propagation = Propagation.REQUIRED,isolation=Isolation.DEFAULT




### Spring Boot 多数据源环境下 `@Transactional` 注解失效原因分析 在多数据源环境中,`@Transactional` 注解可能失效的主要原因是事务管理器未能正确绑定到目标数据源。Spring 使用 AOP(面向切面编程)机制来处理 `@Transactional` 注解的方法,在运行时为带有该注解的 Bean 创建代理对象[^2]。然而,在多数据源场景下,默认的事务管理器通常只针对单个数据源配置。 当多个数据源存在时,如果未显式指定哪个事务管理器应与特定的数据源关联,则可能导致事务无法正常启动或回滚。此外,动态切换数据源的注解(如 `@DS`),如果没有正确应用到服务层实现类或其实现的具体方法上,也可能导致事务失效[^3]。 另外一种常见情况是异常被捕获而未重新抛出,这会阻止 `@Transactional` 注解检测到异常的发生,从而使得事务不执行回滚操作[^4]。 --- ### 解决方案 #### 1. 配置多个事务管理器 为了支持多数据源环境下的事务管理,需分别为每个数据源定义独立的事务管理器,并通过自定义注解将其绑定至对应的服务逻辑中: ```java @Configuration public class DataSourceConfig { @Bean(name = "dataSourceOneTransactionManager") public PlatformTransactionManager dataSourceOneTransactionManager(@Qualifier("dataSourceOne") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "dataSourceTwoTransactionManager") public PlatformTransactionManager dataSourceTwoTransactionManager(@Qualifier("dataSourceTwo") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } } ``` 接着可以创建一个基于 `@Transactional` 的扩展注解用于区分不同的事务管理器: ```java @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Transactional(value = "dataSourceOneTransactionManager") public @interface TransactionalDataSourceOne {} @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Transactional(value = "dataSourceTwoTransactionManager") public @interface TransactionalDataSourceTwo {} ``` 这样可以在业务代码中按需选择合适的事务管理器: ```java @Service public class MyService { @TransactionalDataSourceOne public void methodForDataSourceOne() { // 数据库操作... } @TransactionalDataSourceTwo public void methodForDataSourceTwo() { // 另一数据库操作... } } ``` --- #### 2. 正确使用动态数据源切换注解 对于动态数据源切换的需求,推荐将 `@DS` 或类似的注解放置于 **服务实现类** 中而非接口处。这是因为 Spring AOP 默认采用 JDK 动态代理方式工作,只有实现了接口的对象才会被代理;而对于没有接口的情况则需要启用 CGLIB 支持。 可以通过如下设置开启 CGLIB 模式: ```properties spring.aop.proxy-target-class=true ``` 同时确保 `@DS` 被放置于实际执行业务逻辑的位置,例如: ```java @Service public class UserServiceImpl implements UserService { @Override @DS("slave") public User getUserById(Long id) { // 查询用户信息... } } ``` --- #### 3. 确保异常传播行为满足需求 为了避免因异常捕获而导致事务失效的现象发生,应当注意不要在受控方法内部吞掉任何已知错误。即使某些情况下确实希望局部处理部分问题,也建议手动重投这些异常以便触发必要的回滚动作。 例如下面的例子展示了如何让 checked exception 强制引发 rollback: ```java @AfterThrowing(pointcut="execution(* com.example.service.*(..))", throwing="ex") public void handleException(Exception ex){ throw new RuntimeException(ex); } ``` --- ### 总结 综上所述,解决 Spring Boot 多数据源环境下 `@Transactional` 注解失效的关键在于合理配置多个事务管理器并明确其作用范围,同时保证动态数据源切换策略的有效实施以及适当调整程序结构以适应框架的工作原理。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值