• Spring基础-11-事务细节


    1         事务的隔离级别

    1.1         数据库事务并发问题

    假设现在有两个事务:Transaction01和Transaction02并发执行。

    脏读

          [1]Transaction01将某条记录的AGE值从20修改为30。

          [2]Transaction02读取了Transaction01更新后的值:30。

          [3]Transaction01回滚,AGE值恢复到了20。

          [4]Transaction02读取到的30就是一个无效的值。

    不可重复读

          [1]Transaction01读取了AGE值为20。

          [2]Transaction02将AGE值修改为30。

          [3]Transaction01再次读取AGE值为30,和第一次读取不一致。

    幻读

          [1]Transaction01读取了STUDENT表中的一部分数据。

          [2]Transaction02向STUDENT表中插入了新的行。

          [3]Transaction01读取了STUDENT表时,多出了一些行。

    1.2         隔离级别

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

    读未提交:READ UNCOMMITTED

    允许Transaction01读取Transaction02未提交的修改。

    读已提交:READ COMMITTED

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

    可重复读:REPEATABLE READ

             确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。

    串行化:SERIALIZABLE

             确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。

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

    脏读

    不可重复读

    幻读

    READ UNCOMMITTED

    READ COMMITTED

    REPEATABLE READ

    SERIALIZABLE

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

    Oracle

    MySQL

    READ UNCOMMITTED

    ×

    READ COMMITTED

    REPEATABLE READ

    ×

    √(默认)

    SERIALIZABLE

    修改MySQL隔离级别
    SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
    
    如:SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    
    查询MySQL的隔离级别
    SELECT @@global.tx_isolation; //查询全局隔离级别
    SELECT @@session.tx_isolation;//查询当前会话隔离级别 
    SELECT @@tx_isolation;//同上
    
    
    事务操作
    开启事务  start transaction;
    提交事务  commit;
    回滚事务  rollback;

    各种读写情况演示

    脏读:

     可重复度:

     不可重复度:

     并发修改:底层会自动进行排队等待

     事务的传播行为

       简介

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

    事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为。

     

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

    说明

    ①REQUIRED传播行为

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

    ②REQUIRES_NEW传播行为

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

     案例代码:

    BookDao.java:

     1 package com.atguigu.dao;
     2 
     3 import org.springframework.beans.factory.annotation.Autowired;
     4 import org.springframework.jdbc.core.JdbcTemplate;
     5 import org.springframework.stereotype.Repository;
     6 
     7 @Repository
     8 public class BookDao {
     9     
    10     @Autowired
    11     JdbcTemplate jdbcTemplate;
    12     
    13     /**
    14      * 1、减余额
    15      * 
    16      * 减去某个用户的余额
    17      */
    18     public void updateBalance(String userName,int price){
    19         String sql = "UPDATE account SET balance=balance-? WHERE username=?";
    20         jdbcTemplate.update(sql, price,userName);
    21     }
    22     
    23     /**
    24      * 2、按照图书的ISBN获取某本图书的价格
    25      * @return
    26      */
    27     public int getPrice(String isbn){
    28         String sql = "SELECT price FROM book WHERE isbn=?";
    29         return jdbcTemplate.queryForObject(sql, Integer.class, isbn);
    30     }
    31     
    32     /**
    33      * 3、减库存;减去某本书的库存;为了简单期间每次减一
    34      */
    35     public void updateStock(String isbn){
    36         String sql = "UPDATE book_stock SET stock=stock-1 WHERE isbn=?";
    37         jdbcTemplate.update(sql, isbn);
    38     }
    39     
    40     /**
    41      * 4、改图书价格
    42      * @param isbn
    43      * @param price
    44      */
    45     public void updatePrice(String isbn,int price){
    46         String sql = "update book set price=? where isbn=?";
    47         jdbcTemplate.update(sql, price,isbn);
    48     }
    49 }

    BookService.java:

      1 package com.atguigu.service;
      2 
      3 import java.io.FileInputStream;
      4 import java.io.FileNotFoundException;
      5 
      6 import org.springframework.beans.factory.annotation.Autowired;
      7 import org.springframework.stereotype.Service;
      8 import org.springframework.transaction.annotation.Isolation;
      9 import org.springframework.transaction.annotation.Propagation;
     10 import org.springframework.transaction.annotation.Transactional;
     11 
     12 import com.atguigu.dao.BookDao;
     13 
     14 @Service
     15 public class BookService {
     16     
     17     @Autowired
     18     BookDao bookDao;
     19     
     20     
     21     
     22 //    @Autowired
     23 //    BookService bookService;
     24     
     25     /**
     26      * 事务细节:
     27      * isolation-Isolation:事务的隔离级别;
     28      * 
     29      * 
     30      * 
     31      * noRollbackFor-Class[]:哪些异常事务可以不回滚
     32      * noRollbackForClassName-String[](String全类名):
     33      * 
     34      * rollbackFor-Class[]:哪些异常事务需要回滚;
     35      * rollbackForClassName-String[]:
     36      * 
     37      * 异常分类:
     38      *         运行时异常(非检查异常):可以不用处理;默认都回滚;
     39      *         编译时异常(检查异常):要么try-catch,要么在方法上声明throws
     40      *                 默认不回滚;
     41      * 
     42      * 事务的回滚:默认发生运行时异常都 回滚,发生编译时异常不会回滚;
     43      * noRollbackFor:哪些异常事务可以不回滚;(可以让原来默认回滚的异常给他不回滚)
     44      *     noRollbackFor={ArithmeticException.class,NullPointerException.class}
     45      * noRollbackForClassName
     46      * 
     47      * rollbackFor:原本不回滚(原本编译时异常是不回滚的)的异常指定让其回滚;
     48      * 
     49      * readOnly-boolean:设置事务为只读事务:
     50      *         可以进行事务优化;
     51      *         readOnly=true:加快查询速度;不用管事务那一堆操作了。
     52      * 
     53      * timeout-int(秒为单位):超时:事务超出指定执行时长后自动终止并回滚
     54      * @throws FileNotFoundException 
     55      * 
     56      * 
     57      * propagation-Propagation:事务的传播行为;
     58      *     传播行为(事务的传播+事务的行为);
     59      *         如果有多个事务进行嵌套运行,子事务是否要和大事务共用一个事务;
     60      * 传播行为:
     61      * AService{
     62      *         tx_a(){
     63      *             //a的一些方法
     64      *             tx_b(){
     65      *             }
     66      *             tx_c(){
     67      *             }
     68      *         }
     69      * }
     70      * 
     71      * 
     72      */
     73     @Transactional(propagation=Propagation.REQUIRES_NEW)
     74     public void checkout(String username,String isbn){
     75         //1、减库存
     76         bookDao.updateStock(isbn);
     77         
     78         int price = bookDao.getPrice(isbn);
     79 //        try {
     80 //            Thread.sleep(3000);
     81 //        } catch (InterruptedException e) {
     82 //            e.printStackTrace();
     83 //        }
     84         //2、减余额
     85         bookDao.updateBalance(username, price);
     86         
     87         //int i = 10/0;
     88         //new FileInputStream("D://hahahahha.aa");
     89     }
     90     
     91     
     92     @Transactional(propagation=Propagation.REQUIRES_NEW)
     93     public void updatePrice(String isbn,int price){
     94         bookDao.updatePrice(isbn, price);
     95     }
     96     
     97     
     98     
     99     /**
    100      * 根据业务的特性;进行调整
    101      * isolation=Isolation.READ_UNCOMMITTED:读出脏数据
    102      * 
    103      * 
    104      *         READ_COMMITTED;实际上业务逻辑中用的最多的也是这个;
    105      *         REPEATABLEP_READ;
    106      * @param isbn
    107      * @return
    108      */
    109     @Transactional(readOnly=true)
    110     public int getPrice(String isbn){
    111         return bookDao.getPrice(isbn);
    112     }
    113     
    114     
    115     @Transactional
    116     public void mulTx(){
    117         
    118         //ioc.getBean("BookSerice");
    119         checkout("Tom", "ISBN-001");
    120         
    121         updatePrice("ISBN-002", 998);
    122         
    123         int i=10/0;
    124     }
    125 }

    MulService.java:

     1 package com.atguigu.service;
     2 
     3 import org.springframework.beans.factory.annotation.Autowired;
     4 import org.springframework.stereotype.Service;
     5 import org.springframework.transaction.annotation.Transactional;
     6 
     7 @Service
     8 public class MulService {
     9     
    10     @Autowired
    11     private BookService bookService;
    12     
    13     @Transactional
    14     public void mulTx(){
    15         //都是可以设置的;
    16         //传播行为来设置这个事务方法是不是和之前的大事务共享一个事务(使用同一条连接);
    17         //REQUIRED  
    18         bookService.checkout("Tom", "ISBN-001");
    19         
    20         //REQUIRED   REQUIRES_NEW
    21         bookService.updatePrice("ISBN-002", 998);
    22         
    23         //int i = 10/0;
    24     }
    25 
    26 }

    tx.xml:

     1 <?xml version="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"
     5     xmlns:tx="http://www.springframework.org/schema/tx"
     6     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
     7         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
     8         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
     9 
    10     <context:component-scan base-package="com.atguigu"></context:component-scan>
    11 
    12 
    13     <!-- 0、引入外部配置文件 -->
    14     <context:property-placeholder location="classpath:dbconfig.properties" />
    15     <!-- 1、配置数据源 -->
    16     <bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    17         <property name="user" value="${jdbc.user}"></property>
    18         <property name="password" value="${jdbc.password}"></property>
    19         <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
    20         <property name="driverClass" value="${jdbc.driverClass}"></property>
    21     </bean>
    22     <!-- 2、配置JdbcTemplate操作数据库   value="#{pooledDataSource}"  ref="pooledDataSource"-->
    23     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    24         <property name="dataSource" value="#{pooledDataSource}"></property>
    25     </bean>
    26     
    27     <!-- 3、配置声明式事务
    28         1)、Spring中提供事务管理器(事务切面),配置这个事务管理器
    29         2)、开启基于注解的事务式事务;依赖tx名称空间
    30         3)、给事务方法加注解
    31      -->
    32      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    33          <property name="dataSource" ref="pooledDataSource"></property>
    34      </bean>
    35      
    36      <tx:annotation-driven transaction-manager="transactionManager"/>
    37 
    38 
    39 </beans>

    TxTest.java:

     1 package com.atguigu.test;
     2 import java.io.FileNotFoundException;
     3 
     4 import org.junit.Test;
     5 import org.springframework.context.ApplicationContext;
     6 import org.springframework.context.support.ClassPathXmlApplicationContext;
     7 
     8 import com.atguigu.service.BookService;
     9 import com.atguigu.service.MulService;
    10 
    11 public class TxTest {
    12     
    13     ApplicationContext ioc = new ClassPathXmlApplicationContext("tx.xml");
    14 
    15     /**
    16      * 有事务的业务逻辑,容器中保存的是这个业务逻辑的代理对象
    17      * @throws FileNotFoundException
    18      * 
    19      * 
    20      * multx(){
    21      *         //REQUIRED
    22      *         A(){
    23      *             //REQUIRES_NEW
    24      *             B(){}
    25      *             //REQUIRED
    26      *             c(){}
    27      *         }
    28      *         
    29      *         //REQUIRES_NEW
    30      *         D(){
    31      *             DDDD()//  REQUIRES_NEW不崩,REQUIRED崩
    32      *             //REQUIRED
    33      *             E(){
    34      *                 //REQUIRES_NEW
    35      *                 F(){
    36      *                     //10/0(E崩,G崩,D崩,A,C崩)
    37      *                 }
    38      *             }
    39      *             //REQUIRES_NEW
    40      *             G(){}
    41      *         }
    42      * 
    43      * 
    44      *         10/0(B成功,D整个分支下全部成功)
    45      *         任何处崩,已经执行的REQUIRES_NEW都会成功;
    46      * 
    47      *         如果是REQUIRED;事务的属性都是继承于大事务的;
    48      *         而propagation=Propagation.REQUIRES_NEW可以调整
    49      *         默认:REQUIRED;
    50      * 
    51      *         REQUIRED:将之前事务用的connection传递给这个方法使用;
    52      *         REQUIRES_NEW:这个方法直接使用新的connection;
    53      * }
    54      * 
    55      */
    56     @Test
    57     public void test() throws FileNotFoundException {
    58         BookService bookService = ioc.getBean(BookService.class);
    59         
    60         //MulService bean = ioc.getBean(MulService.class);
    61         //bean.mulTx();
    62         
    63         //bookService.checkout("Tom", "ISBN-001");
    64         //int price = bookService.getPrice("ISBN-001");
    65         //System.out.println("读取到的数据:"+price);
    66         //System.out.println(bookService.getClass());
    67         
    68         //效果都没改(相当于回滚了),虽然mulTx的两个方法都开新车
    69         //bookService.mulTx();
    70         
    71         System.out.println(bookService.getClass());
    72         //如果是MulService --mulTx()---调用bookService两个方法
    73         //BookService---mulTx()--直接调用两个方法
    74         
    75         /***
    76          * MulServiceProxy.mulTx(){
    77          *         bookServiceProxy.checkout();
    78          *         bookServiceProxy.updatePrice();
    79          * }
    80          * 
    81          * 
    82          * 本类方法的嵌套调用就只是一个事务;
    83          * BookServiceProxy.mulTx(){
    84          *         checkout();
    85          *         updatePrice();
    86          *         //相当于
    87          *         bookDao.updateStock(isbn);
    88          *         int price = bookDao.getPrice(isbn);
    89          *         bookDao.updateBalance(username, price);
    90          * 
    91          *         bookDao.updatePrice(isbn, price);
    92          * }
    93          */
    94         
    95     }
    96 
    97 }

    流程图解:

  • 相关阅读:
    客户(项目)经理应具备那些基本心理素质
    js 添加数组 删除数组
    c# ifram 刷新父页面
    [转].Net ajax检测用户名是否重复
    js 自动执行方法
    js 层 分页显示选择用户名
    c# Cookie的操作
    c# 取IE地址值
    js 弹出层无刷新分页
    iframe 无边框
  • 原文地址:https://www.cnblogs.com/116970u/p/13081650.html
Copyright © 2020-2023  润新知