事务(Transaction),在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在开发应用系统中,事务的使用是必不可少的一部分。Spring框架提供了事务处理机制。Spring为事务管理提供了丰富的功能支持。Spring事务管理分为编码式和声明式的两种方式。编程式事务指的是通过编码方式实现事务;声明式事务基于AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。声明式事务有两种方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于@Transactional注解的方式。下面将介绍Spring事务的使用。
1、声明式事务
使用Spring的声明式事务,我们无需编写程序代码,所有的工作全在配置文件中完成。
1.1 下载依赖的jar包
除了下载Spring框架包,还需要下载aspectjweaver.jar包,aspectjweaver是spring AOP的切入点表达式需要用到的包。spring配置中的<aop:config>节点需要引入aspectjweaver.jar。
1.2 编写配置信息
在Spring核心配置文件applicationContext.xml中,配置声明式事务信息。
<!-- 事务管理 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 事务通知 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="all*" propagation="REQUIRED"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务的代理 -->
<aop:config>
<!-- 定义一个切面 -->
<aop:pointcut id="allManagerMethod" expression="execution(* com.pjb.fms.biz.impl.*.*(..))"/>
<!-- 将事务通知与切面组合 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="allManagerMethod"/>
</aop:config>
(1)通过<tx:advice>定义事务通知,需要指定一个事务管理器,然后在其属性中声明事务规则。
(2)然后定义一个切面(pointcut,即定义哪些方法应用这些规则):
<!-- 定义一个切面 -->
<aop:pointcut id="allManagerMethod" expression="execution(* com.pjb.fms.biz.impl.*.*(..))"/>
上面的代码表示:com.pjb.fms.service.impl包下的所有类的所有方法都应用事务规则。
(3)最后,将事务通知和切面组合:
<!-- 将事务通知与切面组合 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="allManagerMethod"/>
2、基于@Transactional注解的事务
另外还可以使用基于@Transactional注解的方式,注释配置是目前流行的使用方式。
了解Spring中@Transactional注解的详细使用,请求点击:透彻的掌握Spring中@transactional的使用
2.1 编写配置信息
使用@Transactional注解管理事务,首先需要配置事务的信息。除了用配置文件的方式,@EnableTransactionManagement 注解也可以启用事务管理功能。这里以简单的 DataSourceTransactionManager 为例。
在Spring核心配置文件中配置如下信息:
<!-- 事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- SpringMVC注解模式(设置注解映射器、注解适配器) -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
注意:如果项目使用了SpringMVC,而且配置<mvc:annotation-driven/>自动注解标签,则表明使用SpringMVC提供的注解型配置,那么这种情况<mvc:annotation-driven/>自动注解标签的写法需要如上述配置。
2.2 编写业务代码
编写业务方法代码,并使用@Transactional注解。
/**
* 新增用户信息
* @author pan_junbiao
*/
@Transactional
public void addUserInfo()
{
//新增用户A(该方法将执行成功)
addUseA();
//新增用户B(该方法将执行失败)
addUseB();
}
当执行addUserInfo()方法时,其中的addUseA()方法将正常执行,但是由于addUseB()方法执行异常,所以最终事务将被回滚。
2.3 @Transactional注解的类级别
@Transactional注解还可以写在类上,表示该类中的所有public方法都将进行事务管理。
/**
* 用户信息业务逻辑类
* @author pan_junbiao
**/
@Service
@Transactional
public class UserServiceImpl implements UserService
{
}
注意:只有@Transactional注解应用到public方法,才能进行事务管理。
3、解决 try/catch 后事务失效问题
Spring管理的事务,无论是声明式事务还是注解式事务默认是在抛出运行异常(RuntimeException异常)时,才会被Spring框架捕获到然后回滚,如果在需要执行事务管理的方法里就已经try,catch了。那么Spring就捕获不到这个异常,也就不会回滚了。
/**
* 新增用户信息
* @author pan_junbiao
*/
@Transactional
public void addUserInfo()
{
try
{
//新增用户A(该方法将执行成功)
addUseA();
//新增用户B(该方法将执行失败)
addUseB();
}
catch (Exception ex)
{
//事务失效:由于发生异常的代码被try/catch起来,
//所以Spring框架无法捕获异常,从而事务处理失效。
}
}
上述代码中,由于发生异常的代码被try/catch起来,所以Spring框架无法捕获异常,从而事务处理失效。
注意:默认情况下,如果在事务中抛出了未检查异常(继承自RuntimeException的异常)或者Error,则Spring将回滚事务;除此之外,Spring不会回滚事务。
3.1 解决方法一
可以在catch块中,将异常再次抛出。
/**
* 新增用户信息
* @author pan_junbiao
*/
@Transactional
public void addUserInfo()
{
try
{
//新增用户A(该方法将执行成功)
addUseA();
//新增用户B(该方法将执行失败)
addUseB();
}
catch (Exception ex)
{
logger.error("记录日志");
throw new RuntimeException("新增用户信息执行异常");
//throw ex;
}
}
3.2 解决方法二
首先在catch块中,添加如下代码:
//手动硬编码开启Spring事务管理
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
然后为了让所有异常都会让事务启动,可以将 @Transactional配置为 @Transactional(rollbackFor = Exception.class)。
/**
* 新增用户信息
* @author pan_junbiao
*/
@Transactional(rollbackFor = Exception.class)
public void addUserInfo()
{
try
{
//新增用户A(该方法将执行成功)
addUseA();
//新增用户B(该方法将执行失败)
addUseB();
}
catch (Exception ex)
{
logger.error("记录日志");
//手动硬编码开启spring事务管理
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}