配置基于注解的声明式事务:

...配置tx,aop的命名空间
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
...

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd

...

<!-- transaction support-->
<!-- PlatformTransactionMnager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- enable transaction annotation support -->
<tx:annotation-driven transaction-manager="txManager" />

其中TransactionDefinition接口定义以下特性:

事务隔离级别

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

  • TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别(读已提交)。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
  • TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

事务传播行为

所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在(一个事务方法里,调用另一个事务方法,牵扯到的传播行为)

在TransactionDefinition定义了如下传播行为常量:

  • TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。就像是一个事务方法里调用了含not-supported的方法,或者反过来,都不支持事务
  • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行(在前一个事务的基础上,不能算真正的独立事务);如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。感觉很像required,不过required是一个大事务,而nested是内嵌事务(基于save point),内嵌的事务成功后不会立即提交,等外层事务提交之后才会提交,外层可以对内层事物做try-catch,决定是否提交,还是整体回滚,只有外层事物提交了,内层才跟着提交。

tip:将事务挂起,意味着本条数据所在排他锁不会释放,只有requires_new,算是真正独立的内层事务,其他都是加入事务

只有requires_new和not supported将当前事务挂起(容易死锁)

REQUIRES_NEW和PROPAGATION_NESTED的区别:

PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全 commited 或 rolled back (不依赖于外部事务), 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.

PROPAGATION_NESTED 开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务(依赖外部事务). 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint(子事务开始的地方). 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交.

最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit

事务超时

所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。

默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。

事务只读属性

只读事务用于客户代码只读但不修改数据的情形,只读事务用于特定情景下的优化,比如使用Hibernate的时候。
默认为读写事务。

@Transactional 使用:

@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。方法上注解属性会覆盖类注解上的相同属性

虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

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

//xxService里可以调用xxDao,因为xxService是一个类,xxDao又是一个类

                               @Transactional属性值

value String 可选的限定描述符,指定使用的事务管理器
propagation enum: Propagation 可选的事务传播行为设置
isolation enum: Isolation 可选的事务隔离级别设置
readOnly boolean 读写或只读事务,默认读写
timeout int (in seconds granularity) 事务超时时间设置
rollbackFor(类) Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组
rollbackForClassName(类名) 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组

@Transactional实战:

@Service
public class TxService { @Autowired
GoodsDao goodsDao; @Autowired
TxService2 txService2; @Transactional
public void x(){
MiaoshaGoods good = new MiaoshaGoods();
good.setGoodsId(1l);
goodsDao.reduceStock(good);
GoodsVo vo = goodsDao.getGoodsVoByGoodsId(1l);
System.out.println("当前库存x:"+vo.getStockCount());
txService2.y();// y方法事务有效
     //y(); 本类中,y方法事务失效,但是y方法加入了x中,作为了一个整体
throw new RuntimeException("brx!!!!!!!!!!!!");
  //这种情况,事务失效
  @Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED)
  public void y(){
MiaoshaGoods good = new MiaoshaGoods();
good.setGoodsId(1L);
goodsDao.reduceStock(good);
GoodsVo vo = goodsDao.getGoodsVoByGoodsId(1L);
System.out.println("当前库存y:"+vo.getStockCount()); }
}

@Service
public class TxService2 { @Autowired
GoodsDao goodsDao; //@Transactional(propagation = Propagation.REQUIRES_NEW) 设置tx传播级别为requires_new,会造成CannotAcquireLockException(死锁)
//因为dml操作会抢占排它锁,对同一条数据在外层事务进行dml,那么内层事务会将外层事务挂起,去请求该行锁,直到超时,,但是nest,required不会
  @Transactional
public void y(){
MiaoshaGoods good = new MiaoshaGoods();
good.setGoodsId(1L);
goodsDao.reduceStock(good);
GoodsVo vo = goodsDao.getGoodsVoByGoodsId(1L); // 如果修改的是2L,那么Propagation.REQUIRES_NEW不会死锁
  System.out.println("当前库存y:"+vo.getStockCount()); } }

如果TxService1的x方法加@Trxxx注解,那么调用自身的y方法,不管y上有没有@Trxxx,当外界调用x方法时,x包含y做统一事务处理

如果TxService1的x方法不加@Trxxx注解,自身的y方法加注解,那么当外界调用x方法时,y事务失效,但是,当外界单独去掉y方法时,事务有效
如果TxService1的x方法不加@Trxxx注解,TxService2的y方法加@Trxxx注解,那么当外界调用x方法时,y事务有效,但是x事务无效
如果TxService1的x方法加@Trxxx注解,传播级别为required,TxService2的y方法加@Trxxx注解,传播级别为required,那么会作为一个整体进行回滚,提交
如果TxService1的x方法加@Trxxx注解,传播级别为required,TxService2的y方法加@Trxxx注解,传播级别为requires_new,那么内层事务是独立的,
外层(抛异常)影响不了内层,但是内层可以影响外层(内层抛异常,会导致整体事务回滚),但是,如果外层对内层的异常做捕获,那么外层可以提交事务,内层回滚 如果TxService1的x方法加@Trxxx注解,传播级别为required,TxService2的y方法加@Trxxx注解,传播级别为nested,那么外层会影响内层,内层
可以影响外层(内层抛异常,会导致整体事务回滚),但是,如果外层对内层的异常做捕获,那么外层可以提交事务,内层回滚;
(跟required不同的是,required只要抛出异常,不管是外层抛出还是内层抛出,整体回滚,捕获无效)
严格级别:required > nested > requires_new

补充(dml操作会抢占排它锁):

通过DML语句对一张表的某一行数据进行修改,一个事务开始,背后的步骤是:
1.对这张表(表结构)加一个共享锁。防止其他事务DDL语句(数据定义语言)修改这张表的表结构。注意:读锁和写锁是互斥的,但是一个是针对表结构没,一个是针对行数据
2.对修改的那一行加一个排他锁,别的会话不能修改这一行。但是我对整张表加的是共享锁而不是排他锁,所以别的会话还是可以修改其他行(也经历1、2两个步骤)

当锁的持有时间较长并且大部分操作都不会修改被守护的资源时,读-写锁可以提高并发性。

@Transactional注解使用心得的更多相关文章

  1. 数据库事务中的隔离级别和锁+spring Transactional注解

    数据库事务中的隔离级别和锁 数据库事务在后端开发中占非常重要的地位,如何确保数据读取的正确性.安全性也是我们需要研究的问题.ACID首先总结一下数据库事务正确执行的四个要素(ACID): 原子性(At ...

  2. Spring声明式事务管理基于@Transactional注解

    概述:我们已知道Spring声明式事务管理有两种常用的方式,一种是基于tx/aop命名空间的xml配置文件,另一种则是基于@Transactional 注解.         第一种方式我已在上文为大 ...

  3. spring事物配置,声明式事务管理和基于@Transactional注解的使用

    http://blog.csdn.net/bao19901210/article/details/41724355 http://www.cnblogs.com/leiOOlei/p/3725911. ...

  4. Spring中的事物管理,用 @Transactional 注解声明式地管理事务

    事物: 事务管理是企业级应用程序开发中必不可少的技术,  用来确保数据的 完整性和 一致性. 事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用 事务的四 ...

  5. Java事务处理全解析(七)—— 像Spring一样使用Transactional注解(Annotation)

    在本系列的上一篇文章中,我们讲到了使用动态代理的方式完成事务处理,这种方式将service层的所有public方法都加入到事务中,这显然不是我们需要的,需要代理的只是那些需要操作数据库的方法.在本篇中 ...

  6. spring,mybatis事务管理配置与@Transactional注解使用[转]

    spring,mybatis事务管理配置与@Transactional注解使用[转] spring,mybatis事务管理配置与@Transactional注解使用 概述事务管理对于企业应用来说是至关 ...

  7. @Transactional注解*

    类或者方法加@Transactional注解 表示该类里面的所有方法或者这个方法的事务由spring处理,来保证事务的原子性,不知道这样说你能不能理解,即是方法里面对数据库操作,如果有一个方法操作失败 ...

  8. @Transactional 注解说明

    先让我们看代码吧! 以下代码为在"Spring3事务管理--基于tx/aop命名空间的配置"基础上修改.首先修改applicationContext.xml如下: <pre ...

  9. spring的@Transactional注解详细用法

    概述 事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性.Spring Framework对事务管理提供了一致的抽象,其特点如下: 为不同的事务API提供一致的编程模型, ...

随机推荐

  1. Charles界面介绍及使用方法

    本随笔主要内容: 一.Charles界面介绍 二.Charles使用 1.会话(Repeat.Focus.Compare.黑白名单等) 2.模拟请求做mock,使用断点.Map或Rewrite 3.指 ...

  2. Numpy:ndarray数据类型和运算

    Numpy的ndarray:一种多维数组对象 N维数组对象,该对象是一个快速而灵活的大数据集容器,nadarry是一个通用的同构数据多维容器,也就是说,其中的所有元素必须是相同类型的.每个数组都有一个 ...

  3. Win7下VB6.0不能加载mscomctl.ocx的解决办法

    下载这个:http://pan.baidu.com/s/1sjJgrbJ 然后在命令框下注册这个组件: regsvr32 mscomctl.ocx 即可

  4. Django项目之客户

    关于客户的操作 主页(被继承) {% load static %} <!DOCTYPE html> <html lang="en"> <head> ...

  5. 【转】chrome devtools protocol——Web 性能自动化

    前言 在测试Web页面加载时间时,可能会是这样的: 打开chrome浏览器. 按F12打开开发者工具. 在浏览器上打开要测试的页面 查看开发者工具中Network面板的页面性能数据并记录 或者在开发者 ...

  6. 微信小程序template模板与component组件的区别和使用

    前言: 除了component,微信小程序中还有另一种组件化你的方式template模板,这两者之间的区别是,template主要是展示,方法则需要在调用的页面中定义.而component组件则有自己 ...

  7. Linux系统(Centos)下安装nodejs并配置环境

    总结Centos下安装nodejs并配置环境,记录成功安装的方法.推荐的安装方法是利用已编译的二进制文件安装,不推荐使用源码的形式安装,一是源码安装比较麻烦,二是需要自行下载编译浪费时间. 1.安装n ...

  8. 问题1:jquery实现全选功能,第二次失效(已解决)

    问题:使用了attr("checked",true”)设置子复选框的被选状态,第一次执行功能正常,但第二次失效. 解决方案:将attr("checked",tr ...

  9. dUMP:A new value is to be assigned to the field "<L_BOX>"

    DUMP: A new value is to be assigned to the field "<L_BOX>", although this field is e ...

  10. shell脚本小集锦

    1) 如何向脚本传递参数 ? ./script argument 例子: 显示文件名称脚本 ./show.sh file1.txt cat show.sh #!/bin/bash 2) 如何在脚本中使 ...