在分析深入分析@Transactional的使用之前,我们先回顾一下事务的一些基本内容。

事务的基本概念

先来回顾一下事务的基本概念和特性。数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。事务,就必须具备ACID特性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。

编程式事务与声明式事务

Spring 与Hibernate的整合实现的事务管理是常用的一种功能。Hibernate 建议所有的数据库访问都应放在事务内进行,即使只进行只读操作。事务又应该尽可能短,因为长事务会导致长时间无法释放表内行级锁,从而降低系统并发的性 能。 Spring 同时支持编程式事务和声明式事务。

编程式事务需要在代码中显式调用beginTransaction()、commit()、rollback()等事务管理相关的方法。Spring 的声明式事务管理在底层是建立在 AOP 的基础之上的,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。事务增强也是AOP的一大用武之处。

使用声明式事务

声 明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明,很好的 分离了业务逻辑和事务管理逻辑。以spring+jpa整合增加声明式事务为例,下面是使用声明式事务的配置方式以及使用。

applicationContext.xml配置

<!-- 配置entityManagerFactory 配置 -->

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

<property name="dataSource" ref="dataSource" />

<property name="persistenceXmlLocation" value="/WEB-INF/classes/persistence.xml" />

<property name="loadTimeWeaver">

<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />

</property>

</bean>

<!-- 配置声明式事务管理,使用JpaTransactionManager作为事务管理器的实现类 –>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">

<property name="entityManagerFactory" ref="entityManagerFactory" />

</bean>

<!--声明业务组件使用注解生成事务代理 -->

<tx:annotation-driven transaction-manager="transactionManager" />

使用@Transactional 配置声明式事务管理

在spirng 的配置文件中配置了事务管理和注解驱动之后,我们就可以在业务层使用@Transactional 配置声明式事务管理了。如下

@Service

@Transactional

public class BaseService<T> implements IBaseService<T> {

@Autowired

private IBaseDao<T> baseDao;

......

}

@Transactional 深入使用

上面我们已经知道了声明式事务管理的用法了,下面我们将进一步分析@Transactional在各种场景下用法。

事务的传播行为

@Transactional注解支持9个属性的设置,其中Propagation属性用来枚举事务的传播行为。所谓事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。Spring 支持 7 种事务传播行为:

REQUIRED 是常用的事务传播行为,如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。其它传播行为大家可另查阅。

多个事务方法的嵌套调用时的事务传播

有三个事务方法调用嵌套关系如下:

PayHisInfoService.update()->PayHisInfoService.save()->PayInfoService.update()

@Transactional(propagation=Propagation.REQUIRED)

public abstract class BaseService<E>{

public abstract List<E> findAll();

}

@Component

public class PayHisInfoService extends BaseService<PayHisInfo>{

@Resource

private PayHisInfoDao payHisInfoDao;

@Resource

private PayInfoService payInfoService;

public  void update(PayHisInfo payHisInfo,PayInfo payInfo){

save(payHisInfo);

payInfoService.update(payInfo);

}

public int save(PayHisInfo payHisInfo){

return payHisInfoDao.insert(payHisInfo);

}

}

@Component

public class PayInfoService extends BaseService<PayInfo>{

@Resource

private transient PayInfoDao payInfoDao;

@Override

@Transactional(propagation=Propagation.SUPPORTS, readOnly=true)

public List<PayInfo> findAll() {

return payInfoDao.findAll();

}

public int update(PayInfo payInfo){

return payInfoDao.update(payInfo);

}

}

当我们按照上面的调用嵌套关系执行时,结果如下

从 日志中我们可以看出,PayHisInfoService.update()执行时开启了一个事务,而PayHisInfoService.save() 方法和PayHisInfoService.update()方法处于同一个类中,调用时没有发生事务传播,就像已经处于 PayHisInfoService.update()方法的事务当中一样。而当执行PayInfoService.update()时,提示 “Participating in existing transaction”,这说明发生了事务传播,即PayInfoService.update()并没有新建一个事务,而是加入到已有的 PayHisInfoService.update()事务中。

多线程环境下的事务传播

现在我们将上面例子中的PayHisInfoService.update()用另一个线程来执行,如下

@Component

public class PayHisInfoService extends BaseService<PayHisInfo>{

@Resource

private PayHisInfoDao payHisInfoDao;

@Resource

private PayInfoService payInfoService;

public  void update(PayHisInfo payHisInfo,PayInfo payInfo){

save(payHisInfo);

//payInfoService.update(payInfo);

PayInfoThread pifth = new PayInfoThread(payInfoService,payInfo);

pifth.start();

}

public int save(PayHisInfo payHisInfo){

return payHisInfoDao.insert(payHisInfo);

}

private class PayInfoThread extends Thread{

private PayInfoService payInfoService;

private PayInfo payInfo;

private PayInfoThread(PayInfoService payInfoService,PayInfo payInfo) {

this.payInfoService = payInfoService;

this.payInfo = payInfo;

}

public void run() {

System.out.println("PayInfoThread updating payInfo...");

payInfoService.update(payInfo);

}

}

}

当我们按照上面的调用嵌套关系执行时,结果如下

从 日志结果可以看出,在执行PayHisInfoService.update()和PayInfoService.update()时分别创建了各自的事 务。而PayHisInfoService.save()和PayHisInfoService.update()则在同一个线程中执行。所以spring 的事务管理是线程安全的。

在相同线程中进行相互嵌套调用的事务方法工作于相同的事务中。如果这些相互嵌套调用的方法工作在不同的线程中,不同线程下的事务方法工作在独立的事务中。

@Transactional的继承关系

或许你已经在上面的例子中看出了,我们只在父类BaseService中声明了@Transactional,子类就自然得到事务增强。注解并没有继承这种说法,但此处用“继承关系”来形容父类@Transactional和子类方法之间的关系最恰当不过了:父类Service声明了@Transactional,子类继承父类,父类的声明的@Transactional会对子类的所有方法进行事务增强。这个还有个很实用的用法,例如测试基类继承

AbstractTransactionalJUnit4SpringContextTests

声明@Transactional可方便进行事务测试

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration("/applicationContext.xml")

@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)

@Transactional

public class BaseTest extends AbstractTransactionalJUnit4SpringContextTests {

public Object getBean(String beanName){

return applicationContext.getBean(beanName);

}

}

给BaseTest 声明了@Transactional了之后,就会自动给我们自己编写的测试类中的所有测试方法进行事务增强。

@Transactional的优先级

如果子类的方法重写了父类的方法并且声明了@Transactional,那么子类的事务声明会优先于父类的事务声明。

利用spring的AOP进行事务切面增强

还是上面的列子,把父类声明的@Transactional去掉。在applicationContext.xml中配置事务增强切面,配置如下:

<!--②使用aop和tx命名空间语法为PayHisInfoService所有公用方法添加事务增强 -->

<aop:config proxy-target-class="true">

<aop:pointcut id="serviceJdbcMethod"

expression="execution(public * com.xxx.service.PayHisInfoService.*(..))"/>

<aop:advisor pointcut-ref="serviceJdbcMethod" advice-ref="jdbcAdvice" order="0"/>

</aop:config>

<tx:advice id="jdbcAdvice" transaction-manager="transactionManager">

<tx:attributes>

<tx:method name="*"/>

</tx:attributes>

</tx:advice>

运行测试用例,结果如下:

当有一条数据更新失败时,事务会自动回滚,如下:

@Transactional方法的可见度

上面为了测试演示方便,我们把@Transactional都声明在了类上。实际上@Transactional 可以作用于接口、接口方法、类以及类方法上。但是 Spring 小组建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的(从上面的Spring AOP 事务增强可以看出,就是针对方法的)。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

总结

(1)声明式事务优于编程式事务

(2)事务方法的嵌套调用会产生事务传播

(3)spring 的事务管理是线程安全的

(4)父类的声明的@Transactional会对子类的所有方法进行事务增强

(5)从Spring AOP本质看,@Transactional 注解应该只被应用到 public 方法上

Spring源码学习之:spring注解@Transactional的更多相关文章

  1. 框架源码系列六:Spring源码学习之Spring IOC源码学习

    Spring 源码学习过程: 一.搞明白IOC能做什么,是怎么做的  1. 搞明白IOC能做什么? IOC是用为用户创建.管理实例对象的.用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建 ...

  2. spring源码学习(三)--spring循环引用源码学习

    在spring中,是支持单实例bean的循环引用(循环依赖)的,循环依赖,简单而言,就是A类中注入了B类,B类中注入了A类,首先贴出我的代码示例 @Component public class Add ...

  3. 框架源码系列八:Spring源码学习之Spring核心工作原理(很重要)

    目录:一.搞清楚ApplicationContext实例化Bean的过程二.搞清楚这个过程中涉及的核心类三.搞清楚IOC容器提供的扩展点有哪些,学会扩展四.学会IOC容器这里使用的设计模式五.搞清楚不 ...

  4. 【spring源码学习】spring的aop目标对象中进行自我调用,且需要实施相应的事务定义的解决方案

    转载:http://www.iteye.com/topic/1122740 1.预备知识 aop概念请参考[http://www.iteye.com/topic/1122401]和[http://ji ...

  5. 【spring源码学习】Spring @PostConstruct和@PreDestroy实例

    在Spring中,既可以实现InitializingBean和DisposableBean接口或在bean配置文件中指定 init-method 和 destroy-method 在初始化和销毁回调函 ...

  6. 【spring源码学习】spring集成orm数据框架

    [一]简易的数据源配置 (1)配置文件 <!--springJdbcTemplemate数据操作配置信息 --> <bean id="driver" class= ...

  7. 【spring源码学习】spring的事务管理的源码解析

    [一]spring事务管理(1)spring的事务管理,是基于aop动态代理实现的.对目标对象生成代理对象,加入事务管理的核心拦截器==>org.springframework.transact ...

  8. 【spring源码学习】spring的远程调用实现源码分析

    [一]spring的远程调用提供的基础类 (1)org.springframework.remoting.support.RemotingSupport ===>spring提供实现的远程调用客 ...

  9. 【spring源码学习】spring的事件发布监听机制源码解析

    [一]相关源代码类 (1)spring的事件发布监听机制的核心管理类:org.springframework.context.event.SimpleApplicationEventMulticast ...

  10. 【spring源码学习】spring的IOC容器之自定义xml配置标签扩展namspaceHandler向IOC容器中注册bean

    [spring以及第三方jar的案例]在spring中的aop相关配置的标签,线程池相关配置的标签,都是基于该种方式实现的.包括dubbo的配置标签都是基于该方式实现的.[一]原理 ===>sp ...

随机推荐

  1. C# ClickOnce deployment for Windows Services ClickOnce 部署windows service

    A simple solution that I use is to merely stop the service and x-copy the files from my bin folder i ...

  2. char 数组和 int 之间转化

    上周工作结束,来到斯凯网络也将近半个月来. 没有新人的感念,最多的是将自己当作一个战士. 废话不多说,直接入正题,在没有仔细考虑问题之前我们总会 觉得:这尼玛的有毛线难度啊,不就是一个 int 转为c ...

  3. 【转】FFMPEG 库移植到 VC 需要的步骤

    原文:http://blog.csdn.net/leixiaohua1020/article/details/12747899 在VC下使用FFMPEG编译好的库,不仅仅是把.h,.lib,.dll拷 ...

  4. 12-27cell 的可重用性(英雄列表应用性能的优化)

    在英雄列表中动态生成cell的代码在中, - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N ...

  5. postgreSQL环境搭建

    一.安装 操作系统:windows7 安装介质:postgresql-9.1.3-1-windows.exe 二.psql控制台简单使用 1打开psql 2根据提示运行help 3列出表命令 三.安装 ...

  6. Objective-c 命名规则

    1.方法名:第一个单词的首字母用小写,后面的单词首字母要大写<骆驼规则>,例如doSomething . doSomethingElse 2.类名:第一个单词的首字母要求大写,后面的单词首 ...

  7. Swift URL Schemes使用

    URL Schemes通常用于分享和第三方登录,但有时需要在html跳至APP,或者APP跳至另外一个APP.这时也需要使用URL Schemes. 一.html跳转至APP eg:html跳转至te ...

  8. Oracle sysdate

    SYSDATE --可得到目前系统的时间 ex.       select sysdate from dual; sysdate       ----------       20-SEP-07    ...

  9. magento提速的一些小技巧,列举manegnto网站提速的

    下面列举一些可以 Magneot提速 的方法 本文系宇讯原创Magento教程,转载请注明出处. 1:使用CSS /图像精灵Magento提速. 一种图像精灵放入一个单一的图像,并通过特定的CSS类调 ...

  10. magento在产品详细页面添加分享链接的方法

    1,在产品详细页面的对用位置加入一下代码 <div class="sharethis_box">          <?php echo $this->ge ...