写点什么

Spring 5 中文解析数据存储篇 -@Transactional 使用

用户头像
青年IT男
关注
发布于: 2020 年 09 月 21 日
Spring 5 中文解析数据存储篇-@Transactional使用

Spring核心篇章:



Spring 5 中文解析之核心篇-IoC容器



Spring 5 中文解析核心篇-IoC容器之依赖关系



Spring 5 中文解析核心篇-IoC容器之Bean作用域



Spring 5 中文解析核心篇-IoC容器之自定义Bean性质



Spring 5 中文解析核心篇-IoC容器之BeanDefinition继承与容器拓展点



Spring 5 中文解析核心篇-IoC容器之基于注解的容器配置



Spring 5 中文解析核心篇-IoC容器之类路径扫描和组件管理



Spring 5 中文解析核心篇-IoC容器之JSR330标准注解



Spring 5 中文解析核心篇-IoC容器之基于Java容器配置



Spring 5 中文解析核心篇-IoC容器之Environment抽象



Spring 5 中文解析核心篇-IoC容器之ApplicationContext与BeanFactory



Spring 5 中文解析核心篇-IoC容器之Resources



Spring 5 中文解析核心篇-IoC容器之数据校验、数据绑定和类型转换



Spring 5 中文解析核心篇-IoC容器之SpEL表达式



Spring 5 中文解析核心篇-IoC容器之AOP编程(上)



Spring 5 中文解析核心篇-IoC容器之AOP编程(下)



Spring 5 中文解析核心篇-IoC容器之Spring AOP API



Spring测试篇章:



Spring 5 中文解析测试篇-Spring测试



Spring 5 中文解析核心篇-集成测试之概要和集成测试注解



Spring 5 中文解析核心篇-集成测试之TestContext(上)



Spring 5 中文解析核心篇-集成测试之TestContext(中)



Spring 5 中文解析测试篇-集成测试之TestContext(下)



Spring 5 中文解析测试篇-Spring MVC测试框架



Spring 5 中文解析测试篇-WebTestClient



Spring存储篇章:



Spring 5 中文解析数据存储篇-Spring框架的事物支持模型的优势



Spring 5 中文解析数据存储篇-事务同步和声明式事物管理



Spring 5 中文解析数据存储篇-@Transactional使用



完整电子书地址



除了基于XML的声明式方法进行事务配置外,还可以使用基于注解的方法。直接在Java源代码中声明事务语义会使声明更加接近受影响的代码。不存在过多耦合的风险,因为原本打算以事务方式使用的代码几乎总是以这种方式部署。



还支持使用标准的javax.transaction.Transactional注解来替代Spring自己的注解。请参阅JTA 1.2文档以获取更多详细信息。



使用@Transactional注解提供的易用性将通过一个示例得到最好的说明,下面的示例对此进行了说明。考虑以下类定义:



// the service class that we want to make transactional@Transactionalpublic class DefaultFooService implements FooService {​    Foo getFoo(String fooName) {        // ...   }​    Foo getFoo(String fooName, String barName) {        // ...   }​    void insertFoo(Foo foo) {        // ...   }​    void updateFoo(Foo foo) {        // ...   }}



在上面的类级别使用,注解表示声明类(及其子类)的所有方法的默认值。另外,每种方法都可以单独注解。注意,类级别的注解不适用于类层次结构中的祖先类。在这种情况下,需要在本地重新声明方法,以参与子类级别的注解。



当将一个以上的POJO类在Spring上下文中定义为bean时,可以通过@Configuration类中的@EnableTransactionManagement注解使bean实例具有事务性。有关完整的详细信息,请参见javadoc



在XML配置中,<tx:annotation-driven/>标签提供了类似的便利操作:



<!-- from the file 'context.xml' --><?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">​    <!-- this is the service object that we want to make transactional -->    <bean id="fooService" class="x.y.service.DefaultFooService"/>​    <!-- enable the configuration of transactional behavior based on annotations -->    <tx:annotation-driven transaction-manager="txManager"/><!-- a TransactionManager is still required --> //1​    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <!-- (this dependency is defined somewhere else) -->        <property name="dataSource" ref="dataSource"/>    </bean>​    <!-- other <bean/> definitions here --></beans>



  1. 使bean实例具有事务性的行为



如果要连接的TransactionManager的bean名称具有名称transactionManager,则可以在<tx:annotation-driven/>标签中省略transaction-manager属性。如果要依赖注入的TransactionManager bean具有其他名称,则必须使用transaction-manager属性,如上例所示。



相对于命令式编程,响应式事务方法使用响应式返回类型,如下清单所示:



// the reactive service class that we want to make transactional@Transactionalpublic class DefaultFooService implements FooService {​    Publisher<Foo> getFoo(String fooName) {        // ...   }​    Mono<Foo> getFoo(String fooName, String barName) {        // ...   }​    Mono<Void> insertFoo(Foo foo) {        // ...   }​    Mono<Void> updateFoo(Foo foo) {        // ...   }}



请注意,对于返回的Publisher,在响应流取消信号方面有一些特殊注意事项。有关更多详细信息,请参见“使用TransactionOperator”下的“取消信号”部分。



**方法可见性和@Transactional**

使用代理时,应仅将@Transactional注解应用于具有公共可见性的方法。如果使用@Transactional注解对protectedprivate或程序包可见的方法进行注解,则不会引发任何错误,但是带注解的方法不会显示已配置的事务设置。如果需要注解非公共方法,请考虑使用AspectJ(稍后描述)。



你可以将@Transactional注解应用于接口定义、接口上的方法、类定义或类上的公共方法。但是,仅@Transactional注解的存在不足以激活事务行为。 @Transactional注解仅仅是元数据,可以被某些支持@Transactional的运行时基础结构使用,并且可以使用元数据来配置具有事务行为的适当Bean。在前面的示例中,<tx:annotation-driven/>元素打开事务行为。



Spring团队推荐仅使用@Transactional注解对具体类(以及具体类的方法)进行注解,而不是对接口进行注解。你当然可以在接口(或接口方法)上放置@Transactional注解,但这仅在你使用基于接口的代理时才可以达到预期。Java注解不能从接口继承的事实意味着,如果你使用基于类的代理(proxy-target-class="true")或基于编织的切面(mode =“aspectj”),则事务设置不会由代理和编织基础结构识别,并且该对象未包装在事务代理中。

在代理模式(默认)下,仅拦截通过代理传入的外部方法调用。这意味着即使调用的方法标记有@Transactional,自调用(实际上是目标对象中的方法调用目标对象的另一种方法)也不会在运行时使用实际事务(译者:调用实例自身的方法就是自调用)。另外,必须完全初始化代理才能提供预期的行为,因此你不应在初始化代码(即@PostConstruct)中依赖此功能。



如果期望自调用也与事务包装在一起,请考虑使用AspectJ模式(请参见下表的mode属性)。在这种情况下,首先没有代理。而是编织目标类(即,修改其字节码)以将@Transactional转换为任何方法上的运行时行为。



XML属性注解属性默认值描述transaction-managerN/A (查看 Transaction-ManagementConfigurer-javadoc)transactionManager要使用的事务管理器的名称。如上例所示,仅当事务管理器的名称不是transactionManager时才需要。modemodeproxy默认模式(代理)通过使用Spring的AOP框架来处理带注解的bean(遵循代理语义,如前所述,仅适用于通过代理传入的方法调用)。替代模式(aspectj)则将受影响的类与Spring的AspectJ事务切面进行编织,修改目标类字节码以应用于任何类型的方法调用。AspectJ编织需要在类路径中使用spring-aspects.jar并启用加载时编织(或编译时编织)。(有关如何设置加载时编织的详细信息,请参见Spring配置。)proxy-target-classproxyTargetClassfalse仅适用于代理模式。控制为使用@Transactional注解注释的类创建哪种类型的事务代理。如果proxy-target-class属性设置为true,则将创建基于类的代理。如果proxy-target-classfalse或省略了属性,则将创建基于标准JDK接口的代理。(有关不同代理类型的详细检查,请参见代理机制。)orderorderOrdered.LOWEST_PRECEDENCE定义应用于带@Transactional注解的bean的事务通知的顺序。(有关AOP通知排序相关规则的更多信息,请参见通知顺序。)没有指定的顺序意味着AOP子系统确定通知的顺序。



处理@Transactional注解的默认通知模式是代理,它仅允许通过代理拦截调用。同一类内的本地调用无法以这种方式被拦截(自调用)。对于更高级的拦截模式,请考虑结合编译时或加载时编织切换到Aspectj模式。

proxy-target-class属性控制为使用@Transactional注解注释的类创建哪种类型的事务代理。如果proxy-target-class设置为true,则将创建基于类的代理。如果proxy-target-classfalse或省略了属性,则将创建基于标准JDK接口的代理。(有关不同代理类型的讨论,请参见core.html。)

@EnableTransactionManagement<tx:annotation-driven/>仅在定义它们的相同应用程序上下文中的bean上查找@Transactional。这意味着,如果将注解驱动的配置放在DispatcherServletWebApplicationContext中,它将仅在控制器而不是服务中检查@Transactional bean。有关更多信息,请参见MVC



在评估方法的事务设置时,最派生的位置优先(译者:范围最小的优先级越高,例如:类和方法配置时方法优先级更高)。在下面的示例中,DefaultFooService类在类级别使用只读事务的设置进行注解,但是同一类中updateFoo(Foo)方法上的@Transactional注解优先于定义的事务设置在类级别上。



@Transactional(readOnly = true)public class DefaultFooService implements FooService {​    public Foo getFoo(String fooName) {        // ...   }​    // these settings have precedence for this method    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)    public void updateFoo(Foo foo) {        // ...   }}



@Transactional设置



@Transactional注解是元数据,它指定接口、类或方法必须具有事务语义(例如,在调用此方法时启动一个全新的只读事务、暂停任何现有事务)。默认的@Transactional设置如下:



  • 事物传播设置为PROPAGATION_REQUIRED

  • 事物隔离级别为ISOLATION_DEFAULT

  • 事务是读写的。

  • 事务超时默认为基础事务系统的默认超时,如果不支持超时,则默认为无。

  • 任何RuntimeException都会触发回滚,而任何检测的Exception都不会触发。



你可以更改这些默认设置。下表总结了@Transactional注解的各种属性:



属性类型描述valueString可选的限定符,指定要使用的事务管理器。propagationenumPropagation可选的传播设置。isolationenumIsolation可选的隔离级别。仅适用于REQUIREDREQUIRES_NEW的传播值。timeoutint (以秒为单位)可选的事务超时。仅适用于REQUIREDREQUIRES_NEW的传播值。readOnlyboolean读写与只读事务。仅适用于REQUIREDREQUIRES_NEW的值。rollbackForClass对象数组,必须从Throwable派生。必须引起回滚的异常类的可选数组。rollbackForClassName类名数组。这些类必须从Throwable派生。必须引起回滚的异常类名称的可选数组。noRollbackForClass对象数组,必须从Throwable派生。不能导致回滚的异常类的可选数组。noRollbackForClassName字符串类名称的数组,必须从Throwable派生。不能引起回滚的异常类名称的可选数组。



当前,你无法对事务名称进行显式控制,其中“名称”是指显示在事务监视器(如果适用)(例如,WebLogic的事务监视器)和日志输出中的事务名称。对于声明式事务,事务名称始终是全限定类名称+'.' +通知类的方法名称。例如,如果BusinessService类的handlePayment(..)方法启动了事务,则事务的名称将为:com.example.BusinessService.handlePayment



具有@Transactional的多个事务管理器



大多数Spring应用程序仅需要一个事务管理器,但是在某些情况下,你可能需要在一个应用程序中使用多个独立的事务管理器。你可以使用@Transactional注解的valuetransactionManager属性来选择指定要使用的TransactionManager。这可以是事务管理器bean的bean名称或限定符值。例如,使用限定符表示法,可以在应用程序上下文中将以下Java代码与以下事务管理器bean声明进行组合:



public class TransactionalService {​    @Transactional("order")    public void setSomething(String name) { ... }​    @Transactional("account")    public void doSomething() { ... }​    @Transactional("reactive-account")    public Mono<Void> doSomethingReactive() { ... }}



以下清单显示了bean声明:



<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></tx:annotation-driven>



在这种情况下,TransactionalService上的各个方法在单独的事务管理器下运行,并根据orderaccountreactive-account限定符进行区分。如果未找到特别限定的TransactionManager bean,则仍使用默认的<tx:annotation-driven>目标bean名称transactionManager



参考代码:org.liyong.dataaccess.starter.QualifierAnnotationTransactionManagerIocContainer



自定义组合的注解



如果你发现在许多不同的方法上将@Transactional重复使用相同的属性,则Spring的元注解支持可让你为特定用例定义自定义的注解。例如,考虑以下注解定义:



@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Transactional("order")public @interface OrderTx {}​@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Transactional("account")public @interface AccountTx {}



前面的注解使我们可以按照上一节的内容编写示例,如下所示:



public class TransactionalService {​    @OrderTx    public void setSomething(String name) {        // ...   }​    @AccountTx    public void doSomething() {        // ...   }}



在前面的示例中,我们使用了语法来定义事务管理器限定符,但是我们还可以包括传播行为,回滚规则、超时和其他功能。



参考代码:org.liyong.dataaccess.starter.CustomAnnotationTransactionManagerIocContainer



作者



个人从事金融行业,就职过易极付、思建科技、某网约车平台等重庆一流技术团队,目前就职于某银行负责统一支付系统建设。自身对金融行业有强烈的爱好。同时也实践大数据、数据存储、自动化集成和部署、分布式微服务、响应式编程、人工智能等领域。同时也热衷于技术分享创立公众号和博客站点对知识体系进行分享。关注公众号:青年IT男 获取最新技术文章推送!



博客地址: http://youngitman.tech



CSDN: https://blog.csdn.net/liyong1028826685



微信公众号:

技术交流群:



发布于: 2020 年 09 月 21 日阅读数: 45
用户头像

青年IT男

关注

站在巨人肩上看得更远! 2018.04.25 加入

从事金融行业,就职过易极付、思建科技、网约车平台等一流技术团队,目前就职于银行负责支付系统建设。对金融行业有强烈的爱好。实践大数据、数据存储、自动化集成和部署、分布式微服务、响应式编程、人工智能等领域

评论

发布
暂无评论
Spring 5 中文解析数据存储篇-@Transactional使用