Spring之事件

打印 上一主题 下一主题

主题 887|帖子 887|积分 2661

一、事件根本概念

   什么是事件
  

  • 数据库事件( transaction)是访问并可能操纵各种数据项的一个数据库操纵序列
  • 这些操纵要么全部执行,要么全部不执行,是一个不可分割的工作单位
   事件的特性
  

  • A:原子性(Atomicity)

    • 一个事件(transaction)中的所有操纵,要么全部完成,要么全部不完成,不会竣事在中心某个环节
    • 事件在执行过程中发生错误,会被回滚(Rollback)到事件开始前的状态,就像这个事件从来没有执行过一样

  • C:一致性(Consistency)

    • 一个事件必须使数据库从一个一致性状态变换到另一个一致性状态
    • 假如事件乐成地完成,那么系统中所有变化将正确地应用,系统处于有效状态
    • 假如在事件中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态

  • I:隔离性(Isolation)

    • 指的是在并发环境中,当不同的事件同时操纵雷同的数据时,每个事件都有各自的完备数据空间
    • 由并发事件所做的修改必须与任何其他并发事件所做的修改隔离
    • 事件查看数据更新时,数据所处的状态要么是另一事件修改它之前的状态,要么是另一事件修改它之后的状态,事件不会查看到中心状态的数据

  • D:长期性(Durability)

    • 指的是只要事件乐成竣事,它对数据库所做的更新就必须保存下来
    • 即使发生系统瓦解,重新启动数据库系统后,数据库还能规复到事件乐成竣事时的状态

二、基于注解的声明式事件

1、@Transactional注解标识的位置



  • @Transactional标识在方法上,则只会影响该方法
  • @Transactional标识的类上,则会影响类中所有的方法
2、事件属性:只读



  • 对一个查询操纵来说,假如我们把它设置成只读,就能够明白告诉数据库,这个操纵不涉及写操纵
  • 这样数据库就能够针对查询操纵来进行优化
  • 默认false,可以查询,可以增删改,设置为true,只能查询
使用方式:
  1. @Transactional(readOnly = true)
  2. public void buyBook(Integer bookId, Integer userId) {
  3.     //查询图书的价格
  4.     Integer price = bookDao.getPriceByBookId(bookId);
  5.     //更新图书的库存
  6.     bookDao.updateStock(bookId);
  7.     //更新用户的余额
  8.     bookDao.updateBalance(userId, price);
  9. }
复制代码
对增删改操纵设置只读会抛出下面非常:Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed
3、事件属性:超时



  • 事件在执行过程中,有可能由于遇到某些问题,导致程序卡住,从而长时间占用数据库资源
  • 而长时间占用资源,大概率是由于程序运行出现了问题(可能是Java程序或MySQL数据库或网络连接等等)
  • 此时这个很可能出问题的程序应该被回滚,撤销它已做的操纵,事件竣事,把资源让出来,让其他正常程序可以执行
  • 概括来说就是一句话:超时回滚,释放资源
  • 默认值为-1,不超时。设置时间以秒单位盘算
使用方式:
  1. //超时时间单位秒
  2. @Transactional(timeout = 3)
  3. public void buyBook(Integer bookId, Integer userId) {
  4.     try {
  5.         TimeUnit.SECONDS.sleep(5);
  6.     } catch (InterruptedException e) {
  7.         e.printStackTrace();
  8.     }
  9.     //查询图书的价格
  10.     Integer price = bookDao.getPriceByBookId(bookId);
  11.     //更新图书的库存
  12.     bookDao.updateStock(bookId);
  13.     //更新用户的余额
  14.     bookDao.updateBalance(userId, price);
  15. }
复制代码
执行过程中抛出非常:org.springframework.transaction.TransactionTimedOutException: Transaction timed out: deadline was Fri Jun 04 16:25:39 CST 2022
4、事件属性:回滚策略



  • 声明式事件默认只针对运行时非常回滚,编译时非常不回滚
  • 可以通过@Transactional中相干属性设置回滚策略

    • rollbackFor属性:须要设置一个Class类型的对象
    • noRollbackFor属性:须要设置一个Class类型的对象
    • rollbackForClassName属性:须要设置一个字符串类型的全类名
    • rollbackFor属性:须要设置一个字符串类型的全类名

使用方式:
  1. @Transactional(noRollbackFor = ArithmeticException.class)
  2. //@Transactional(noRollbackForClassName = "java.lang.ArithmeticException")
  3. public void buyBook(Integer bookId, Integer userId) {
  4.     //查询图书的价格
  5.     Integer price = bookDao.getPriceByBookId(bookId);
  6.     //更新图书的库存
  7.     bookDao.updateStock(bookId);
  8.     //更新用户的余额
  9.     bookDao.updateBalance(userId, price);
  10.     System.out.println(1/0);
  11. }
复制代码
出现了数学运算非常(ArithmeticException),但是我们设置的回滚策略是,当出现ArithmeticException不发生回滚,因此购买图书的操纵正常执行
5、事件属性:隔离级别



  • 数据库系统必须具有隔离并发运行各个事件的能力,使它们不会相互影响,避免各种并发问题
  • 一个事件与其他事件隔离的程度称为隔离级别
  • SQL标准中规定了多种事件隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱
隔离级别一共有四种:


  • 读未提交:READ_UNCOMMITTED(read_uncommitted)

    • 答应Transaction01读取Transaction02未提交的修改

  • 读已提交:READ_COMMITTED(read_committed)

    • 要求Transaction01只能读取Transaction02已提交的修改

  • 可重复读:REPEATABLE_READ(repeatable_read)

    • 确保Transaction01可以多次从一个字段中读取到雷同的值
    • 即使Transaction01执行期间别的事件对这个字段进行更新,Transaction01读到也没有变化

  • 串行化:SERIALIZABLE(serializable)

    • 确保Transaction01可以多次从一个表中读取到雷同的行,在Transaction01执行期间,克制别的事件对这个表进行添加、更新、删除操纵
    • 可以避免任何并发问题,但性能十分低下

各个隔离级别办理并发问题的能力见下表:
隔离级别脏读不可重复读幻读READ UNCOMMITTED-读未提交有有有READ COMMITTED-读已提交无有有REPEATABLE READ-可重复读无无有SERIALIZABLE-串行读无无无 各种数据库产物对事件隔离级别的支持程度:
隔离级别OracleMySQLREAD UNCOMMITTED-读未提交×√READ COMMITTED-读已提交√(默认)√REPEATABLE READ-可重复读×√(默认)SERIALIZABLE-串行读√√ 使用方式:
  1. @Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别
  2. @Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
  3. @Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
  4. @Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
  5. @Transactional(isolation = Isolation.SERIALIZABLE)//串行化
复制代码
6、事件属性:流传行为

   什么是事件的流传行为?
  

  • 在service1类和service2类中分别有a()方法和b()方法,两个方法都是事件
  • 当a()方法执行过程中调用了b()方法,事件是如何传递的?
  • 归并到一个事件里?还是开启一个新的事件?这就是事件流传行为
   七种流传行为
  

  • REQUIRED(required):支持当前事件,假如不存在就新建一个(默认)【没有就新建,有就加入】
  • SUPPORTS(supports):支持当前事件,假如当前没有事件,就以非事件方式执行【有就加入,没有就不管了】
  • MANDATORY(mandatory):必须运行在一个事件中,假如当前没有事件正在发生,将抛出一个非常【有就加入,没有就抛非常】
  • REQUIRES_NEW(requires_new):开启一个新的事件,假如一个事件已经存在,则将这个存在的事件挂起【不管有没有,直接开启一个新事件,开启的新事件和之前的事件不存在嵌套关系,之前事件被挂起】实用内部事件和外部事件不存在业务关联情况,如日志
  • NOT_SUPPORTED(not_supported):以非事件方式运行,假如有事件存在,挂起当前事件【不支持事件,存在就挂起】
  • NEVER(never):以非事件方式运行,假如有事件存在,抛出非常【不支持事件,存在就抛非常】
  • NESTED(nested):假如当前正有一个事件在进行中,则该方法应当运行在一个嵌套式事件中。被嵌套的事件可以独立于外层事件进行提交或回滚。假如外层事件不存在,行为就像REQUIRED一样【有事件的话,就在这个事件里再嵌套一个完全独立的事件,嵌套的事件可以独立的提交和回滚。没有事件就和REQUIRED一样】


   测试
  

  • 买书方法
  1. @Transactional(propagation = Propagation.REQUIRED)
  2. public void buyBook(Integer bookId, Integer userId) {
  3.     //查询图书的价格
  4.     Integer price = bookDao.getPriceByBookId(bookId);
  5.     //更新图书的库存
  6.     bookDao.updateStock(bookId);
  7.     //更新用户的余额
  8.     bookDao.updateBalance(userId, price);
  9. }
复制代码


  • 另一个有事件的方法,多次调用买书方法
  1. @Service
  2. public class CheckoutServiceImpl implements CheckoutService {
  3.     @Autowired
  4.     private BookService bookService;
  5.     @Override
  6.     @Transactional
  7.     //一次购买多本图书
  8.     public void checkout(Integer[] bookIds, Integer userId) {
  9.         for (Integer bookId : bookIds) {
  10.             bookService.buyBook(bookId, userId);
  11.         }
  12.     }
  13. }
复制代码
观察结果:


  • 买书方法@Transactional(propagation = Propagation.REQUIRED)
  • 表现假如当火线程上有已经开启的事件可用,那么就在这个事件中运行
  • 经过观察,购买图书的方法buyBook()在checkout()中被调用,checkout()上有事件注解,因此在此事件中执行
  • 所购买的两本图书的价格为80和50,而用户的余额为100
  • 在购买第二本图书时余额不足失败,导致整个checkout()回滚,即只要有一本书买不了,就都买不了



  • 买书方法@Transactional(propagation = Propagation.REQUIRES_NEW)
  • 表现不管当火线程上是否有已经开启的事件,都要开启新事件
  • 加在buybook()上,每次调用这个方法,都开起一个新事件,每次调用都不相干
  • 每次购买图书都是在buyBook()的事件中执行,因此第一本图书购买乐成,事件竣事,第二本图书购买失败,只在第二次的buyBook()中回滚,购买第一本图书不受影响,即能买几本就买几本

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

勿忘初心做自己

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表