事务@Transactional注解的属性

事务的传播行为

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为。

事务传播属性可以在@Transactional注解的propagation属性中定义。

1) . 说明
  ①REQUIRED传播行为
当bookService的purchase()方法被另一个事务方法checkout()调用时,它默认会在现有的事务内运行。这个默认的传播行为就是REQUIRED。因此在checkout()方法的开始和终止边界内只有一个事务。这个事务只在checkout()方法结束的时候被提交,结果用户一本书都买不了。
 

②. REQUIRES_NEW传播行为
表示该方法必须启动一个新事务,并在自己的事务内运行。如果有事务在运行,就应该先挂起它。

事务的隔离级别

1、数据库事务并发问题
   假设现在有两个事务:Transaction01和Transaction02并发执行。
  1) 脏读(针对一条数据中的一个字段)
     ①Transaction01将某条记录的AGE值从20修改为30。
     ②Transaction02读取了Transaction01更新后的值:30。
     ③Transaction01回滚,AGE值恢复到了20。
     ④Transaction02读取到的30就是一个无效的值。
  2) 不可重复读(针对一条数据中的一个字段)
     ①Transaction01读取了AGE值为20。
     ②Transaction02将AGE值修改为30。
     ③Transaction01再次读取AGE值为30,和第一次读取不一致。
  3) 幻读(针对表中的一行数据而言)
     ①Transaction01读取了STUDENT表中的一部分数据。
     ②Transaction02向STUDENT表中插入了新的行。
     ③Transaction01读取了STUDENT表时,多出了一些行。
2、 隔离级别
数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。
  1) 读未提交:READ UNCOMMITTED
    允许Transaction01读取Transaction02未提交的修改。
  2) 读已提交:READ COMMITTED
     要求Transaction01只能读取Transaction02已提交的修改。
  3) 可重复读:REPEATABLE READ
       确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
  4) 串行化:SERIALIZABLE
      确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。(单线程,在一个请求中,其他线程读都不可以)

用@Transactional注解声明式地管理事务时可以在@Transactional的isolation属性中设置隔离级别。

触发事务回滚的异常
1、默认情况
捕获到RuntimeException或Error时回滚,而捕获到编译时异常不回滚。
2、设置途经
1) 注解@Transactional 注解
 ① rollbackFor属性:指定遇到时必须进行回滚的异常类型,可以为多个
 ② noRollbackFor属性:指定遇到时不回滚的异常类型,可以为多个

 

 

事务的超时和只读属性

1、简介
 由于事务可以在行和表上获得锁,因此长事务会占用资源,并对整体性能产生影响。如果一个事务只读取数据但不做修改,数据库引擎可以对这个事务进行优化。
 超时事务属性:事务在强制回滚之前可以保持多久。这样可以防止长期运行的事务占用资源。
 只读事务属性: 表示这个事务只读取数据但不更新数据, 这样可以帮助数据库引擎优化事务。
2、注解设置
@Transaction注解

package com.atguigu.book.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.atguigu.book.dao.BookDao;
import com.atguigu.book.exception.MyException;
import com.atguigu.book.service.BookService;

@Service
//@Transactional
public class BookServiceImpl implements BookService {

    @Autowired
    private BookDao dao;
    
    /**
     * @Transactional:对方法中所有的操作作为一个事务进行管理
     * 在方法上使用,只对方法有效果
     * 在类上使用,对类中所有的方法都有效果
     * @Transactional中可以设置的属性:
     * 
     * propagation:A方法和B方法都有事务,当A在调用B时,会将A中的事务传播给B方法,B方法对于事务的处理方式就是事务的传播行为
     *         Propagation.REQUIRED:必须使用调用者的事务(默认值)
     *         Propagation.REQUIRES_NEW:将调用者的事务挂起,不使用调用者的事务,使用新的事务进行处理
     * 
     * isolation:事务的隔离级别,在并发的情况下,操作数据的一种规定
     *             读未提交:脏读   1
     *             读已提交:不可重复读   2
     *             可重复读:幻读   4
     *             串行化:性能低,消耗大   8
     * 
     * 
     * timeout:在事务强制回滚前最多可以执行(等待)的时间
     * 
     * readOnly:指定当前事务中的一系列的操作是否为只读
     * 若设置为只读,不管事务中有没有写的操作,mysql都会在请求访问数据的时候,不加锁,提高性能
     * 但是如果有写操作的情况,建议一定不能设置只读
     * 
     * rollbackFor|rollbackForClassName|noRollbackFor|noRollbackForClassName:设置事务回滚的条件
     */
    
    @Transactional(propagation=Propagation.REQUIRES_NEW, timeout=3, noRollbackFor= {NullPointerException.class, MyException.class})
    public void buyBook(String bid, String uid) {
        /*try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }*/
        Integer price = dao.selectPrice(bid);
        dao.updateSt(bid);
        dao.updateBalance(uid, price);
    }
}

 


 
 

 

posted @ 2020-05-19 15:59  kkzhang  阅读(3021)  评论(0编辑  收藏  举报