小言_互联网的博客

Spring 之 声明式事务控制

487人阅读  评论(0)

注解配置

(1)
不管是注解配置方式,还是 XML 配置方式,都需要在配置文件中配置事务管理器。

事务管理器的主要实现有:

  • DataSourceTransactionManager:在应用程序中只需要处理一个数据源,而且通过 JDBC 存取。
  • JtaTransactionManager:在 JavaEE 应用服务器上用 JTA(Java Transaction API) 进行事务管理。
  • HibernateTransactionManager:用 Hibernate 框架存取数据库。
<!-- 1. 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

(2)
在对应的方法上加上 @Transactional 注解。在 @Transactional 注解中还可以配置事务的属性,比如事务的传播行为、事务的隔离级别、是否只读、超时时间等。

  1. 事务的传播行为
    @Transactional 注解的 propagation 属性中定义。
    事务的传播行为指的是:当一个事务方法被另一个事务方法调用的时候,是用另一个方法的事务,还是将另一个方法的事务挂起,自己新开一个事务呢?

    Spring 定义了 7 种传播行为:

    最常用的是 REQUIREDREQUIRED_NEW,默认的传播行为时 REQUIRED
    ① REQUIRED
    当一个事务方法被另一个事务方法调用的时候,这种情况是使用另一个方法的事务。
    ② REQUIRED_NEW
    这种情况是将另一个方法的事务挂起,而开启一个新事务,并在自己的事务内运行。
    这两种情况具体的区别就是:比如我们在双十一的时候要抢购东西,我们事先将这些商品加入到购物车中,但是在 0 点支付的时候,银行卡中的钱不够了,这时候事务的传播行为如果是 REQUIRED,则一件商品也买不到,而如果是 REQUIRED_NEW 的话,则会买到一部分商品。

  2. 事务的隔离级别
    @Transactional 注解的 isolation 属性中定义。
    MySQL 中有 4 中隔离级别,分别是:READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE。

  3. 哪些异常回滚,哪些异常不回滚
    rollbackFor 属性中指定遇到时必须进行回滚的异常类型,可以为多个;
    noRollbackFor 属性中指定遇到时不回滚的异常类型,可以为多个。
    默认情况下捕获到 RuntimeException 或 Error 时回滚,而捕获到编译时异常不回滚。

  4. 事务是否只读
    如果一个事物只读取数据但不做修改,数据库引擎可以对这个事务进行优化。
    所以我们可以在 readOnly 属性中配置该事务是否是只读事务,从而帮助数据库引擎优化事务。

  5. 事务的超时时长
    如果一个事务长时间占用资源,则会使整体的性能下降。
    我们可以配置 timeout 的属性,用来指明当一个事务的运行时长超过我们配置的超时时长就会强制回滚,超时时长以秒为单位。

下面来看一个示例:

/**
  * 添加事务注解
  * 1.使用 propagation 指定事务的传播行为, 即当前的事务方法被另外一个事务方法调用时,如何使用事务, 默认取值为 REQUIRED, 即使用调用方法的事务
      REQUIRES_NEW: 事务自己的事务, 调用的事务方法的事务被挂起. 
  * 2.使用 isolation 指定事务的隔离级别, 最常用的取值为 READ_COMMITTED
  * 3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚. 也可以通过对应的属性进行设置. 通常情况下取默认值即可. 
  * 4.使用 readOnly 指定事务是否为只读. 表示这个事务只读取数据但不更新数据, 这样可以帮助数据库引擎优化事务. 若真的是一个只读取数据库值的方法, 应设置 readOnly=true
  * 5.使用 timeout 指定强制回滚之前事务可以占用的时间.  
  */
@Transactional(propagation=Propagation.REQUIRES_NEW,
                            isolation=Isolation.READ_COMMITTED,
                            noRollbackFor={UserAccountException.class},
                            readOnly=false,
                            timeout=3)
@Override
public void purchase(String username, String isbn) {
    //1. 获取书的单价
    int price = bookShopDao.findBookPriceByIsbn(isbn);

    //2. 更新数的库存
    bookShopDao.updateBookStock(isbn);

    //3. 更新用户余额
    bookShopDao.updateUserAccount(username, price);
}

(3)
配置了 @Transactional 注解之后,如何让该注解起作用呢?
我们还需要在配置文件中开启事务注解支持:

<!-- 启用事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

前提是在配置文件中加入 tx 命名空间。

XML 配置

当然我们也可以采用 XML配置的方式,也需要 3 个步骤:
(1)配置事务管理器

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

(2)配置事务的属性

<!-- 2. 配置事务属性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- 根据方法名指定事务的属性,如果没有配置属性则表示采用默认的属性 -->
        <tx:method name="purchase" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" no-rollback-for="java.lang.ArithmeticException,java.lang.NullPointerException" read-only="false" timeout="5"/>
        <tx:method name="get*" read-only="true"/>
        <tx:method name="find*" read-only="true"/>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

(3)配置切入点表达式,表示事务应用在哪些方法上,并将切入点表达式和事务属性关联起来

<!-- 3. 配置事务切入点,,以及把事务切入点和事务属性关联起来 -->
<aop:config>
    <aop:pointcut id="txPointCut" expression="execution(* com.spring.tx.xml.service.*.*(..))" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>	
</aop:config>

转载:https://blog.csdn.net/zwj_jyzl/article/details/106960719
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场