Spring声明式事务

个人学习笔记摘要:

特别申明:此篇文章基本上相当于对Spring官方文档中重要信息的翻译,主要供自己以后查看方便。

一、Spring声明式事务的原理

声明式事务是由AOP代理模式实现的。transactional advice是通过元数据驱动的,包括XML类型的元数据或者基于注解的原数据。

The most important concepts to grasp with regard to the Spring Framework’s declarative transaction support are that this support is enabled via AOP proxies and that the transactional advice is driven by metadata (currently XML- or annotation-based). The combination of

AOP代理模式组合事务元数据产生一个AOP代理,该代理将事务拦截器 TransactionInterceptor 结合适当的事务管理器接口 TransactionManager 的实现类来驱动调用的方法的周围的事务。

AOP with transactional metadata yields an AOP proxy that uses a TransactionInterceptor in conjunction with an appropriate TransactionManager implementation to drive transactions around method invocations.

TransactionInterceptor 为命令式编程模型和响应式编程模型都提供事务管理,通过检查方法的返回值类型,检测需要的事务管理风格。

Spring Framework’s TransactionInterceptor provides transaction management for imperative and reactive programming models. The interceptor detects the desired flavor of transaction management by inspecting the method return type.

方法返回响应式类型,比如 Publisher 或者 Kotlin Flow(或者它们的子类),那么符合响应式事务管理风格的条件。所有其他返回类型(包括void)都将代码路径用于命令式事务管理风格。

特别注意:这里说的命令式事务管理风格,针对的是编程模型,而不是事务本身是命令式的,我们都知道spring家用的是声明式事务。因为编程模型不同,使用的事务管理接口也不同。一句话概括意思:命令式编程模型的事务管理风格是命令式的,但事务本身是声明式的。

Methods returning a reactive type such as Publisher or Kotlin Flow (or a subtype of those) qualify for reactive transaction management. All other return types including void use the code path for imperative transaction management.

事务管理风格影响需要哪个事务管理器。命令式事务管理风格需要使用 PlatformTransactionManager 实现,而响应式事务管理风格则使用ReactiveTransactionManager 实现。

Transaction management flavors impact which transaction manager is required. Imperative transactions require a PlatformTransactionManager, while reactive transactions use ReactiveTransactionManager implementations.

二、基于XML元数据实现声明式事务管理

在本示例中,您只需关注接口及其实现类,无需关心其中涉及的domain类,这样您才可以把注意力都集中到事务管理的用法上。

1、接口和实现类

接口:FooService

package x.y.service;

public interface FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);

}

实现类:DefaultFooService

package x.y.service;

public class DefaultFooService implements FooService {

    @Override
    public Foo getFoo(String fooName) {
        // ...
    }

    @Override
    public Foo getFoo(String fooName, String barName) {
        // ...
    }

    @Override
    public void insertFoo(Foo foo) {
        // ...
    }

    @Override
    public void updateFoo(Foo foo) {
        // ...
    }
}

2、使用XML元数据驱动transactional advice

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
        
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- 给事务管理器注入数据源依赖 -->
        <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 配置数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
        <property name="username" value="scott"/>
        <property name="password" value="tiger"/>
</bean>

<!-- 配置事务管理Advice,这个就是Spring使用AspectJ定义的Advice,我们也可以使用AspectJ自定义自己的Advice -->
<tx:advice id="txAdvice" transaction-manager="txManager">
        <!-- 事务管理参数配置,相当于在@Transanctional注解中定义参数 -->
        <tx:attributes>
        	<!-- 给适配的方法设置特定的事务参数,注意和下面的pointcut的作用进行区分 -->
            <!-- all methods starting with 'get' are read-only -->
            <tx:method name="get*" read-only="true"/>
            <!-- other methods use the default transaction settings (see below) -->
            <tx:method name="*"/>
        </tx:attributes>
</tx:advice>

<!-- 确保txAdvice,应用到程序中合适的连接点上 -->
<aop:config>
		<!-- PointCut和JoinPoint的适配 -->
        <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
        <!-- Advice和PointCut的关联 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>

</beans>

其实,命令式编程模型的事务管理定义和响应式编程模型的事务管理定义都是一样的。二者最主要的区别在于后者会有延迟性。因为TransactionInterceptor会用事务运算符装饰返回的响应式类型后,才开始和清理事务。

三、声明式事务的回滚

1、如何控制声明式事务的回滚?

官方推荐:从事务作用域中正在执行的代码中抛出异常,来指示事务管理结构代码事务是否需要回滚。因为没有被处理的异常,会被抛出,然后被事务管理结构代码捕捉到,从而进行事务回滚。默认情况下,只有抛出的异常是RuntimeException类型时,事务才会被标记为回滚事务。

2、自定义哪些异常类抛出时,事务回滚

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
    	<!-- NoProductInStockException,会导致事务回滚 -->
    	<tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
    	<tx:method name="*"/>
    </tx:attributes>
</tx:advice>

2、自定义哪些异常类抛出时,事务不回滚

<tx:advice id="txAdvice">
    <tx:attributes>
    <!-- InstrumentNotFoundException,不会导致事务回滚 -->
    <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
    <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

3、回滚强匹配规则

<tx:advice id="txAdvice">
    <tx:attributes>
    <!-- 除InstrumentNotFoundException以外的任何异常,都会导致事务回滚 -->
    <tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
    </tx:attributes>
</tx:advice>

4、编程式控制事务回滚

不推荐使用此种方式,侵入性和耦合性太大。

public void resolvePosition() {
    try {
        // some business logic...
    } catch (NoProductInStockException ex) {
        // trigger rollback programmatically
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

四、如何使用完全不同的事务设置来配置不同的Bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <aop:config>

        <aop:pointcut id="defaultServiceOperation"
                expression="execution(* x.y.service.*Service.*(..))"/>

        <aop:pointcut id="noTxServiceOperation"
                expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..))"/>

        <aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>

        <aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/>

    </aop:config>

    <!-- this bean will be transactional (see the 'defaultServiceOperation' pointcut) -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/>

    <!-- this bean will also be transactional, but with totally different transactional settings -->
    <bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/>

    <tx:advice id="defaultTxAdvice">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <tx:advice id="noTxAdvice">
        <tx:attributes>
            <tx:method name="*" propagation="NEVER"/>
        </tx:attributes>
    </tx:advice>

    <!-- other transaction infrastructure beans such as a TransactionManager omitted... -->

</beans>

五、事务管理可以设置的所有参数

参数名是否要求默认值参数描述
nameY方法名
propagationNREQUIRED事务传播行为
isolationNDEFAULT事务隔离级别,仅适用于传播行为REQUIRED或REQUIRES_NEW的时候
timeoutN-1设置超时,单位为秒,仅适用于传播行为REQUIRED或REQUIRES_NEW的时候
read-onlyNfalse仅适用于传播行为REQUIRED或REQUIRES_NEW的时候
rollback-forN设置哪些异常被抛出会导致事务回滚
no-rollback-forN设置哪些异常被抛出不会导致事务回滚

六、基于注解元数据实现声明式事务管理

1、@Transactional
该注解可以作用在类上或者方法上,作用在类上,表明该类及其子类中的方法都具有事务性。作用在方法上,表示该方法具有事务性。

2、如果使用了上面的注解就必须开启事务功能

  • 2.1、在配置类上使用@EnableTransactionManagement

  • 2.2、通过XML配置

<!-- transaction-manager="txManager"指定事务管理器 -->
<tx:annotation-driven transaction-manager="txManager"/>

3、@Transactional 参数

参数名参数类型参数描述
valueString指定事务管理器名
propagationenum: Propagation事务传播行为
isolationenum: Isolation事务隔离级别,仅适用于传播行为REQUIRED或REQUIRES_NEW的时候
timeoutint设置超时,单位为秒,仅适用于传播行为REQUIRED或REQUIRES_NEW的时候
read-onlyboolean仅适用于传播行为REQUIRED或REQUIRES_NEW的时候
rollback-forArray of Class objects, which must be derived from Throwable设置哪些异常被抛出会导致事务回滚
no-rollback-forArray of Class objects, which must be derived from Throwable.设置哪些异常被抛出不会导致事务回滚
rollbackForClassNameArray of Class objects, which must be derived from Throwable.Optional array of names of exception classes that must cause rollback.
noRollbackForClassNameArray of Class objects, which must be derived from Throwable.Optional array of names of exception classes that must not cause rollback.
labelArray of String labels to add an expressive description to the transaction.Labels may be evaluated by transaction managers to associate implementation-specific behavior with the actual transaction.

七、配置多个事务管理器

基于XML元数据

<tx:annotation-driven/>

    <bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        ...
        <qualifier value="order"/>
    </bean>

    <bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        ...
        <qualifier value="account"/>
    </bean>

    <bean id="transactionManager3" class="org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager">
        ...
        <qualifier value="reactive-account"/>
    </bean>

八、自定义组合注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(transactionManager = "order", label = "causal-consistency")
public @interface OrderTx {
}

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(transactionManager = "account", label = "retryable")
public @interface AccountTx {
}

九、理解事务传播
PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_NESTED

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值