• (续)SSM整合之spring笔记(声明式事务)(P110-117)(还没完)


     

    目录

    一 .声明式事务概念

    二 .基于注解的声明式事务

    1 .准备工作 

    (1)加入依赖

    (2)创建jdbc.properties 

    (3)配置Spring的配置文件     tx-annotation.xml

    (4)创建表

     (5)创建组件   创建三层架构  

    (6) 添加扫描组件

     (7) 添加自动装配@Autowired

    (8) 写buyBook方法   买书的方法  

    (9) 在BookService里面创建买书的方法 

    (10) 找到实现类BookServiceImpl对买书的方法进行重写 

     (11)创建getPriceByBookId   updateStock    updateBalance方法

    ​编辑

    (12)  找到实现类BookDaoImpl      对方法进行重写

    (13)创建jdbcTemplate

    (14)方法重写

    二  .  测试无事务情况

    三  .  加入事务

    ①添加事务配置

    ②添加事务注解

     ③观察结果

    ④ @Transactional注解标识的位置

    四 、事务属性之只读

    ①介绍

    ②使用方式

    ③注意

    五.  事务属性之超时 

    ①介绍

    ②使用方式

    ③观察结果

    六.  事务属性之回滚策略

    ①介绍

    ②使用方式

    ③观察结果

    七.  事务属性之事务隔离级别

    八.  事务属性之事务传播行为

    ①介绍

    ②测试

    ③观察结果


    一 .声明式事务概念

    编程式事务

    事务功能的相关操作全部通过自己编写代码来实现:
    1. Connection conn = ...;
    2. try {
    3. // 开启事务:关闭事务的自动提交
    4. conn.setAutoCommit(false);
    5. // 核心操作
    6. // 提交事务
    7. conn.commit();
    8. }catch(Exception e){
    9. // 回滚事务
    10. conn.rollBack();
    11. }finally{
    12. // 释放数据库连接
    13. conn.close();
    14. }
    编程式的实现方式存在缺陷:
      细节没有被屏蔽:具体操作过程中,所有细节都需要程序员自己来完成,比较繁琐。
      代码复用性不高:如果没有有效抽取出来,每次实现功能都需要自己编写代码,代码就没有得到    复用。
    声明式事务
    既然事务控制的代码有规律可循,代码的结构基本是确定的,所以框架就可以将固定模式的代码抽取出
    来,进行相关的封装。
    封装起来后,我们只需要在配置文件中进行简单的配置即可完成操作。
    好处 1 :提高开发效率
    好处 2 :消除了冗余的代码
    好处 3 :框架会综合考虑相关领域中在实际开发环境下有可能遇到的各种问题,进行了健壮性、性
    能等各个方面的优化
    所以,我们可以总结下面两个概念:
    编程式 自己写代码 实现功能
    声明式 :通过 配置 框架 实现功能

    二 .基于注解的声明式事务

    1 .准备工作 

    (1)加入依赖

    jar
    
    
    
        
        
            org.springframework
            spring-context
            5.3.1
        
    
        
        
        
        
            org.springframework
            spring-orm
            5.3.1
        
    
        
        
            org.springframework
            spring-test
            5.3.1
        
    
        
        
            junit
            junit
            4.12
            test
        
    
        
        
            mysql
            mysql-connector-java
            8.0.16
        
        
        
            com.alibaba
            druid
            1.0.31
        
        
            org.springframework
            spring-aspects
            5.3.1
        
    
    

    (2)创建jdbc.properties 

    jdbc.driver=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
    jdbc.username=root
    jdbc.password=123456

    (3)配置Spring的配置文件     tx-annotation.xml

    1. "1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    6. <context:component-scan base-package="com.atguigu.spring">context:component-scan>
    7. <context:property-placeholder location="classpath:jdbc.properties">context:property-placeholder>
    8. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    9. <property name="driverClassName" value="${jdbc.driver}">property>
    10. <property name="url" value="${jdbc.url}">property>
    11. <property name="username" value="${jdbc.username}">property>
    12. <property name="password" value="${jdbc.password}">property>
    13. bean>
    14. <bean class="org.springframework.jdbc.core.JdbcTemplate">
    15. <property name="dataSource" ref="dataSource">property>
    16. bean>
    17. beans>

    (4)创建表

    CREATE TABLE `t_book` (
    `book_id` int ( 11 ) NOT NULL AUTO_INCREMENT COMMENT ' 主键 ' ,
    `book_name` varchar ( 20 ) DEFAULT NULL COMMENT ' 图书名称 ' ,
    `price` int ( 11 ) DEFAULT NULL COMMENT ' 价格 ' ,
    `stock` int ( 10 ) unsigned DEFAULT NULL COMMENT ' 库存(无符号) ' ,
    PRIMARY KEY (`book_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT= 3 DEFAULT CHARSET=utf8;
    insert into `t_book`(`book_id`,`book_name`,`price`,`stock`) values ( 1 , ' 斗破苍
    ' , 80 , 100 ),( 2 , ' 斗罗大陆 ' , 50 , 100 );
    CREATE TABLE `t_user` (
    `user_id` int ( 11 ) NOT NULL AUTO_INCREMENT COMMENT ' 主键 ' ,
    `username` varchar ( 20 ) DEFAULT NULL COMMENT ' 用户名 ' ,
    `balance` int ( 10 ) unsigned DEFAULT NULL COMMENT ' 余额(无符号) ' ,
    PRIMARY KEY (`user_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT= 2 DEFAULT CHARSET=utf8;
    insert into `t_user`(`user_id`,`username`,`balance`) values ( 1 , 'admin' , 50 );

     (5)创建组件   创建三层架构  

    控制层:controller.BookController
    持久层:dao.BookDao    dao.impl.BookDaoImpl  
    业务层:service.BookService     service.impl.BookServiceImpl

    控制层:controller.BookController

    1. package com.atguigu.spring.controller;
    2. import org.springframework.stereotype.Controller;
    3. @Controller
    4. public class BookController {
    5. }
     
    

     持久层:dao.BookDao         dao .impl.BookDaoImpl

    dao.BookDao 

    1. package com.atguigu.spring.dao;
    2. public interface BookDao {
    3. }
     
    

     dao .impl.BookDaoImpl

    1. package com.atguigu.spring.dao.impl;
    2. import org.springframework.stereotype.Repository;
    3. @Repository
    4. public class BookDaoImpl {
    5. }

     业务层:service.BookService           service.impl.BookServiceImpl

     service.BookService

    1. package com.atguigu.spring.service;
    2. public interface BookService {
    3. }
     
    

      service.impl.BookServiceImpl

    1. package com.atguigu.spring.service.impl;
    2. import org.springframework.stereotype.Service;
    3. @Service
    4. public class BookServiceImpl {
    5. }
     
    

    (6) 添加扫描组件

    1. <context:component-scan base-package="com.atguigu.spring">context:component-scan>

     

     (7) 添加自动装配@Autowired

    1. package com.atguigu.spring.controller;
    2. import com.atguigu.spring.service.BookService;
    3. import org.springframework.beans.factory.annotation.Autowired;
    4. import org.springframework.stereotype.Controller;
    5. @Controller
    6. public class BookController {
    7. @Autowired
    8. private BookService bookService;
    9. }

    1. package com.atguigu.spring.service.impl;
    2. import com.atguigu.spring.dao.BookDao;
    3. import org.springframework.beans.factory.annotation.Autowired;
    4. import org.springframework.stereotype.Service;
    5. @Service
    6. public class BookServiceImpl {
    7. @Autowired
    8. private BookDao bookDao;
    9. }

     

    (8) 写buyBook方法   买书的方法  

    1. package com.atguigu.spring.controller;
    2. import com.atguigu.spring.service.BookService;
    3. import org.springframework.beans.factory.annotation.Autowired;
    4. import org.springframework.stereotype.Controller;
    5. @Controller
    6. public class BookController {
    7. @Autowired
    8. private BookService bookService;
    9. public void buyBook(Integer userId, Integer bookId){
    10. bookService.buyBook(userId, bookId);
    11. }
    12. }

    (9) 在BookService里面创建买书的方法 

     
    

    1. package com.atguigu.spring.service;
    2. public interface BookService {
    3. /**
    4. * 买书
    5. * @param userId
    6. * @param bookId
    7. */
    8. void buyBook(Integer userId, Integer bookId);
    9. }
     
    

    (10) 找到实现类BookServiceImpl对买书的方法进行重写 

             

     
    
    1. @Service
    2. public class BookServiceImpl implements BookService {
    3. @Autowired
    4. private BookDao bookDao;
    5. @Override
    6. public void buyBook(Integer userId, Integer bookId) {
    7. //查询图书的价格
    8. Integer price = bookDao.getPriceByBookId(bookId);
    9. //更新图书的库存
    10. bookDao.updateStock(bookId);
    11. //更新用户的余额
    12. bookDao.updateBalance(userId, price);
    13. }
    14. }

     (11)创建getPriceByBookId   updateStock    updateBalance方法

    1. package com.atguigu.spring.dao;
    2. public interface BookDao {
    3. /**
    4. * 根据图书的id查询图书的价格
    5. * @param bookId
    6. * @return
    7. */
    8. Integer getPriceByBookId(Integer bookId);
    9. /**
    10. * 更新图书的库存
    11. * @param bookId
    12. */
    13. void updateStock(Integer bookId);
    14. /**
    15. * 更新用户的余额
    16. * @param userId
    17. * @param price
    18. */
    19. void updateBalance(Integer userId, Integer price);
    20. }

     

    (12)  找到实现类BookDaoImpl      对方法进行重写

     

    (13)创建jdbcTemplate

    在配置文件中配置了JdbcTemplate  测试事务功能的时候就是 使用JdbcTemplate执行sql语句 的过程中来测试的

    1. package com.atguigu.spring.dao.impl;
    2. @Repository
    3. public class BookDaoImpl implements BookDao {
    4. @Autowired
    5. private JdbcTemplate jdbcTemplate;
    6. @Override
    7. public Integer getPriceByBookId(Integer bookId) {
    8. return null;
    9. }
    10. @Override
    11. public void updateStock(Integer bookId) {
    12. }
    13. @Override
    14. public void updateBalance(Integer userId, Integer price) {
    15. }
    16. }

     JdbcTemplate在iOC容器里面有 因为 当前配置文件已经配置了一个bean  所以我们这里可以自动装配@Autowired

    (14)方法重写

    1. package com.atguigu.spring.dao.impl;
    2. import com.atguigu.spring.dao.BookDao;
    3. import org.springframework.beans.factory.annotation.Autowired;
    4. import org.springframework.jdbc.core.JdbcTemplate;
    5. import org.springframework.stereotype.Repository;
    6. @Repository
    7. public class BookDaoImpl implements BookDao {
    8. @Autowired
    9. private JdbcTemplate jdbcTemplate;
    10. //根据图书的id查询图书的价格
    11. @Override
    12. public Integer getPriceByBookId(Integer bookId) {
    13. String sql = "select price from t_book where book_id = ?";
    14. return jdbcTemplate.queryForObject(sql, Integer.class, bookId);
    15. }
    16. //更新图书的库存
    17. @Override
    18. public void updateStock(Integer bookId) {
    19. String sql = "update t_book set stock = stock - 1 where book_id = ?";
    20. jdbcTemplate.update(sql, bookId);
    21. }
    22. //更新用户的余额
    23. @Override
    24. public void updateBalance(Integer userId, Integer price) {
    25. String sql = "update t_user set balance = balance - ? where user_id = ?";
    26. jdbcTemplate.update(sql, price, userId);
    27. }
    28. }

    二  .  测试无事务情况

    让用户id为1的用户  去购买图书id为1的图书

    可以看到库存够  余额不够  书的价格是80  余额只有50  

     创建测试类TxByAnnotationTest

    1. package com.atguigu.spring.test;
    2. @RunWith(SpringJUnit4ClassRunner.class)
    3. @ContextConfiguration("classpath:tx-annotation.xml")
    4. public class TxByAnnotationTest {
    5. @Autowired
    6. private BookController bookController;
    7. @Test
    8. public void testBuyBook(){
    9. bookController.buyBook(1, 1);
    10. }
    11. }
    报错
    

    Caused by: com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: BIGINT UNSIGNED value is out of range in '(`ssm`.`t_user`.`balance` - 80)'

    UNSIGNED:无符号  

    结果超出范围

     

    我们当前是没有事物的情况

    在mysql默认的情况下 我们当前的一个sql语句 独占一个事物 并且自动提交  

    所以在当前没有设置事物有情况下  我们的三个sql 都是独占一个事物 并且自动提交

    所以我们在实现事物的过程中   一定要把事物的自动提交给关闭掉   因为如果不关闭 每一个sql语句是独占一个事物的  

    总结:在mysql 默认的情况下  一个sql语句 独占一个事物 并且自动提交  

    观察数据库 发现更新图书的库存可以成功   更新用户的余额不成功  

    原来

    现在

     
    

     

    三  .  加入事务

    ①添加事务配置

       Spring的配置文件中添加配置:

    1. "1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    6. <context:component-scan base-package="com.atguigu.spring">context:component-scan>
    7. <context:property-placeholder location="classpath:jdbc.properties">context:property-placeholder>
    8. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    9. <property name="driverClassName" value="${jdbc.driver}">property>
    10. <property name="url" value="${jdbc.url}">property>
    11. <property name="username" value="${jdbc.username}">property>
    12. <property name="password" value="${jdbc.password}">property>
    13. bean>
    14. <bean class="org.springframework.jdbc.core.JdbcTemplate">
    15. <property name="dataSource" ref="dataSource">property>
    16. bean>
    17. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    18. <property name="dataSource" ref="dataSource">property>
    19. bean>
    20. <tx:annotation-driven transaction-manager="transactionManager" />
    21. beans>

     

     拓展:注意:导入的名称空间需要 tx 结尾的那个。

    ②添加事务注解

    因为 service 层表示业务逻辑层,一个方法表示一个完成的功能,因此处理事务一般在 service 层处理 在BookServiceImpl buybook() 添加注解 @Transactional
    1. package com.atguigu.spring.service.impl;
    2. import com.atguigu.spring.dao.BookDao;
    3. import com.atguigu.spring.service.BookService;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.stereotype.Service;
    6. import org.springframework.transaction.annotation.Transactional;
    7. @Service
    8. public class BookServiceImpl implements BookService {
    9. @Autowired
    10. private BookDao bookDao;
    11. @Override
    12. @Transactional //使用事务进行管理
    13. public void buyBook(Integer userId, Integer bookId) {
    14. //查询图书的价格
    15. Integer price = bookDao.getPriceByBookId(bookId);
    16. //更新图书的库存
    17. bookDao.updateStock(bookId);
    18. //更新用户的余额
    19. bookDao.updateBalance(userId, price);
    20. }
    21. }

    现在我们在t_book表里面的库存由98改成100

    原来

     

     改成100

     ③观察结果

    由于使用了Spring的声明式事务,更新库存和更新余额都没有执行

     从新执行  

    1. package com.atguigu.spring.test;
    2. /**
    3. * 声明式事务的配置步骤:
    4. * 1、在Spring的配置文件中配置事务管理器
    5. * 2、开启事务的注解驱动
    6. * 在需要被事务管理的方法上,添加@Transactional注解,该方法就会被事务管理
    7. * @Transactional注解标识的位置:
    8. * 1、标识在方法上
    9. * 2、标识在类上,则类中所有的方法都会被事务管理
    10. */
    11. @RunWith(SpringJUnit4ClassRunner.class)
    12. @ContextConfiguration("classpath:tx-annotation.xml")
    13. public class TxByAnnotationTest {
    14. @Autowired
    15. private BookController bookController;
    16. @Test
    17. public void testBuyBook(){
    18. bookController.buyBook(1, 1);
    19. }
    20. }

     总结:

    /**
     * 声明式事务的配置步骤:
     * 1、在Spring的配置文件中配置事务管理器
     * 2、开启事务的注解驱动
     * 在需要被事务管理的方法上,添加@Transactional注解,该方法就会被事务管理
     * @Transactional注解标识的位置:
     * 1、标识在方法上
     * 2、标识在类上,则类中所有的方法都会被事务管理
     */

    @Transactional注解标识的位置

    @Transactional 标识在方法上,咋只会影响该方法
    @Transactional 标识的类上,咋会影响类中所有的方法

    、事务属性之只读

    ①介绍

    对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化。

    ②使用方式

    1. @Service
    2. public class BookServiceImpl implements BookService {
    3. @Autowired
    4. private BookDao bookDao;
    5. @Override
    6. @Transactional(readOnly = true) //使用事务进行管理
    7. public void buyBook(Integer userId, Integer bookId) {
    8. //查询图书的价格
    9. Integer price = bookDao.getPriceByBookId(bookId);
    10. //更新图书的库存
    11. bookDao.updateStock(bookId);
    12. //更新用户的余额
    13. bookDao.updateBalance(userId, price);
    14. }
    15. }

    测试

    1. @RunWith(SpringJUnit4ClassRunner.class)
    2. @ContextConfiguration("classpath:tx-annotation.xml")
    3. public class TxByAnnotationTest {
    4. @Autowired
    5. private BookController bookController;
    6. @Test
    7. public void testBuyBook(){
    8. bookController.buyBook(1, 1);
    9. }
    10. }

    ③注意

    对增删改操作设置只读会抛出下面异常:
    Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification
    are not allowed

    报错:

    Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed 

    我们当前的连接是只读的  我们的数据修改是不被允许的

    只有当前的事物全部是查询操作的时候才可以用只读

     

    五.  事务属性之超时 

    ①介绍

    事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间 占用资源,大概率是因为程序运行出现了问题(可能是Java 程序或 MySQL 数据库或网络连接等等)。
    此时这个很可能出问题的程序应该被回滚,撤销它已做的操作,事务结束,把资源让出来,让其他正常程序可以执行。
    概括来说就是一句话:超时回滚,释放资源。

    ②使用方式

    1. @Service
    2. public class BookServiceImpl implements BookService {
    3. @Autowired
    4. private BookDao bookDao;
    5. @Override
    6. @Transactional(
    7. //readOnly = true
    8. timeout = 3
    9. ) //使用事务进行管理
    10. public void buyBook(Integer userId, Integer bookId) {
    11. try {
    12. TimeUnit.SECONDS.sleep(5);
    13. } catch (InterruptedException e) {
    14. e.printStackTrace();
    15. }
    16. //查询图书的价格
    17. Integer price = bookDao.getPriceByBookId(bookId);
    18. //更新图书的库存
    19. bookDao.updateStock(bookId);
    20. //更新用户的余额
    21. bookDao.updateBalance(userId, price);
    22. }
    23. }

    ③观察结果

    执行过程中抛出异常:
    org.springframework.transaction. TransactionTimedOutException : Transaction timed out:
    deadline was Fri Jun 04 16:25:39 CST 2022
    如果在规定的时间内  事物没有执行完  那就可强制回滚并抛出异常
    报错:
    org.springframework.transaction.TransactionTimedOutException: Transaction timed out: deadline was Tue Nov 08 22:19:55 CST 2022
    事物真是异常  

    六.  事务属性之回滚策略

    ①介绍

    声明式事务默认只针对运行时异常回滚,编译时异常不回滚。
    可以通过 @Transactional 中相关属性设置回滚策略
    rollbackFor 属性:需要设置一个 Class 类型的对象
    rollbackForClassName 属性:需要设置一个字符串类型的全类名
    noRollbackFor 属性:需要设置一个 Class 类型的对象
    rollbackFor 属性:需要设置一个字符串类型的全类名

    ②使用方式

    1. @Service
    2. public class BookServiceImpl implements BookService {
    3. @Autowired
    4. private BookDao bookDao;
    5. @Override
    6. @Transactional(
    7. //readOnly = true
    8. //timeout = 3
    9. ) //使用事务进行管理
    10. public void buyBook(Integer userId, Integer bookId) {
    11. /* try {
    12. TimeUnit.SECONDS.sleep(5);
    13. } catch (InterruptedException e) {
    14. e.printStackTrace();
    15. }*/
    16. //查询图书的价格
    17. Integer price = bookDao.getPriceByBookId(bookId);
    18. //更新图书的库存
    19. bookDao.updateStock(bookId);
    20. //更新用户的余额
    21. bookDao.updateBalance(userId, price);
    22. System.out.println(1 / 0); //数学运算异常 运行时异常
    23. }
    24. }

    原来

     现在

     结果:

     

    1. @Service
    2. public class BookServiceImpl implements BookService {
    3. @Autowired
    4. private BookDao bookDao;
    5. @Override
    6. @Transactional(
    7. //readOnly = true
    8. //timeout = 3
    9. noRollbackFor = ArithmeticException.class //不造成回滚
    10. ) //使用事务进行管理
    11. public void buyBook(Integer userId, Integer bookId) {
    12. /* try {
    13. TimeUnit.SECONDS.sleep(5);
    14. } catch (InterruptedException e) {
    15. e.printStackTrace();
    16. }*/
    17. //查询图书的价格
    18. Integer price = bookDao.getPriceByBookId(bookId);
    19. //更新图书的库存
    20. bookDao.updateStock(bookId);
    21. //更新用户的余额
    22. bookDao.updateBalance(userId, price);
    23. System.out.println(1/0); //数学运算异常 运行时异常
    24. }
    25. }

    ③观察结果

    虽然购买图书功能中出现了数学运算异常( ArithmeticException ),但是我们设置的回滚策略是,当 出现ArithmeticException 不发生回滚,因此购买图书的操作正常执行

     

    七.  事务属性之事务隔离级别

    ①介绍
    数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事 务与其他事务隔离的程度称为隔离级别。SQL 标准中规定了多种事务隔离级别,不同隔离级别对应不同 的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。
    隔离级别一共有四种:
    读未提交: READ UNCOMMITTED
    允许 Transaction01 读取 Transaction02 未提交的修改。
    读已提交: READ COMMITTED
    要求 Transaction01 只能读取 Transaction02 已提交的修改。
    可重复读: REPEATABLE READ
    确保 Transaction01 可以多次从一个字段中读取到相同的值,即 Transaction01 执行期间禁止其它
    事务对这个字段进行更新。
    串行化: SERIALIZABLE
    确保 Transaction01 可以多次从一个表中读取到相同的行,在 Transaction01 执行期间,禁止其它
    事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。

    各个隔离级别解决并发问题的能力见下表:

    各种数据库产品对事务隔离级别的支持程度:

    ②使用方式

    1. @Service
    2. public class BookServiceImpl implements BookService {
    3. @Autowired
    4. private BookDao bookDao;
    5. @Override
    6. @Transactional(
    7. //readOnly = true
    8. //timeout = 3
    9. // noRollbackFor = ArithmeticException.class 不造成回滚
    10. // noRollbackForClassName = "java.lang.ArithmeticException" //不造成回滚
    11. isolation = Isolation.DEFAULT
    12. ) //使用事务进行管理
    13. public void buyBook(Integer userId, Integer bookId) {
    14. /* try {
    15. TimeUnit.SECONDS.sleep(5);
    16. } catch (InterruptedException e) {
    17. e.printStackTrace();
    18. }*/
    19. //查询图书的价格
    20. Integer price = bookDao.getPriceByBookId(bookId);
    21. //更新图书的库存
    22. bookDao.updateStock(bookId);
    23. //更新用户的余额
    24. bookDao.updateBalance(userId, price);
    25. System.out.println(1/0); //数学运算异常 运行时异常
    26. }
    27. }

    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)//串行化

     

    八.  事务属性之事务传播行为

    ①介绍

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

    ②测试

    设置mysql

     

     创建接口CheckoutService

    1. public interface CheckoutService {
    2. /**
    3. * 结账
    4. * @param userId
    5. * @param bookIds
    6. */
    7. void checkout(Integer userId, Integer[] bookIds);
    8. }
    创建实现类 CheckoutServiceImpl
    1. @Service
    2. public class CheckoutServiceImpl implements CheckoutService {
    3. @Autowired
    4. private BookService bookService;
    5. @Override
    6. //@Transactional
    7. public void checkout(Integer userId, Integer[] bookIds) {
    8. for (Integer bookId : bookIds) {
    9. bookService.buyBook(userId, bookId);
    10. }
    11. }
    12. }
    BookController 中添加方法:
    1. @Controller
    2. public class BookController {
    3. @Autowired
    4. private BookService bookService;
    5. @Autowired
    6. private CheckoutService checkoutService;
    7. public void buyBook(Integer userId, Integer bookId){
    8. bookService.buyBook(userId, bookId);
    9. }
    10. public void checkout(Integer userId, Integer[] bookIds){
    11. checkoutService.checkout(userId, bookIds);
    12. }
    13. }
    测试:
    1. @RunWith(SpringJUnit4ClassRunner.class)
    2. @ContextConfiguration("classpath:tx-annotation.xml")
    3. public class TxByAnnotationTest {
    4. @Autowired
    5. private BookController bookController;
    6. @Test
    7. public void testBuyBook(){
    8. // bookController.buyBook(1, 1);
    9. bookController.checkout(1, new Integer[]{1,2});
    10. }
    11. }

    在数据库中将用户的余额修改为 100

    ③观察结果

    可以通过 @Transactional 中的 propagation 属性设置事务传播行为
    修改 BookServiceImpl buyBook() 上,注解 @Transactional propagation 属性
    @Transactional(propagation = Propagation.REQUIRED) ,默认情况,表示如果当前线程上有已经开启的事务可用,那么就在这个事务中运行。经过观察,购买图书的方法buyBook() checkout() 中被调 用,checkout() 上有事务注解,因此在此事务中执行。所购买的两本图书的价格为 80 50 ,而用户的余 额为100 ,因此在购买第二本图书时余额不足失败,导致整个 checkout() 回滚,即只要有一本书买不 了,就都买不了
    @Transactional(propagation = Propagation.REQUIRES_NEW) ,表示不管当前线程上是否有已经开启 的事务,都要开启新事务。同样的场景,每次购买图书都是在buyBook() 的事务中执行,因此第一本图 书购买成功,事务结束,第二本图书购买失败,只在第二次的buyBook() 中回滚,购买第一本图书不受 影响,即能买几本就买几本

  • 相关阅读:
    互联网快讯:吉利正式收购魅族;胰岛素集采在31省全面落地
    Azkaban环境配置-尚硅谷大数据培训
    数据结构与算法——线性表的顺序存储
    联邦学习应用研究现状及发展趋势
    【Java 学习】方法 和 一些编程案例
    linux下部署nacos(单机、集群)
    同时看过 unreal4 和 Unity 源代码的人觉得哪个引擎架构更好?
    MCP2515调试心得
    云计算有什么作用
    打造工业操作系统开源开放体系
  • 原文地址:https://blog.csdn.net/m0_59281987/article/details/127749987