Spring的事务控制
编程式事务控制
通过写代码的方式,去控制事务,有三个api
PlatformTransactionManager-spring的事务管理器,提供了常用的操作事务的方法
- TransactionStatus
- getTransaction(TransactionDefination defination) 获取事务的状态信息
PlatformTransactionManager是接口类型,不同的Dao层技术则有不同的实现类,如jdbc或mybatis是和hibernate时的实现是不一样的
TransactionDefinition 事务的定义信息对象
内部封装事务的一些参数,方法如下:
- int getIsolationLevel() 获得事务的隔离级别
- int getPropogationBehavior() 获得事务的传播行为
- int getTimeout() 获得超时时间
- boolean isReadOnly() 是否只读
- 事务的隔离级别:
设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读(幻读)。
- READ-UNCOMMITTED:读未提交,不能解决
- READ-COMMITTED:能解决脏读
- REPEATABLE_READ:可重复读,能解决不可重复读
- SERIALIZABLE:串行化,全能解决,但性能低
- DEFAULT:默认
传播行为的主要作用:解决业务方法在调用业务方法时,事务同一性的问题;即在开发时,业务层有很多方法,会出现A业务方法调用B业务方法,若对AB两个业务方法都进行事务控制,则A调用B时会出现重复或统一的问题。
- REQUIRED(required):如果当前没有事务,就新建一个事务,如果已经存在一个事务,就加入到这个事务,是一般的选择(默认值);即当A业务方法调用B业务方法,B业务方法看A业务方法当前有没有事务,如果B业务方法看A业务方法没有事务,就新建一个事务,若A业务方法已经有事务,B业务方法就加入到这个事务
- SUPPORTS(supports): A业务方法调用B业务方法,B看A有没有事务,若有事务,则支持当前事务,若当前没事务,就以非事务方式执行。
- MANDATORY(mandatory):A业务方法调用B业务方法,若当前没有业务,就抛出异常
- REQUIRED_NEW
- NOT_SUPPORTED
- NEVER
- NESTED
- 超时时间:超时限制
- 是否只读
事务定义信息对象,若使用注解方式,需要注解配置
TransactionStatus
TransactionStatus接口提供的是事务具体的运行状态
- boolean hasSavePoint() 是否存储回滚点
- boolean isCompleted() 事务是否完成
- boolean isNewTransacction() 是否是新事务
- boolean isRollbackOnly() 事务是否回滚
这些状态信息不需要配置进行维护,状态随着事务的运行,是个被动信息,不是事务主动控制的。
事务的状态对象和前两者的关系:
平台事务管理器指定事务控制行为,事务的定义对象时维护事务的信息,事务运行状态是封装一些状态信息,是被动的。
平台事务管理器+事务定义对象=事务状态对象
基于XML的声明式事务控制
什么是声明式事务控制
即用声明的方式来处理事务;声明,指在配置文件中声明,用在Spring配置文件中声明式的处理事务来代替代码式的处理事务
声明式事务处理的作用
- 不侵入开发的组件,即解耦性;业务逻辑对象不会意识到处于事务管理之中,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,即AOP思想--切点:事务 被增强的是业务方法 通知:事务增强
- 在不需要事务管理的时候,只需要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译
Spring声明式事务控制底层就是AOP
业务背景
- domain:保存Account表结构
- dao:实现out函数和in函数接口和实现,实现控制money
- service:实现转账服务,控制转入人和转出人和转账金额
- web层controller:实现转账功能
一般在业务层(service)进行事务控制,但当前存在问题:
public void transfer(String outMan, String inMan, double money) { accountDao.out(outMan,money); accountDao.in(inMan,money); }
当前service对transfer方法进行事务控制,当调用out方法时,调用jdbc.Template的update方法,是一个事务,再执行in方法时,还是jdbc模板调用update方法,是另一个事务,此时不能进行事务控制
若此时两者间出现错误,则执行out函数后,出现错误没有执行in函数,则数据库数据错误。即此时业务层transfer函数并没有实现业务控制。
此时就可以把业务层的transfer函数当成一个切面进行AOP处理。
声明式事务控制的实现
声明式事务控制明确事项:
- 谁是切点? --转账方法
- 谁是通知? --事务增强(事务控制)
- 谁是切面?
快速入门
xml配置文件中引用tx命名空间,操作和之前引入context的方法一样
确定目标对象,如service,内部的方法就是切点
配置事务通知
配置文件设定:
- 首先引用事务tx标签 然后设定advice id,和之前bean id性质一样,然后设置transaction-manager,设定事务管理器,如jdbc或mybatis使用的事务管理器(DataSourceTransactionManager)与hibernate时不一样,这个事务管理器可以自己设定
- 设定好之后,内部需要注入DataSource至此平台事务管理器配置完成
- 然后将配制好的平台事务管理器配置到事务增强里
- 然后配置方法属性。
- 然后配置事务的aop织入,其中aop专门为事务增强有配置,advisor;然后配置切点和切点表达式
- 然后测试
```
tx:attributes-事务的属性,即编程式里TransactionDefination里的内容,里面设定事务的属性信息
tx:method-哪些方法被增强 isolation-管理级别 propagation-传播行为 time-out-失效时间 read-only-是否只读
attributes内可设置多个方法的配置
- 号的使用:如method name设定成update*,即update开头的所有方法都进行配置
xml声明式事务控制的配置要点
- 平台事务管理器配置
- 事务通知的配置
- 事务aop织入的配置
同时和aop一样,可以抽取point切点表达式
基于注解的声明式事务控制
一般情况下,自定义的bean用注解,非自定义的bean用xml配置
首先把Dao和Service用注解方式配置,具体操作见前-设置注解,设置组件自动扫描
然后替换掉事务增强信息和aop织入相关,找到需要对应事务控制的方法,可以通过@Transactional注解根据不同方法进行单独配置
@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED) public void transfer(String outMan, String inMan, double money) { //开启事务 accountDao.out(outMan,money); accountDao.in(inMan,money); //提交事务 } @Transactional(isolation = Isolation.DEFAULT) public void xxx(){ }
还可以在类上进行定义,代表当前类上所有方法都使用此事务配置,若多个配置,就近原则,离函数近的地方起作用
如果配置了@Transactional,需要在xml文件中配置注解驱动
注解配置声明式事务控制解析
- 使用@Transactional在需要进行事务控制的类或方法上修饰,注释可用的属性同xml配置方式,例如隔离级别,传播行为等
- 注解使用在类上,那么该类下的所有方法都同一套注解参数配置
- 使用在方法上,不同的方法可以采用不同的事务参数配置
- Xml配置文件中需要进行配置注解驱动
注解声明式事务控制的配置要点
- 平台事务管理器配置在xml中配置
- 事务通知的配置使用@Transactional注解配置
- 事务注解驱动的配置 tx:annotation-driven
ctrl+d复制这一行到下一行 ctrl+y删除当前行