Spring事务管理小结

Spring配置事务管理有多种方法,本文总结其中常用的两种,第一种是直接在XML文件中配置,另一种是使用@Transactional注解。总结之前,下面先对Spring事务管理的一些基础知识做一些概要总结。

事务管理是为了保证数据的一致性,当程序出现异常时,可以通过rollback进行回滚。Spring支持编程式事务管理和声明式事务管理两种方式。

编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。

声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。

显然声明式事务管理要优于编程式事务管理,这也是spring推荐的,所以本文也只总结声明式事务管理的配置方法。声明式事务管理有两种常用的方式,一种是基于tx和aop命名空间的xml配置文件,另一种是基于@Transactional注解。一般来说,基于注解的方法更加简单、易用、灵活,但是需要对每一个需要的地方都加上注解,配置的地方比较多,如果对于一些比较通用的方法名,比如save*、update*这种,可以直接在配置文件中进行统一配置。

1、在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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

	<!-- 如果将service和dao的扫描放到springmvc-servlet.xml中会发现事务不能生效 -->
	<!-- 如果将controller的扫描也放到applicationContext.xml中会发现URI Mapping有问题 -->
	<!-- 所以,在这里将controller的扫描和service、dao的扫描分开 -->
	<context:component-scan base-package="**.service.impl" />
	<context:component-scan base-package="**.dao.impl" />

	<!-- 加载数据库连接信息db.properties -->
	<context:property-placeholder location="/WEB-INF/config/db.properties" />

	<!-- 配置数据源 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="${jdbc.driverClassName}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="username" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>

	<!-- 配置sqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<!-- 配置mapper扫描路径 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="**.*.mapper"></property>
	</bean>

	<!-- 配置事务管理器 -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<!-- 配置事务的advice -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
			<tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
			<tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
			<tx:method name="select*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception" timeout="3000" read-only="true"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- 配置事务的切面 -->
	<aop:config>
		<aop:pointcut expression="execution(* **.service.impl.*.*(..))" id="txPointcut"/>
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
	</aop:config>
</beans>

如上,上面是配置文件中已经对事务做好了配置,对**.service.impl包下的任意类的指定方法都加上了事务管理,这指定方法包括:以save开头的方法、以update开头的方法、以delete开头的方法、以select开头的方法。

2、使用@Transactional注解

<?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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

	<!-- 如果将service和dao的扫描放到springmvc-servlet.xml中会发现事务不能生效 -->
	<!-- 如果将controller的扫描也放到applicationContext.xml中会发现URI Mapping有问题 -->
	<!-- 所以,在这里将controller的扫描和service、dao的扫描分开 -->
	<context:component-scan base-package="**.service.impl" />
	<context:component-scan base-package="**.dao.impl" />

	<!-- 加载数据库连接信息db.properties -->
	<context:property-placeholder location="/WEB-INF/config/db.properties" />

	<!-- 配置数据源 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="${jdbc.driverClassName}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="username" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>

	<!-- 配置sqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<!-- 配置mapper扫描路径 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="**.*.mapper"></property>
	</bean>

	<!-- 配置事务管理器 -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<!-- 启用事务注解,后面在Service类或方法上面加上@Transactional注解 -->
	<tx:annotation-driven transaction-manager="transactionManager" />
</beans>

上面这段配置信息就是使用注解的必要配置信息,配置<tx:annotation-driven transaction-manager="transactionManager" />来启用事务注解。

使用@Transactional注解的时候,语法如下:

@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class, isolation=Isolation.DEFAULT, timeout=3000)

@Transactional注解可以加在接口、接口方法、类、类方法上。作用于类上的时候,该类的所有public方法都将具有该类型的事务属性;同时,也可以在方法级别上使用该注解来覆盖类级别的定义。虽然@Transactional注解可以作用于接口及接口方法上,但是spring建议不要这样使用。

默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是说,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用了@Transactional注解进行修饰。

Spring中一些事务知识的概括:

(1)事务管理器

事务 说明
org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Spring JDBC或MyBatis进行持久化数据时使用
org.springframework.orm.hibernate4.HibernateTransactionManager 使用Hibernate4版本时进行持久化数据时使用
org.springframework.orm.hibernate3.HibernateTransactionManager 使用Hibernate3版本时进行持久化数据时使用
org.springframework.orm.jpa.JpaTransactionManager 使用JPA进行持久化数据时使用
org.springframework.orm.jdo.JdoTransactionManager 当持久化机制是Jdo时使用
org.springframework.transaction.jta.JtaTransactionManager 使用一个JTA实现来管理事务,在一个事务跨很多个资源时必须使用

目前常用的持久层框架是Hibernate、MyBatis,所以常用的事务管理器是上表的前面三种。

(2)事务隔离级别

事务隔离级别是指若干个并发的事务之间的隔离程度。Isolation接口中定义了5个表示隔离级别的常量:

隔离级别
含义
Isolation.DEFAULT 默认缺省值,表示使用底层数据库的默认隔离级别,大部分数据库中,default表示的也就是read_committed级别。(Spring默认隔离级别)
Isolation.READ_UNCOMMITTED 表示一个事务可以读取另一个事务修改但是还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,很少使用该级别。
Isolation.READ_COMMITTED 表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,也是大多数情况下的推荐值。
Isolation.REPEATABLE_READ 表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
Isolation.SERIALIZABLE 表示所有的事务依次逐个执行,这样事务之间就完全不可能干扰。该级别可以防止脏读、不可重复读以及幻读。但是会严重影响程序性能,通常情况下不会使用该级别。

(3)事务传播行为

事务传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时要怎么去执行当前事务的行为。Propagation中也定义了7个表示事务传播行为的常量:

事务传播行为类型
说明
Propagation.REQUIRED 默认缺省值。表示如果当前存在事务,则加入事务;如果当前没有事务存在,则创建一个新的事务。
Propagation.REQUIRES_NEW 表示创建一个新的事务,如果当前存在事务,则把当前事务挂起。
Propagation.SUPPORTS 表示如果当前存在事务,则加入事务;如果当前不存在事务,则以非事务的方式继续运行。
Propagation.NOT_SUPPORTED 表示以非事务方式运行,如果当前存在事务,则把当前事务挂起。
Propagation.NEVER 表示以非事务方式运行,如果当前存在事务,则抛出异常。
Propagation.MANDATORY 表示如果当前存在事务,则加入事务;如果当前没有事务,则抛出异常。
Propagation.NESTED 表示如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则创建一个新的事务。

(4)事务超时

事务超时指的是一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。以int的值作为超时时间,单位为秒,例如timeout=3000。默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就没有超时限制。

(5)事务只读属性

只读事务表示创建的是一个只读事务,该事务下只能读数据,不能写数据。默认情况下,事务是读写数据,即数据可读可写。

(6)spring事务回滚规则

spring事务回滚的推荐方法是在当前事务的上下文内抛出异常,spring事务管理器捕获任何未处理的异常,然后根据规则决定是否回滚抛出异常的事务。默认配置下,spring只在抛出的异常为运行时异常(RuntimeException)才回滚事务,而抛出检查时异常则不会回滚。

本文标题:Spring事务管理小结

本文链接:http://yedward.net/?id=347

本文版权归作者所有,欢迎转载,转载请以文字链接的形式注明文章出处。

相关文章