您的位置:首页 > 其它

事务的传播特性 和隔离级别

2018-01-24 17:31 239 查看

转自:http://blog.csdn.net/loadhai/article/details/17800537

事物传播行为介绍: 

@Transactional(propagation=Propagation.REQUIRED) 

如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)

@Transactional(propagation=Propagation.NOT_SUPPORTED) 

容器不为这个方法开启事务

@Transactional(propagation=Propagation.REQUIRES_NEW) 

不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务

@Transactional(propagation=Propagation.MANDATORY) 

必须在一个已有的事务中执行,否则抛出异常

@Transactional(propagation=Propagation.NEVER) 

必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)

@Transactional(propagation=Propagation.SUPPORTS) 

如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.

事物超时设置:
@Transactional(timeout=30) //默认是30秒

事务隔离级别:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)

读取未提交数据(会出现脏读, 不可重复读) 基本不使用

@Transactional(isolation = Isolation.READ_COMMITTED)

读取已提交数据(会出现不可重复读和幻读)

@Transactional(isolation = Isolation.REPEATABLE_READ)

可重复读(会出现幻读)

@Transactional(isolation = Isolation.SERIALIZABLE)

串行化

MYSQL: 默认为REPEATABLE_READ级别

SQLSERVER: 默认为READ_COMMITTED

事务

原子性、一致性、隔离性、持久性

定义事务管理器

常见的事务管理器有JDBC事务、HIBERNATE事务、JTA事务、选择使用事务管理器可以根据项目的需要。

事务隔离

  1、ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别,另外四个与JDBC的隔离级别相对应。

  2、 ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。

      这种隔离级别会产生脏读,不可重复读和幻像读。

  3、 ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据

  4、ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。

  5、ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。

事务传播特性

 事务的传播特性通俗的讲,事务的是会传播的。传播的属性包括是否使用事务、事务是否可以传播、是否可以嵌套事务等。当调用Service层的一个方法的时候,事务保证方法中所有有关数据库更新操作在一个原子中。在事务层里面调用的这些方法要么全部成功,要么全部失败。那么事务的传播特性也是从这里说起的。

   如果你在你的Service层的这个方法中,除了调用了Dao层的方法之外,还调用了本类的其他的Service方法是解决这个问题的,此时就需要考虑事务的传播特性。“事务是会传播的”在Spring中有针对传播特性的多种配置我们大多数情况下只用其中的一种:

REQUIRED
Required属性告诉容器某个特定的方法需要一个事务,如果上下文中已经存在事务,则加入;否则,开启一个事务。这是一种使用最频繁的事务属性,适用于大多数情况;但是,并不绝对,下面我们将会看到对于某些场景。


MANDATORY(强制)

Mandatory属性告诉容器某个特定的方法需要一个事务。但是,不同于Required属性,它无论如何都不会开启新的事务;相反的,它会 “强制”要求该方法被调用时上下文中必须存在事务,否则会抛出TransactionRequiredException异常,提示需要一个事务但没有找 到。


REQUIRESNEW(创建)

RequiresNew属性告诉容器某个特定的方法需要一个新事务的支持。如果上下文中已经存在事务A,则该事务A挂起,并启动一个新的事务B。 当事务B结束后,事务A被唤醒并继续执行。事实上,使用RequiresNew违反了事务的ACID原则,因为新事务会导致原有事务的挂起。该属性在某个行为必须被完成(提交或回滚)而不受外部事务结果影响时十分有用,例如记录日志。大多数交易系统的每一个操作都必须写进日志,无论其 执行结果如何(成功或失败)。假设某个股票交易的方法placement()启动了一个事务,并且调用了一个通用的方法audit()来记录日志。由于
audit()和placement()处于同一个事务的管辖范围之内,因此一旦placement()回滚,audit()记录的日志也会相应的进行回 滚;这就违背了“任何成功或失败的操作都必须记录日志”这一业务逻辑。这个时候,如果将audit()的事务属性设作RequiresNew,则确保了 audit()在一个新的、单独的事务中记录日志,因此不受placement()中外部事务的影响。


SUPPORTS()

Supports属性告诉容器,该方法不需要事务支持,但如果当前上下文中已经存在了一个事务,则加入其中。Supports这是一个相当强大、 相当有用的事务属性。

NotSupported(事务排斥)
NotSupported属性告诉容器,该方法不需要事务支持;如果当前上下文中已经存在事务,则该事务被挂起直到该方法执行完毕。如果当前上下 文中不存在事务,该方法则在没有事务支持的环境下执行。NotSupported适用于“某些方法在事务控制下有较大可能性会产生异常”的场合。例如在 XA架构的事务处理过程中,调用包含DDL语句的存储过程往往会抛出异常,因此比较好的做法是将其设为NotSupported,暂时挂起当前事务。


Never(事务绝缘)

Never属性告诉容器,该方法必须在无事务的上下文中运行。注意,这与NotSupported不同,后者意味着,如果上下文中存在事务,则将 该事务暂时挂起并在无事务的上下文中运行。但Never却不同,如果当前上下文中已经存在事务,则在调用该方法时会抛出一个异常,提示该方法不能在事务环 境下运行。因为使用Never会导致各种意料之外的运行时异常,因此除非必要,一般不推荐使用


Required vs. Mandatory

Required和Mandatory是两个往往容易被混淆的事务属性。两者都可以运行在事务上下文环境中,但当上下文中不存在事务时,Required会自己开启一个事务,而Mandatory则不会。大多数情况下,你可以采用如下的“最佳实践”来区分何时需要哪种策略:如果某一方 法需要运行在具备事务的环境,但其本身又不负责事务的控制(回滚),则该方法应该标志为Mandatory。可以看出,上述最佳实践是基于事务的所有权而得来的。


RequiresNew vs. Required

另外两个比较容易的事务属性是Spring提供的Nested与RequiresNew。如果当前上下文中存在事务,两者都是启动一个新的事务; 如果当前上下文中不存在事务,则类似于PROPAGATION_REQUIRED,两者也都新建一个事务。那么,它们有什么区别呢?

PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等。当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行。另一方面, PROPAGATION_NESTED 开始一个 "嵌套的" 事务,  它是已经存在事务的一个真正的子事务。嵌套事务开始执行时,  它将取得一个 savepoint。如果这个嵌套事务失败, 我们将回滚到此
savepoint。嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。

总结一下,使用嵌套事务的场景总共有两个,如下图所示:

  需要事务BC与事务AD一起提交,即:作为事务AD的子事务,事务BC只有在事务AD成功提交时(阶段3成功)才提交。这个需求简单称之为“联合提交”。这一点PROPAGATION_REQUIRED可以做到。

    需要事务BC的回滚不会影响事务AD的提交。这个需求简单称之为“隔离回滚”。这一点,可以通过设置事务属性为PROPAGATION_REQUIRES_NEW做到。

   使用PROPAGATION_REQUIRED满足需求1,但子事务BC的rollback会迫使父事务AD也回滚,不能满足需求2。使用PROPAGATION_REQUIRES_NEW满足需求2,但子事务(严格意义上说,这时不应该称之为子事务)BC是一个全新的事务,父事务(严格意 义上说,这时也不应该称之为父事务)AD的成功与否完全不影响BC的提交,不能满足需求1。

同时满足上述两条需求就要用到PROPAGATION_NESTED了。PROPAGATION_NESTED在事务AD执行到B点时,设置了 savePoint。当BC事务成功提交时,PROPAGATION_NESTED的行为与PROPAGATION_REQUIRED一样。只有当事务 AD在D点成功提交时,事务BC才真正提交; 如果阶段3执行异常,导致事务AD 回滚,事务BC也将一起回滚,从而满足了“联合提交”。 当阶段2执行异常,导致BC事务rollback时,因为设置了savePoint的缘故,AD事务可以选择与BC一起rollback或继续阶段3的执
行并保留阶段1的执行结果,从而满足了“隔离回滚”。例如,可以把执行BC事务的方法try-catch起来,在catch中选择是否继续向上抛出异常 (是,则回滚到A处;否,则回滚到B处)。而是用PROPAGATION_REQUIRED时,即使try-catch住BC的异常,AD事务也一定会被 无条件rollback。

XML事务配置

   spring完成事务管理的根本目的就是让开发更加的专注于的业务逻辑。而不用去关心事务的问题。spring有两种事务实现方式,一种是摒弃的声明事务,一种是配置事务。(默认情况下当发生RuntimeException的情况下,事务才会回滚,所以要注意一下如果你在程序发生错误的情况下,有自己的异常处理机制定义自己的Exception,必须从RuntimeException类继承这样事务才会回滚!)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

<!-- 加载配置文件 -->
<context:property-placeholder location="config.properties"/>

<!-- 指定spring注解 -->
<context:component-scan base-package="com.hskj"/>

<!-- 连接池管理 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${db.driverClass}"/>
<property name="jdbcUrl" value="${db.jdbcUrl}"/>
<property name="user" value="${db.user}"/>
<property name="password" value="${db.password}"/>
<property name="initialPoolSize" value="${db.initialPoolSize}"/>

<!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime" value="${db.maxIdleTime}"/>
<!--连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize" value="${db.maxPoolSize}"/>
<property name="minPoolSize" value="${db.minPoolSize}"/>

<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
<property name="acquireIncrement" value="${db.acquireIncrement}"/>
<!--两次连接中间隔时间,单位毫秒。Default: 1000 -->
<property name="acquireRetryDelay" value="${db.acquireRetryDelay}"/>
<!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
<property name="acquireRetryAttempts" value="${db.acquireRetryAttempts}"/>
<!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试
获取连接失败后该数据源将申明已断开并永久关闭。Default: false -->
<property name="breakAfterAcquireFailure" value="${db.breakAfterAcquireFailure}"/>
</bean>

<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<!-- 声明事务 -->
<tx:advice id="userTxAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="delete*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" no-rollback-for="java.lang.RuntimeException" />
<tx:method name="insert*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.RuntimeException" />
<tx:method name="update*" propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception" />
<tx:method name="find*" propagation="SUPPORTS" />
<tx:method name="get*" propagation="SUPPORTS" />
<tx:method name="select*" propagation="SUPPORTS" />
</tx:attributes>
</tx:advice>

<!-- 织如事务(到业务层切面)(到此spring事务管理结束了) -->
<aop:config>
<aop:pointcut id="servicePointCut" expression="execution(public * com.liyb.test.spring.tracsaction.service.*.*(..))"/>
</aop:config>

<!-- mybatis集成 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="MyBatis-Configuration.xml"/>
</bean>

<bean class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.hskj.mapper.UserMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: