Spring13:Spring中的事务属性

1.什么是事务属性

属性:描述物体特征的一些列值

事务属性:主要从5个维度来描述事务。

1.隔离属性
2.传播属性
3.只读属性
4.超时属性
5.异常属性

2.如何添加事务属性

@Transactional(isolation = , propagation = , readOnly = , timeout = , rollbackFor = , noRollbackFor = )

3.事务属性详解

3.1.隔离属性(isolation)

1.隔离属性的概念:它描述了事务解决并发问题的特征

  • 什么是并发:多个事务、用户,在同一时间段(0.00001s,很小的时间段),访问操作了相同的数据
  • 并发会产生哪些问题:
    1.脏读
    2.不可重复读
    3.幻影读
  • 并发问题如何解决?
    通过隔离属性解决,隔离属性中设置不同的值,解决并发处理过程中的问题。

2.事务并发产生的问题:

  • 2.1脏读:一个事物读取了另一个事务中还没有提交的数据 。这些数据在提交前可能会做进一步的修改或者回滚,所以当前事务中的读到的这个数据是脏数据。

  • 解决办法:读已提交的数据。
    @Transactional(isolation = Isolation.READ_COMMITTED)

  • 2.2不可重复度:在一个事务中,多次读取相同的记录但是每次读取的结果不一样。多次读取的间隔中,这些数据可能会被其他用户修改,并提交。这样在一个事务中每次读取的结果不一样,就产生了不可重复度的现象。会另当前用户产生困惑:到底该用哪次结果作为基准来进行后续操作。

  • 注意:不是脏读,因为虽然读取的数据不一样,但是都是已提交的正确数据;而且是在一个事务中,如果不在一个事务中,或者间隔时间很长,那么每次读取的结果不一样就是应该的。

  • 解决:@Transactional(isolation = Isolation.REPEATABLE_READ)。本质是数据库底层为读取的这行数据加上一把行锁,只有在当前用户对这个数据操作完之后,其他用户才能操作这行数据。

  • 2.3幻影读:一个事务中,多次对整表进行查询统计,但是发现结果不一样,会在本事务中产生数据不一致的问题。

  • 解决方案:@Transactional(isolation = Isolation.SERIALIZABLE),本质上是数据库底层为整表加上一个表锁。

  • 2.4隔离属性小结:

    并发安全:解决脏读的READ_COMMITTED,只能保证每次读取的都是已提交的数据。无法保证同一事务中多次读取同一个数据的一致性,所以并发安全性最差。安全性最高的加上了表锁的SERIALIZABLE,其次是加上了行锁的REPEATABLE_READ,最差的是READ_COMMITTED
    运行效率正好相反:READ_COMMITTED>REPEATABLE_READ>SERIALIZABLE

  • 2.5数据库对隔离属性的支持
    在这里插入图片描述
    Oracle不支持REPEATABLE_READ值如何解决不可重复读:采用的是多版本比对的方式 解决不可重复读的问题

  • 2.6默认的隔离属性

    Spring默认的隔离属性是:ISOLATION_DEFAULT。它会调用不同数据库所设置的默认隔离属性。

    MySQL的默认隔离属性 : REPEATABLE_READ
    Oracle的默认隔离属性: READ_COMMITTED

在这里插入图片描述

  • 2.7隔离属性在实战中的建议:

    推荐使用Spring指定的默认值ISOLATION_DEFAULT。
    未来实战中,并发访问情况很低。所以为了一个小概率的事情,而加各种各样的锁影响效率,这是没有意义的。

    如果真的遇到了并发问题,优先推荐的方案:乐观锁,它是应用锁,不是物理锁,不会过多的影响我们的效率。
    Hibernate(JPA):Version;MyBatis:通过拦截器自定义开发。

3.2事务的传播属性

1.传播属性的概念:它描述了解决事务嵌套问题的特征。

什么是事务的嵌套:指的是一个大的事务中,包含若干小的事务。比如Service层的相互调用。
导致的问题:破坏了最外层事务的原子性。

在这里插入图片描述

2.传播属性的值及其用法:@Transactional(propagation = )

中心思想:保证同一时间,只有一个事务。

在这里插入图片描述

  • 2.1REQUIRED:@Transactional(propagation = Propagation.REQUIRED)

    运用了REQUIRED后,如果当前业务方法的外层不包含外部事务,那么会开启新的事务;如果当前的业务方法的外层存在了一个事务,业务方法就会融合到外部事务中,不开启新的事务。

  • 一般为增删改方法加上REQUIRED这个传播属性

  • 2.2SUPPORTS:业务方法外部不存在事务,不开启事务;外部事务存在事务,融合到外部的事务。就是没有事务呗,这样都不开启呗。

  • 所以一般用在查询的方法:查询不需要事务。

  • 2.3默认的传播属性:

    一般而言:REQUIRED和SUPPORTS能满足我们99%的业务情况了。Spring提供的默认传播属性是REQUIRED

    在这里插入图片描述

  • 2.4传播属性推荐的使用方式:

    增删改方法:直接使用默认值REQUIRED
    查询方法:指定传播属性为SUPPORTS

  • 2.5REQUIRES_NEW:

    思考这样一个情况,如过一个save操作失败了发生回滚,那么这次save操作要不要被日志记录下来?

    显然,即使是一次失败的操作也要被记录下来。所以save操作被事务控制,save方法中要调用日志记录方法log,且log方法也被事务控制。如果log方法的传播属性是REQUIRED,那么log事务就不开启,而是融入到save事务中。那么当外层的控制save事务回滚的话,log方法也会回滚。没法记录下这次的save操作。

    所以log方法的事务要独立出来,而且又要保证同一时间只能有一个事务,所以先挂起外部的save事务,开启log的事务,等待log事务结束后,在运行save的事务!
    REQUIRES_NEW:如果没有外部事物,开启本身的事物;如果有外部事物,先挂起外部事务,运行本身的事务,待本身的事务结束后,再运行外部的事务。经常用在日志的记录中。

  • 2.6后面的3个事务传播属性极其不常用。

3.3事务的只读属性(readOnly)

  • 针对于只进行查询操作的业务方法,可以加入只读属性,提高运行效率。

  • 为了保证并发安全,会加上各种各样的锁;将并行执行的方式,变成串行的执行方式,影响效率;当我们确定这个方法是只进行查询操作的话,我们为其加上只读属性,那么就不会为其加上各种各样的锁了,提高运行效率。

在这里插入图片描述

  • 默认值是false:不是只读;所以如果确定一个方法只有查询操作,要显示的指定只读属性为true。

3.4.事务的超时时间属性

  • 指定了事务等待的最长时间。

  • 为什么事务会进行等待:当前事务访问数据时,有可能访问的数据被别的事务进行了加锁处理,那么此时本事务就必须进行等待。

  • 如何设置:@Transactional(timeout=2)。单位:秒,超过时间后,就会抛出异常。

  • 超时属性的默认值:-1。意为由对应的数据库来指定

3.5.事务的异常属性

1.Spring事务处理过程中:

  • 默认对于RuntimeException及其子类,采用的是回滚的策略
  • 默认对于Exception及其子类,采用的是提交的策略(检查异常会抛出来,但是更新操作会正常进行事务提交)。

2.怎么改变这种策略:

  • 想要让遇到检查时异常Exception及其子类,也进行回滚:@Transactional(rollbackFor = java.lang.Exception.class)
  • 让遇到运行时异常时,也能提交(不会滚):@Transactional(rnoRollbackFor = {java.lang.RuntimeException.class})
  • @Transactional(rollbackFor = {java.lang.Exception.class},noRollbackFor = {java.lang.RuntimeException.class})
  • 实战中一般使用Spring的默认事务异常属性。

4.事务属性常见配置总结

1. 隔离属性   默认值 
2. 传播属性   Required(默认值) 增删改;Supports 查询操作
3. 只读属性   readOnly false-->增删改;true-->查询操作
4. 超时属性   默认值 -1
5. 异常属性   默认值 

增删改操作   @Transactional
查询操作     @Transactional(propagation=Propagation.SUPPORTS,readOnly=true)

5.基于标签的事务配置方式(事务开发的第二种形式)

5.1基于@Transactional注解的事务开发回顾

<bean id="userService" class="com.baizhiedu.service.UserServiceImpl">
  <property name="userDAO" ref="userDAO"/>
</bean>

<!--DataSourceTransactionManager-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>

@Transactional(isolation=,propagation=,...)
public class UserServiceImpl implements UserService {
    private UserDAO userDAO;

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

5.2基于标签的事务配

<bean id="userService" class="com.baizhiedu.service.UserServiceImpl">
  <property name="userDAO" ref="userDAO"/>
</bean>

<!--DataSourceTransactionManager-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>

<!--事务属性 -->
<tx:advice id="txAdvice" transacation-manager="dataSourceTransactionManager">
    <tx:attributes>
          <tx:method name="register" isoloation="",propagation=""></tx:method>
          <tx:method name="login" .....></tx:method>
          <!--
          等效于 
          @Transactional(isolation=,propagation=,)
          public void register(){
        
          }
          --> 
    </tx:attributes>
</tx:advice>

<aop:config>
     <aop:pointcut id="pc" expression="execution(* com.baizhiedu.service.UserServiceImpl.register(..))"></aop:pointcut>
     <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
</aop:config>

5.3每个方法都要进行配置:太冗余

编程时候 service中负责进行增删改操作的方法 都以modify开头
                       查询操作 命名无所谓 
<tx:advice id="txAdvice" transacation-manager="dataSourceTransactionManager">
    <tx:attributes>
          <tx:method name="register"></tx:method>
          <tx:method name="modify*"></tx:method>
          <tx:method name="*" propagation="SUPPORTS"  read-only="true"></tx:method>
    </tx:attributes>
</tx:advice>

应用的过程中,service放置到service包中
<aop:config>
     <aop:pointcut id="pc" expression="execution(* com.baizhiedu.service..*.*(..))"></aop:pointcut>
     <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
</aop:config>

猜你喜欢

转载自blog.csdn.net/tttxxl/article/details/115517829