@Transactional事务几点注意及其属性Propagation的使用

@Transactional事务几点注意
这里面有几点需要大家留意:
A. 一个功能是否要事务,必须纳入设计、编码考虑。不能仅仅完成了基本功能就ok。
B. 如果加了事务,必须做好开发环境测试(测试环境也尽量触发异常、测试回滚),确保事务生效。
C. 以下列了事务使用过程的注意事项,请大家留意。
1. 不要在接口上声明@Transactional ,而要在具体类的方法上使用 @Transactional 注解,否则注解可能无效。
2.不要图省事,将@Transactional放置在类级的声明中,放在类声明,会使得所有方法都有事务。故@Transactional应该放在方法级别,不需要使用事务的方法,就不要放置事务,比如查询方法。否则对性能是有影响的。
3.使用了@Transactional的方法,对同一个类里面的方法调用, @Transactional无效。比如有一个类Test,它的一个方法A,A再调用Test本类的方法B(不管B是否public还是private),但A没有声明注解事务,而B有。则外部调用A之后,B的事务是不会起作用的。(经常在这里出错)
4.使用了@Transactional的方法,只能是public,@Transactional注解的方法都是被外部其他类调用才有效,故只能是public。道理和上面的有关联。故在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错,但事务无效。
5.经过在ICORE-CLAIM中测试,效果如下:
A.抛出受查异常XXXException,事务会回滚。
B.抛出运行时异常NullPointerException,事务会回滚。
C.Quartz中,execute直接调用加了@Transactional方法,可以回滚;间接调用,不会回滚。(即上文3点提到的)
D.异步任务中,execute直接调用加了@Transactional方法,可以回滚;间接调用,不会回滚。(即上文3点提到的)
E.在action中加上@Transactional,不会回滚。切记不要在action中加上事务。
F.在service中加上@Transactional,如果是action直接调该方法,会回滚,如果是间接调,不会回滚。(即上文3提到的)
G.在service中的private加上@Transactional,事务不会回滚。




其属性 Propagation 的使用:

Spring Transaction中有一个很重要的属性:Propagation。主要用来配置当前需要执行的方法,与当前是否有transaction之间的关系。
我晓得有点儿抽象,这也是为什么我想要写这篇博客的原因。看了后面的例子,大家应该就明白了。

一、Propagation取值:
REQUIRED (默认值):在有transaction状态下执行;如当前没有transaction,则创建新的transaction;
SUPPORTS :如当前有transaction,则在transaction状态下执行;如果当前没有transaction,在无transaction状态下执行;
MANDATORY :必须在有transaction状态下执行,如果当前没有transaction,则抛出异常IllegalTransactionStateException;
REQUIRES_NEW :创建新的transaction并执行;如果当前已有transaction,则将当前transaction挂起;
NOT_SUPPORTED :在无transaction状态下执行;如果当前已有transaction,则将当前transaction挂起;
NEVER :在无transaction状态下执行;如果当前已有transaction,则抛出异常IllegalTransactionStateException。

二、REQUIRED与REQUIRED_NEW
上面描述的6种propagation属性配置中,最难以理解,并且容易在transaction设计时出现问题的是REQUIRED和REQURED_NEW这两者的区别。当程序在某些情况下抛出异常时,如果对于这两者不够了解,就可能很难发现而且解决问题。

下面我们给出三个场景进行分析:
场景一:
ServiceA.java:
public class ServiceA {
    @Transactional
    public void callB() {
        serviceB.doSomething();
    }
}
ServiceB.java
public class ServiceB {
    @Transactional
    public void doSomething() {
        throw new RuntimeException("B throw exception");
    }
}
这种情况下,我们只需要在调用ServiceA.callB时捕获ServiceB中抛出的运行时异常,则transaction就会正常的rollback。

场景二
在保持场景一中ServiceB不变,在ServiceA中调用ServiceB的doSomething时去捕获这个异常,如下:
public class ServiceA {
    @Transactional
    public void callB() {
        try {
            serviceB.doSomething();
        } catch (RuntimeException e) {
            System.err.println(e.getMessage());
        }
    }
}
这个时候,我们再调用ServiceA的callB。程序会抛出org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only这样一个异常信息。原因是什么呢?
因为在ServiceA和ServiceB中的@Transactional propagation都采用的默认值:REQUREID。根据我们前面讲过的REQUIRED特性,当ServiceA调用ServiceB的时候,他们是处于同一个transaction中。如下图所示:

当ServiceB中抛出了一个异常以后,ServiceB会把当前的transaction标记为需要rollback。但是ServiceA中捕获了这个异常,并进行了处理,认为当前transaction应该正常commit。此时就出现了前后不一致,也就是因为这样,抛出了前面的UnexpectedRollbackException。

场景三
在保持场景二中ServiceA不变,修改ServiceB中方法的propagation配置为REQUIRES_NEW,如下:
public class ServiceB {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void doSomething() {
        throw new RuntimeException("B throw exception");
    }
}
此时,程序可以正常的退出了,也没有抛出UnexpectedRollbackException。原因是因为当ServiceA调用ServiceB时,serviceB的doSomething是在一个新的transaction中执行的。如下图所示:

所以,当doSomething抛出异常以后,仅仅是把新创建的transaction rollback了,而不会影响到ServiceA的transaction。ServiceA就可以正常的进行commit。
当然这里把ServiceA和ServiceB放在两个独立的transaction是否成立,还需要再多多考虑你的业务需求。

Transaction不是一个新东西了,那对于transaction的使用会不会有一些模式?一些经验之谈呢?答案肯定是有的,以后博客再说。

<think>好的,我现在需要回答用户关于@Transactional声明式事务传播行为类型的问题。首先,我得回忆一下Spring事务传播行为的相关知识。传播行为是Spring事务管理中的一个重要概念,它定义了事务方法在调用其他事务方法时,事务应该如何传播。用户提到了“春秋表”,可能是笔误,应该是“传播行为类型”的列表。 根据记忆,Spring的传播行为类型有七种,分别是REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。每个类型都有不同的行为。例如,REQUIRED是默认的,如果当前没有事务,就新建一个,如果有就加入。REQUIRES_NEW则是无论当前是否存在事务,都新建一个,同时挂起当前事务。这些需要详细解释。 接下来,我需要确认这些类型是否正确,以及它们的实际应用场景。比如,MANDATORY要求必须在已有事务中运行,否则抛出异常,适用于必须要有事务支持的场景。而NOT_SUPPORTED则是在无事务状态下运行,如果当前有事务则挂起。这些区别对于正确使用事务非常重要。 然后,用户提供的引用内容中提到了Spring事务的实现原理和配置切入点表达式,这可能和传播行为的配置有关。在声明式事务中,通过@Transactional注解的属性来指定传播行为类型,比如@Transactional(propagation = Propagation.REQUIRED)。需要结合AOP的配置,如引用[2]中的切入点表达式,来定义哪些方法应用这些事务属性。 此外,要确保回答结构清晰,分点说明每种传播行为类型,并给出示例代码。同时,生成的相关问题需要覆盖用户可能感兴趣的方面,比如配置方式、嵌套事务、选择依据等。最后,引用文献需要正确对应到用户提供的引用内容,确保引用标识正确。</think>### @Transactional声明式事务的传播行为类型 Spring的声明式事务通过`@Transactional`注解实现事务管理,其中`propagation`属性用于定义事务的传播行为。传播行为决定了事务方法在调用其他事务方法时,事务的边界如何控制。以下是7种传播行为类型及其说明: #### 1. **REQUIRED(默认)** - **行为**:如果当前存在事务,则加入该事务;否则新建一个事务。 - **应用场景**:适用于多数业务逻辑需要事务支持的场景。 - **示例**: ```java @Transactional(propagation = Propagation.REQUIRED) public void methodA() { ... } ``` #### 2. **SUPPORTS** - **行为**:如果当前存在事务,则加入;否则以非事务方式执行。 - **应用场景**:适用于查询操作,允许灵活适应调用环境。 - **示例**: ```java @Transactional(propagation = Propagation.SUPPORTS) public void queryData() { ... } ``` #### 3. **MANDATORY** - **行为**:必须在已有事务中运行,否则抛出异常。 - **应用场景**:强制要求方法必须在事务上下文中调用。 - **示例**: ```java @Transactional(propagation = Propagation.MANDATORY) public void updateData() { ... } ``` #### 4. **REQUIRES_NEW** - **行为**:无论当前是否存在事务,都新建一个事务,并挂起当前事务(如果存在)。 - **应用场景**:需要独立事务的操作(如日志记录),避免外层事务回滚影响。 - **示例**: ```java @Transactional(propagation = Propagation.REQUIRES_NEW) public void logOperation() { ... } ``` #### 5. **NOT_SUPPORTED** - **行为**:以非事务方式执行,若当前存在事务则挂起。 - **应用场景**:执行不需要事务支持的操作(如文件操作)。 - **示例**: ```java @Transactional(propagation = Propagation.NOT_SUPPORTED) public void processFile() { ... } ``` #### 6. **NEVER** - **行为**:必须在非事务环境中执行,否则抛出异常。 - **应用场景**:确保方法不会在事务中被调用。 - **示例**: ```java @Transactional(propagation = Propagation.NEVER) public void validateInput() { ... } ``` #### 7. **NESTED** - **行为**:在当前事务中创建一个嵌套事务。外层事务回滚时,嵌套事务也会回滚;但嵌套事务可独立提交或回滚。 - **支持条件**:需底层数据库支持保存点(如MySQL的InnoDB)。 - **应用场景**:复杂业务中部分操作需要独立回滚。 - **示例**: ```java @Transactional(propagation = Propagation.NESTED) public void subTask() { ... } ``` --- ### 配置传播行为的方式 在`@Transactional`注解中直接指定`propagation`属性即可: ```java @Transactional(propagation = Propagation.REQUIRED) public void transactionalMethod() { ... } ``` 通过AOP配置(如XML),可结合切入点表达式批量定义事务属性,如引用[2]中的配置: ```xml <aop:config> <aop:pointcut id="pt" expression="execution(* cn.bdqn.service.impl.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/> </aop:config> ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值