Spring事务机制详细解读

2 篇文章 0 订阅

JAVE EE传统的事务机制

 通常有两种策略:全局事务和局部事务。全局事务可以跨多个事务性资源(即数据源,典型的是数据库和消息队列),通常都需要J2EE应用服务器的管理,其底层需要服务器的JTA支持。而局部事务则与底层采用的持久化技术有关,如果底层直接使用JDBC,需要用Connection对象来操事务。如果采用Hibernate持久化技术,则需要使用session对象来操作事务。
 (1)全局事务由应用服务器管理,需要底层服务器JTA支持(如WebLogic、JBoss等)。
 (2)JDBC事务:每个Connection都带有一个事务,只是默认被设置为自动提交。一个连接可以有多个事务。对于JDBC,只有在同一个连接内,才有讨论是否提交的前提。
 (3)Hibernate事务:本质上也是使用JDBC来处理事务。但是不是直接操作,而是使用Session来操作事务。使用Session.getTranction()
 采用传统事务编程,程序代码必须和具体的事务策略的API耦合,如果应用需要切换一种策略,意味着需要大幅修改代码。但是如果使用Spring事务的话,就不会有这个问题。

Spring事务机制:

 Sring没有提供任何事务支持,它只是负责包装底层的事务,而在Spring层面,对外提供统一的编程API。Spring事务的核心是PlatformTransactionManager接口。
 PlatformTransactionManager代表与具体类型无关的事务接口,可以代表任何事务,包括JDBC事务,Hibernate事务,甚至是JTA事务。
 PlatformTransactionManager提供了如下三个接口: 
public interface PlatformTransactionManager {
	/*开始事务*/
	TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
	/*提交事务*/
	void commit(TransactionStatus status) throws TransactionException;
	/*回滚事务*/
	void rollback(TransactionStatus status) throws TransactionException;
}
 Spring事务机制是一种典型的策略模式,PlatformTransactionManager代表事务管理接口,但它并不知道到底如何管理事务,它只要求事务管理提供开始事务getTransaction(),提交事务commit()和回滚事务rollback()这三个方法,但具体如何实现则交给其实现类完成。编程人员只需要在配置文件中根据具体需要使用的事务类型做配置,Spring底层就自动会使用具体的事务实现类进行事务操作,而对于程序员来说,完全不需要关心底层过程,只需要面向PlatformTransactionManager接口进行编程即可。PlatformTransactionManager接口中提供了如下方法:getTransaction(..), commit(); rollback(); 这些都是与平台无关的事务操作。

 一、Spring事务属性:

 Spring声明式事务时,有一个非常重要的概念就是事务属性。事务属性通常由事务的传播行为,事务的隔离级别,事务的超时值和事务只读标志组成。
 Spring在TransactionDefinition接口中定义这些属性,以供PlatfromTransactionManager使用,TransactionDefinition接口定义了如下方法:
public interface TransactionDefinition {
	int TIMEOUT_DEFAULT = -1;//表示永不超时
	int getPropagationBehavior();//返回事务的传播行为。
	int getIsolationLevel();//返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。
	int getTimeout();//返回事务必须在多少秒内完成
	boolean isReadOnly();//事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的,默认为false
}

1、TransactionDefinition接口中定义五个隔离级别(isolation):

说明:
 脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。
 不可重复读:在同一事务中,多次读取到同一数据的结果有所不同。也就是说,后续读取可以读到另一个事务已经提交的更新数据。
 幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了,再查询的时候,第一个事务就会发现有些原来没有的记录。

2. 在TransactionDefinition接口中定义了七个事务传播行为(propagationBehavior):

3、TransactionDefinition接口中定义了事务超时
 所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。
 默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。
4、事务只读属性
 只读事务用于客户代码只读但不修改数据的情形,只读事务用于特定情景下的优化,比如使用Hibernate的时候,默认为读写事务。

二、Spring事务管理:

 Spring的事务管理,主要有两种方式实现,一种是编程式事务管理,在代码中控制事务,一种是声明式事务(又可分为AOP式事务管理和注解式事务管理)。在代码中编写要更加细粒度,而很多时候我们只需要简单的事务处理,那就可以用声明式事务。
Spring事务管理器:

事务管理器目标
org.springframework.jdbc.datasource.DataSourceTransactionManager在JDBC DataSource中管理事务(须注入数据源datasource bean参数)
org.springframework.orm.hibernate.HibernateTransactionManager管理Hibernate事务(须注入SessionFactory Bean参数)
org.springframework.orm.jdo.JdoTransactionManager管理JDO事务
org.springframework.transaction.jta.JtaTransactionManager使用一个JTA管理事务,在一个事务跨越多个资源时必须使用(无须注入参数)
org.springframework.orm.ojb.PersistenceBrokerTransactionManager管理Apache的OJB事务










 这些事务管理器的的父接口都是PlatformTransactionManager.Spring的事务管理机制是一种典型的策略模式,PlatformTransactionManager 代表事务管理接口(该接口定义了下面所说的三个方法),他并不知道底层如何管理事务,他只要求事务管理的实现类提供开始事务 (getTransaction())、提交事务(commit())、回滚事务(rollback()),但具体如何实现则交给具体的实现类完成——不 同的实现类代表不同的事务管理策略。

说明:

 1、JTA事务管理器无须注入参数,是因为全局事务的JTA资源由Java ee服务器提供,而Spring容器能自行从Java ee服务器中获取该事务资源,所以无须使用依赖注入来配置。
 2、当使用JTA全局事务策略时,实际底层须应用服务器支持,而不同的应用服务器所提供的JTA全局事务可能存在细节上的差异,因此实际配置全局事务管理器是可能需要使用JtaTransactionManager的子类,如:OC4JtaTransactionManager(Oracle提供的应用服务器)、WebLogicJtaTransactionManager(Bea提供的WebLogic)、UowJtaTransactionManager(IBM提供的WebSphere)等
Spring事务回滚规则:
 Spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。
 默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。可以明确的配置在抛出那些异常时回滚事务,包括checked异常。也可以明确定义那些异常抛出时不回滚事务。还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚,在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚。

三、Spring事务实现:

1、编程式事务管理实现:

第一步:编写配置文件:
<?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" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
	http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.2.xsd 
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd 
	">

	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
		destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="${mysql.jdbc.url}" />
		<property name="username" value="${mysql.jdbc.username}" />
		<property name="password" value="${mysql.jdbc.password}" />

		<property name="maxActive" value="${maxActive}" />
		<property name="initialSize" value="${initialSize}" />
		<property name="maxWait" value="${maxWait}" />
		<property name="minIdle" value="${minIdle}" />

		<property name="validationQuery" value="${validationQuery}" />
		<property name="testWhileIdle" value="${testWhileIdle}" />
		<property name="testOnBorrow" value="${testOnBorrow}" />
		<property name="testOnReturn" value="${testOnReturn}" />

		<property name="timeBetweenEvictionRunsMillis" value="25000" />
		<property name="minEvictableIdleTimeMillis" value="300000" />

	</bean>

	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="mapperLocations" value="classpath*:/sqlmap/**/*Mapper.xml" />
		<property name="configLocation" value="classpath:spring/mybatis-config.xml" />  
	</bean>

	<!-- ScanMapperFiles -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.sf.amp.mapper" />
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
	</bean>

	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
      <property name="dataSource" ref="dataSource" />
   </bean>
	
	<!-- 声明事务模板 -->
	<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
		<property name="transactionManager" ref="transactionManager" />
	</bean>
	
	<bean id="appManagerImpl" class="com.sf.amp.manager.impl.AppManagerImpl">
      <property name="transactionTemplate" ref="transactionTemplate" />
    </bean>

</beans>
 配置文件中配置了一个org.springframework.transaction.support.TransactionTemplate实例,要在代码中添加事务,Spring为我们提供了一种方法就是使用TransactionTemplate类。我们要为TransactionTemplate装配一个TransactionManager。
第二步:使用TransactionTemplate进行事务管理:
package com.arain.amp.manager.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import com.arain.amp.domain.App;
import com.arain.amp.manager.AppManager;
import com.arain.amp.mapper.AppMapper;
import com.arain.amp.util.UserManager;

/**
 * @author arain.liu
 *
 */
@Component
public class AppManagerImpl implements AppManager {
	
    @Autowired

  
  
private AppMapper appMapper;
private TransactionTemplate transactionTemplate; public TransactionTemplate getTransactionTemplate() { return transactionTemplate; } public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } @Override public int insert(final App app) { int count = 0; //开始事务,如果出现事务,则回滚 count = transactionTemplate.execute(new TransactionCallback<Integer>() { @Override public Integer doInTransaction(TransactionStatus ts){ int num = 0; try { app.setCreatedBy(UserManager.getCurrentUserNo()); int temp = appMapper.insertSelective(app); int count2 = 1/0; num = temp; } catch (Exception e) { ts.setRollbackOnly(); } return num; } }); return count; } }
 调用TransactionTemplate实例的execute()方法将执行包含在TransactionCallback实例里的代码。如果代码出现异常,调用TransactionStatus对象的setRollbackOnly()将事务回滚。否则,如果doInTransaction()方法 正常返回,事务将被提交。

2、声明式事务管理实现:

① 使用XML Schema配置事务策略
当使用声明式事务时,只需要写好配置文件,配置需要事务控制的组件种类,业务组件就会在AOP机制下被织入事务控制,而编程人员不需要写任何事务管理代码,可以专注于业务的开发。因此通常都推荐使用声明式事务管理。
Spring的XML Schema方式提供了简洁的事务配置策略,通过命名空间<tx:advice>来配置一个事务增强处理,其中可以指定事务的各种属性(例如隔离属性,传播属性,超时,只读属性等),然后通过<aop:config>标签可以将事务的增强与AOP的切入点(即Bean的执行方法)进行绑定,从而实现对Bean的方法织入事务操作。
完整的配置如下:
 <tx:advice id="transactionService" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="add*" propagation="REQUIRED" />
			<tx:method name="insert*" propagation="REQUIRED" />
			<tx:method name="delete*" propagation="REQUIRED" />
			<tx:method name="remove*" propagation="REQUIRED" />
			<tx:method name="update*" propagation="REQUIRED" />
			<tx:method name="save*" propagation="REQUIRED" />
			<tx:method name="select*" propagation="SUPPORTS" />
			<tx:method name="find*" propagation="SUPPORTS" />
			<tx:method name="get*" propagation="SUPPORTS" />

		</tx:attributes>
	</tx:advice>
	<aop:config>
		<aop:pointcut id="transactionPointcut"
			expression="execution(* com.arain.amp.manager.impl.*.*(..))" />
		<aop:advisor pointcut-ref="transactionPointcut"
			advice-ref="transactionService" />
	</aop:config>
配置完成后只需要根据切面中<tx:advice>中的方法前缀对应的传播行为类型进行对应的编码就OK了。

② 基于注解@Transactionl配置事务策略

@Transactional属性

属性类型描述
valueString可选的限定描述符,指定使用的事务管理器
propagationenum: Propagation可选的事务传播行为设置
isolationenum: Isolation可选的事务隔离级别设置
readOnlyboolean读写或只读事务,默认读写
timeoutint (in seconds granularity)事务超时时间设置
rollbackForClass对象数组,必须继承自Throwable导致事务回滚的异常类数组
rollbackForClassName类名数组,必须继承自Throwable导致事务回滚的异常类名字数组
noRollbackForClass对象数组,必须继承自Throwable不会导致事务回滚的异常类数组
noRollbackForClassName类名数组,必须继承自Throwable不会导致事务回滚的异常类名字数组










 @Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有public方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
 直接在方法上添加@Transaction注解,使这个方法具有事务属性。在@Transaction中可以为事务配置各种属性(例如隔离属性,传播属性,超时,只读属性等),还需要在在XML配置中加入<tx:annotation-triven配置表明Spring会根据注解来配置事务代理。如下:
<!-- 注解驱动事务管理 -->  
<tx:annotation-driven transaction-manager="transactionManager"/>  
代码中实现:
@Override  
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout = 5)  
public int insert(App app) {  
    app.setCreatedBy(UserManager.getCurrentUserNo());  
    int num = appMapper.insertSelective(app);  
    int i = 1/0;  
    return num;  
}  
 虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是Spring建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外,@Transactional注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用@Transactional注解,这将被忽略,也不会抛出任何异常。
 默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

参考:
  https://www.cnblogs.com/fysola/p/6384399.html
 https://www.cnblogs.com/wzd5230/p/5854932.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值