现在的位置: 首页 > 综合 > 正文

Spring事务管理

2018年02月05日 ⁄ 综合 ⁄ 共 5016字 ⁄ 字号 评论关闭

     学了一周的Spring事务管理,特整理出来,以便以后翻阅。

   概念:事务是一组原子性操作的工作单元,这组工作单元要么执行成功,要么不成功 

事务四大特征:原子性,一致性,隔离性和持久性。

    1. 原子性  一个原子事务要么完整执行,要么干脆不执行。 这意味着,工作单元中的每项任务都必须正确执行。如果有任一任务执行失败,则整个工作单元或事务就会被终止。即此前对数据所作的任何修改都将被撤销。如果所有任务都被成功执行,事务就会被提交,即对数据所作的修改将会是永久性的。

    2. 一致性 一致性代表了底层数据存储的完整性。 它必须由事务系统和应用开发人员共同来保证。事务系统通过保证事务的原子性,隔离性和持久性来满足这一要求; 应用开发人员则需要保证数据库有适当的约束(主键,引用完整性等),并且工作单元中所实现的业务逻辑不会导致数据的不一致(即,数据预期所表达的现实业务情况不相一致)。例如,在一次转账过程中,从某一账户中扣除的金额必须与另一账户中存入的金额相等。

    3. 隔离性  隔离性意味着事务必须在不干扰其他进程或事务的前提下独立执行。 换言之,在事务或工作单元执行完毕之前,其所访问的数据不能受系统其他部分的影响。

    4. 持久性  持久性表示在某个事务的执行过程中,对数据所作的所有改动都必须在事务成功结束前保存至某种物理存储设备。 这样可以保证,所作的修改在任何系统瘫痪时不至于丢失。

      事务分编程式事务和声明式事务

spring出来之前一般都是编程式事务,如果要用声明式事务,必须符合j2ee的规范,比如weblogic。有了spring,我们可以很优雅的用声明式事务。

编程式事务:手动处理事务                                 缺点:每一个业务方法都包含类似启动、 提交、回滚事务的样板代码

ž声明式事务:配置文件中声明式的处理事务       优点:侵入性低,通用性强

 

žSpring同时提供了Annotation和XML配置方式的声明式事务管理

Annotation可以节省很多配置,但是当需要配置一些共有的配置的时候,像spring 声明式事务 xml会更好

选择以哪种方式声明事务可以参考以下几点建议:

  • 如果service层的方法绝大部分都是数据库操作,我们可以使用XML风格在service层声明事务边界。
  • 如果service层的方法除了数据库操作外,还有很多耗时的业务方法(例如XML风格一节的例子),那么就可以采用Annotation风格。
  • 也可以将XML风格和Annotation风格结合起来一起使用。这是我推荐使用的,只使用两种风格的优点,而避免了它们的缺点。

个人感悟:关于选择annotation或者xml,应根据具体情况而定,但目前注解的方式越来越流行,而且我相信注解会越来越完善,虽然不能完全取代xml,但注解占95以上,xml占不到5%,因为注解真的很方便,少了很多配置,只需要@transaction就可以搞定

 

ž<!-- JBoss JTA transactionmanager -->
ž       <!-- START -->
ž       <beanid="jbossTransactionManager"
ž        class="com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple">
ž       </bean>
ž
ž       <beanid="jbossUserTransaction"
ž              class="com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple"/>
ž
ž       <beanid="transactionManager"
ž             class="org.springframework.transaction.jta.JtaTransactionManager">
ž              <propertyname="transactionManager">
ž                     <refbean="jbossTransactionManager" />
ž              </property>
ž              <propertyname="userTransaction">
ž                     <refbean="jbossUserTransaction" />
ž              </property>
ž       </bean>
ž<tx:annotation-driventransaction-manager=“transactionManager”>
 
ž<session-factory>中需定义packagesToScan ,annotatedPackages(早期)
ž<property name="packagesToScan ">
ž<list>  <value>com.service</value></list> </property

 

ž上面用到jboss的jta事务管理

jta和jdbc的事务管理区别:(网上其他人的总结,比较清晰)

JDBC事务由Connnection管理,也就是说,事务管理实际上是在JDBCConnection中实现。事务周期限于Connection的生命周期之类。同样,对于基于JDBC Transaction的 Hibernate 事务管理机制而言,事务管理在 Session 所依托的 JDBC Connection中实现,事务周期限于Session的生命周期。

JTA 事务管理则由 JTA 容器实现,JTA 容器对当前加入事务的众多 Connection 进行调度,实现其事务性要求。JTA的事务周期可横跨多个JDBC Connection生命周期。同样对于基于JTA事务的Hibernate而言,JTA事务横跨可横跨多个Session。

 

我的理解:jdbc的事务由jdbc connection实现,也就是在一个connection中,事务局限于connection的声明周期,而jta事务由jta容器实现,容器来管理connection,这样就可以跨越多个jdbc事务,所以 强烈建议用jta的事务管理。

 

以上配置就可以使用在com.service包下用@Transactional标记类或者方法了。

 

可以使用@Transactional标记一个接口,接口中的方法,一个类的定义或者类中的公共方法。但是仅仅提供@Transactional标记是不够的,@Transactional标记只是一种能够被某种运行时架构使用的配置元数据,因此需要提供能够自动识别@Transactional标记的架构支持。上例中,使用<tx:annotation-driven/>元素开启了标记的事务行为。

 

Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,

你可以通过 <tx:annotation-driven/> 元素的"proxy-target-class" 属性值来控制是基于接口的还是基于类的代理被创建。如果 "proxy-target-class" 属值被设置为"true",那么基于类的代理将起作用(这时需要CGLIB库cglib.jar在CLASSPATH中)。如果 "proxy-target-class" 属值被设置为"false"或者这个属性被省略,那么标准的JDK基于接口的代理将起作用。 

@Transaction的属性

ž@Transactional(noRollbackFor=RuntimeException.class)    不回滚的异常类
@Transactional(RollbackFor=Exception.class)                          回滚的异常类,默认回滚RunTimeExcetion,如果要所有异常都回滚,必须加上RollbackFor=Exception.class
@Transactional(readOnly=true)                                                    是否只读,若设为true,那么这个事务对数据库只能读,而不能增,删,改,默认为false
@Transactional(timeout=100)                          超时(单位 秒)
@Transactional(isolation=Isolation.DEFAULT)            隔离级别

@Transactional(propagation=Propagation.REQUIRED)          传播机制

 

下面主要讲隔离级别和传播机制

 

隔离级别  先理解三个概念  脏读,不可重复读,幻读(幻想读)

 

脏读dirty reads:当事务读取还未被提交的数据时,就会发生这种事件。举例来说:Transaction1 修改了一行数据,然后 Transaction 2 在 Transaction 1 还未提交修改操作之前读取了被修改的行。如果 Transaction 1 回滚了修改操作,那么 Transaction2 读取的数据就可以看作是从未存在过的。
不可重复的读non-repeatable reads:当事务两次读取同一行数据,但每次得到的数据都不一样时,就会发生这种事件。举例来说:Transaction 1 读取一行数据,然后Transaction2 修改或删除该行并提交修改操作。当 Transaction 1 试图重新读取该行时,它就会得到不同的数据值(如果该行被更新)或发现该行不再存在(如果该行被删除)。
虚读phantom read:如果符合搜索条件的一行数据在后面的读取操作中出现,但该行数据却不属于最初的数据,就会发生这种事件。举例来说:Transaction 1 读取满足某种搜索条件的一些行,然后 Transaction2 插入了符合 Transaction 1 的搜索条件的一个新行。如果 Transaction 1 重新执行产生原来那些行的查询,就会得到不同的行。

 

1  @Transaction(ISOLATION.DEFAULT )默认的隔离级别
与底层数据库的隔离级别一致,假如使用oracle,那么就可以防止脏读,不可重复读,可能出现幻读
2 ISOLATION.READ_UNCOMMITTED
脏读,不可重复读和虚读有可能发生。
3 ISOLATION.READ_COMMITTED
指示可以发生脏读 (dirtyread),但可能发生不可重复读和虚读 (phantomread)。
4 ISOLATION.REPEATABLE_READ
指示防止发生脏读和不可重复读的常量;虚读有可能发生。
5 ISOLATION.SERIALIZABLE
指示防止发生脏读、不可重复读和虚读的常量。

  传播机制

REQUIRED:业务方法需要在一个容器里运行。如果方法运行时,已经处在一个事务中,那么加入到这个事务,否则自己新建一个新的事务。 
NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,该事务会被挂起,调用结束后,原先的事务会恢复执行。 
REQUIRES_NEW:不管是否存在事务,该方法总汇为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建,直到方法执行结束,新事务才算结束,原先事务才会恢复执行

MANDATORY:该方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果在没有事务的环境下被调用,容器抛出例外。 
SUPPORTS:该方法在某个事务范围内被调用,则方法成为该事务的一部分。如果方法在该事务范围外被调用,该方法就在没有事务的环境下执行。 
NEVER:该业务方法绝对不能在事务范围内执行。如果在就抛例外。只有该方法没有关联到任何事务,才正常执行。 

上面的事略在ejb中都会存在。只有下面的事务才是spring自己提供的:

NEST:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务 拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。

  

 

抱歉!评论已关闭.