| 提供统一的API接口支持不同的资源。 |
| 提供声明式的事务管理。 |
| 方便的与Spring框架集成。 |
| 多个资源的事务管理、同步。 |
| 接口 | 说明 | 方法定义 |
| PlatformTransactionManager | 平台事务管理器。 | TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException; void commit(TransactionStatus var1) throws TransactionException; void rollback(TransactionStatus var1) throws TransactionException; |
| TransactionDefinition | 事务定义。 | int getPropagationBehavior(); int getIsolationLevel(); int getTimeout(); boolean isReadonly(); String getName(); |
| TransactionStatus | 事务状态。 | boolean isNewTransaction(); boolean hasSavepoint(); void setRollbackonly(); boolean isRollbackonly(); void flush(); boolean isCompleted(); |
| DataSourceTransactionManager |
| JpaTransactionManager |
| JmsTransactionManager |
| JtaTransactionManager |
| 隔离级别 | 含义 |
| ISOLATION_DEFAULT | 使用后端数据库默认的隔离级别。 |
| ISOLATION_READ_UNCOMMITTED | 允许读取尚未提交的更改。可能导致脏读、幻读或不可重复读。 |
| ISOLATION_READ_COMMITTED | (Oracle 默认级别)允许从已经提交的并发事务读取。可防止脏读,但幻读和不可重复读仍可能会发生。 |
| ISOLATION_REPEATABLE_READ | (MYSQL默认级别)对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生。 |
| ISOLATION_SERIALIZABLE | 完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。 |
事务的传播性一般用在事务嵌套的场景,比如一个事务方法里面调用了另外一个事务方法,那么两个方法是各自作为独立的方法提交还是内层的事务合并到外层的事务一起提交,这就是需要事务传播机制的配置来确定怎么样执行。
Spring事务抽象-事务传播机制| 传播机制 | 说明 |
| PROPAGATION_REQUIRED | Spring默认的传播机制,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行。 |
| PROPAGATION_REQURES_NEW | 该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可。(内层事务与外层事务独立提交) |
| PROPAGATION_SUPPORT | 如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务。 |
| PROPAGATION_NOT_SUPPORT | 该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码。 |
| PROPAGATION_NEVER | 该传播机制不支持外层事务,即如果外层有事务就抛出异常。 |
| PROPAGATION_MANDATORY | 与NEVER相反,如果外层没有事务,则抛出异常。 |
| PROPAGATION_NESTED | 如果外层没有事务,则执行方式同REQUIRED,如果有事务,则嵌套调用(同时提交,同时回滚),如果外层事务提交,则会携带子事务一同提交,如果外层事务回滚,则会携带子事务一同回滚。 该传播机制还可以使用try...catch...保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。 |
传播规则回答了这样一个问题:一个新的事务应该被启动还是被挂起,或者是一个方法是否应该在事务性上下文中运行。
Spring事务实现声明式事务管理和编程式事务管理。
声明式事务管理:声明式事务管理建立在AOP之上,(通过代理实现)其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。
显然声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的编程方式。唯一不足的地方就是声明式事务管理的粒度是方法级别,而编程式事务管理是可以到代码块的,但是可以通过提取方法的方式完成声明式事务管理的配置。
Spring JPA transactionManager事务管理使用JPA EntityManagerFactory来做具体的事务管理。
Spring JMS TransactionManagerSpring JMS Session:
| 通过session进行事务管理。 |
| 通过Session的thread-bound(线程中的方法共用同一个session)。 |
| 事务上下文:在一个线程中的一个session。 |
Spring JMS事务类型:
Session管理的事务:Session原生事务。
通过Jmstemplate管理事务session只在Jmstemplate调用时受控制。
通过@JmsListener监听的整个方法受session事务控制,抛出异常时,无论是消息的消费还是生产均能够回滚,默认尝试7次后进入死信队列。
外部管理事务:JmsTransactionManger、JTA
Spring提供了一个JmsTransactionManager用于对JMS ConnectionFactory做事务管理。这将允许JMS应用利用Spring的事务管理特性。JmsTransactionManager在执行本地资源事务管理时将从指定的ConnectionFactory绑定一个ConnectionFactory/Session这样的配对到线程中。JmsTemplate会自动检测这样的事务资源,并对它们进行相应操作。
在Java EE环境中,ConnectionFactory会池化Connection和Session,这样这些资源将会在整个事务中被有效地重复利用。在一个独立的环境中,使用Spring的SingleConnectionFactory时所有的事务将公用一个Connection,但是每个事务将保留自己独立的Session。
事务上下文:在一个线程中的一个Session。
@JmsListener没有配置事务,会存在事务
查看 DefaultJmsListenerContainerFactoryConfigurer类源码,在configure方法中有这么一段,有外部事务的时候使用外部事务,没有的话使用session本地事务。
if (this.transactionManager != null) { factory.setTransactionManager(this.transactionManager); } else { factory.setSessionTransacted(true); }
如果使用自己配置的JmsListenerContainerFactory ,需添加factory.setSessionTransacted(true),
设置了sessionTransacted='true'后。如果抛异常是可以rollback重新放回队列,重新处理的。
Spring内部事务与外部事务:
本地事务:
| spring容器管理事务的生命周期。 |
| 通过spring事务接口调用。 |
| 业务代码与事务的实现无关。 |
本地事务调用流程:
外部(全局)事务:
| 外部事务管理器提供事务管理。 |
| 通过Spring事务接口,调用外部管理器。 |
| 使用JNDI等方式获取外部事务管理器的实例。 |
| 外部事务管理器一般由应用服务器提供,如Jboss。 |
外部(全局)事务 - JTA:
| 外部事务管理器提供JTA事务管理。 |
| JTA事务管理器可以管理多个数据资源。 |
| 通过两阶段提交实现多数据源的事务。 |
JTA一般有两种使用方式,外部应用服务器提供和依赖Java的库提供。
第一种使用外部容器来提供JTA事务支持,通常我们要配合JNDI(Java Naming and Directory Interface,Java命名和目录接口)进行使用,通过JNDI可以调用外部容器中的JTA事务管理器对象实例,并最终通过Spring提供的API来调用事务管理器。外部的事务管理器一般是由应用服务器提供,例如JBOSS等。
外部(全局)事务 - 使用应用服务器调用流程:
第二种事务管理器是通过依赖Java的Lib库的方式来提供。我们只需要依赖对应的对应的lib,由Spring来创建JTA事务管理的Bean。这样JTA事务管理器就存在于容器当中,也可以直接使用Spring的API来进行调用.例如Atomikos。
外部(全局)事务 - 不使用应用服务器调用流程:(在Spring容器中创建JTA管理器 如atomikos)
XA与JTA:
| Transaction Manager | |
| XAresource | |
| 两阶段提交 |
调用流程:
XA分布式事务管理:
XA是由X/Open组织提出的分布式事务的规范。 XA规范主要定义了(全局)事务管理器(TM)和(局 部)资源管理器(RM)之间的接口。主流的关系型 数据库产品都是实现了XA接口的。
XA接口是双向的系统接口,在事务管理器Transaction Manager(TM)以及一个或多个资源管理器Resource Manager(RM)之间形成通信桥梁。
XA之所以需要引入事务管理器是因为,在分布式系统中,从理论上讲两台机器理论上无法达到一致的状态,需要引入一个单点进行协调。
由全局事务管理器管理和协调的事务,可以跨越多个资源(如数据库或JMS队列)和进程。 全局事务管理器一般使用 XA 二阶段提交协议 与数据库进行交互。
| 接口 | 说明 |
| 事务管理器-TransactionManager | 事务管理器是分布式事务的核心管理者。事务管理器与每个资源管理器(resource manager)进行通信,协调并完成事务的处理。事务的各个分支由唯一命名进行标识。 |
| 资源管理器-XAResource | 用来管理系统资源,是通向事务资源的途径。数据库就是一种资源管理器。资源管理还应该具有管理事务提交或回滚的能力。 |
| XID | Xid 接口是 X/Open 事务标识符 XID 结构的 Java 映射。此接口指定三个访问器方法,以检索全局事务格式 ID、全局事务 ID 和分支限定符。Xid 接口供事务管理器和资源管理器使用。此接口对应用程序不可见。 |
XA 不能自动提交。
分段提交:
XA需要两阶段提交: prepare 和 commit。
| 阶段 | 说明 |
| 第一阶段 - PREPARE | 所有的参与者准备执行事务并锁住需要的资源。参与者ready时,向transaction manager报告已准备就绪。 |
| 第二阶段 - COMMIT | 当transaction manager确认所有参与者都ready后,向所有参与者发送commit命令 |
事务协调/管理者:
因为XA 事务是基于两阶段提交协议的,所以需要有一个事务协调者(transaction manager)来保证所有的事务参与者都完成了准备工作(第一阶段)。如果事务协调者(transaction manager)收到所有参与者都准备好的消息,就会通知所有的事务都可以提交了(第二阶段)。MySQL 在这个XA事务中扮演的是参与者的角色,而不是事务协调者(transaction manager)。
XA性能局限性:
| 效率低下,准备阶段的成本持久,全局事务状态的成本持久,性能与本地事务相差10倍左右。 |
| 提交前,出现故障难以恢复和隔离问题。 |
外部(全局)事务-JTA(Java Transaction API):
| JTA是XA协议在Java中的实现。 |
| 外部事务管理器提供JTA事务管理。 |
| JTA事务管理器可以管理多个数据资源。 |
| 通过两个阶段提交实现多数据源的事务。 |
调用流程:
JTA事务管理器的用途:
JTA中数据资源的管理是根据具体的数据资源的资源管理器实现。
JTA事务管理的弊端:
| 两阶段提交。 |
| 事务时间太长,锁数据太长。 |
| 低性能,低吞吐量。 |
JTA原理:
主要的原理是两阶段提交,当整个业务完成了之后只是第一阶段提交,在第二阶段提交之前会检查其他所有事务是否已经提交,如果前面出现了错误或是没有提交,那么第二阶段就不会提交,而是直接rollback操作,这样所有的事务都会做Rollback操作。
JTA的特点:
| JTA的有点就是能够支持多数据库事务同时事务管理,满足分布式系统中的数据的一致性。 |
JTA的使用:
JTA可以用于分布式系统中分布式事务的管理.原理是通过两阶段的提交,可以同时管理多个数据源的事务。
但是JTA也有比较严重的性能问题,由于同时操作多个数据源,如果其中一个数据源获取数据的时间过长,会导致整个请求都非常的长,因此现实中对性能要求比较高的系统较少使用JTA事务管理。
常用分布式系统事务管理实现高性能和高吞吐的方式是Spring事务同步机制以及牺牲掉事务的暂时一致性,而保证事务的最终一致性。
Spring事务的配置方式:
Spring支持编程式事务管理以及声明式事务管理两种方式。
编程式事务管理:
编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。
编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现,只需要在配置文件中做相关的事务规则声明或者通过注解的方式,便可以将事务规则应用到业务逻辑中。
只读:如果一个事务只对数据库执行读操作,那么该数据库就可能利用那个事务的只读特性,采取某些优化措施。通过把一个事务声明为只读,可以给后端数据库一个机会来应用那些它认为合适的优化措施。由于只读的优化措施是在一个事务启动时由后端数据库实施的, 因此,只有对于那些具有可能启动一个新事务的传播行为(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、 ROPAGATION_NESTED)的方法来说,将事务声明为只读才有意义。
事务超时:为了使一个应用程序很好地执行,它的事务不能运行太长时间。因此,声明式事务的下一个特性就是它的超时。
假设事务的运行时间变得格外的长,由于事务可能涉及对数据库的锁定,所以长时间运行的事务会不必要地占用数据库资源。这时就可以声明一个事务在特定秒数后自动回滚,不必等它自己结束。
由于超时时钟在一个事务启动的时候开始的,因此,只有对于那些具有可能启动一个新事务的传播行为(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、ROPAGATION_NESTED)的方法来说,声明事务超时才有意义。
回滚规则:在默认设置下,事务只在出现运行时异常(runtime exception)时回滚,而在出现受检查异常(checked exception)时不回滚(这一行为和EJB中的回滚行为是一致的)。
不过,可以声明在出现特定受检查异常时像运行时异常一样回滚。同样,也可以声明一个事务在出现特定的异常时不回滚,即使特定的异常是运行时异常。
Spring声明式事务管理:
事务的传播性:@Transactional(propagation=Propagation.REQUIRED)
事务的隔离级别:@Transactional(isolation = Isolation.READ_UNCOMMITTED)
读取未提交数据(会出现脏读, 不可重复读) 基本不使用
只读:@Transactional(readonly=true)
该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认false。
事务的超时:@Transactional(timeout=30)
回滚:
指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。



