【Spring】Spring事务

1.事务的概念

事务是一些sql序列的集合, 是多条sql, 作为一个整体执行。

mysql执行事务
beginTransaction 开启事务
insert into student() values.....
select * from student where id=1001
update school set name=xxx where id=1005
endTransaction   事务结束

什么情况下需要使用事务?
一个操作需要多条(2条或2条以上的sql)sql语句一起完成, 操作才能成功。

2.在程序中事务在哪说明

事务:加在业务类的方法上面(public方法上面),表示业务方法执行时,需要事务的支持。

public class AccountService{
    
   private AccountDao dao; 
   private MoneyDao dao2;
   // 在service(业务类)的public方法上面,需要说明事务。
   public void trans(String a, String b, Integer money){
       dao.updateA();
       dao.updateB();
       dao2.insertA();
   }
}


public class AccountDao{
    
    public void updateA(){}
    public void updateB(){}
    
}

public class MoneyDao{
    
    public void insertA(){}
    public void deleteB(){}
    
}

3 事务管理器

3.1 不同的数据库访问技术,处理事务不同

1)使用jdbc访问数据库, 事务处理。

public void updateAccount(){
    Connection conn = ...
    conn.setAutoCommit(false);
    stat.insert()
    stat.update();
    conn.commit();
    con.setAutoCommit(true)
}

2)mybatis执行数据库,处理事务

public void updateAccount(){
    SqlSession session = SqlSession.openSession(false);
    try{
        session.insert("insert into student...");
    	session.update("update school ...");
    	session.commit(); 
    }catch(Exception e){
        session.rollback();
    } 
}
3. 2 spring统一管理事务, 把不同的数据库访问技术的事务处理统一起来。

使用spring的事务管理器,管理不同数据库访问技术的事务处理。
开发人员只需要掌握spring的事务处理一个方案, 就可以实现使用不同数据库访问技术的事务管理。
管理事务面向的是spring, 有spring管理事务、做事务提交、事务回滚。

3.3 Spring事务管理器(接口是重点)

Spring框架使用事务管理器对象,管理所有的事务。
事务管理器接口👉 PlatformTransactionManager
作用: 定义了事务的操作, 主要是commit(), rollback()

事务管理器有很多实现类: 一种数据库的访问技术有一个实现类, 由实现类具体完成事务的提交、回顾。
意味着👉jdbc或者mybatis访问数据库有自己的事务管理器实现类:DataSourceTranactionManager

hibernate框架的事务管理器实现类👉HibernateTransactionManager.

事务管理器工作方式:
在这里插入图片描述

3.4 事务的提交和回滚(理解)的时机
  • 当业务方法正常执行时,没有异常,事务是提交的。
  • 如果业务方法抛出了运行时异常, 事务是回滚的。

异常分类:(理解)

  • Error: 严重错误。 回滚事务。
  • Exception : 异常类,可以处理的异常情况

1)运行时异常: RuntimeException和他的子类都是运行时异常, 在程序执行过程中抛出的异常。 常见的运行时异常: NullPoinerException(空指针) , NumberFormatException(转换异常) , ArithmeticException(算术异常), IndexOutOfBoundsException(数组越界). 👉回滚
2)受查异常 : 编写java代码的时候,必须出来的异常。 例如IOException , SQLException , FileNotFoundException 👉默认提交事务

总结:
方法中抛出了运行时异常, 事务回滚,其他情况(正常执行方法、受查异常)就是默认提交事务。

3.5 事务使用的AOP的环绕通知

环绕通知:可以在目标方法的前和后都能增强功能,不需要修改代码代码

spring给业务方法在执行时,增加上事务的切面功能

@Around("execution(*  所有的业务类中的方法)")
public Object myAround(ProceedingJoinPoint pjp) {
    try{
       PlatformTransactionManager.beginTransaction();//使用spring的事务管理器,开启事务
       pjp.proceed(); //执行目标方法 //doSome()
       PlatformTransactionManager.commit();//业务方法正常执行,提交事务
    }catch(Exception e){ 
        PlatformTransactionManager.rollback();//异常,回滚事务
    }
    
}

5.4 事务定义接口TransactionDefinition (掌握)

TransactionDefinition接口定义了三类常量, 定义了有关事务控制的属性。
事务的属性
1) 隔离级别 2)传播行为 3)事务的超时

要做的事:给业务方法说明事务属性,和ACID不一样。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
用变量来代表后面的整型值。

4.1 隔离级别(掌握)

控制事务之间影响的程度。5个值,只有四个隔离级别
在这里插入图片描述
1)DEFAULT(合适) : 采 用 DB 默 认 的 事 务 隔 离 级 别 。 MySql 的 默 认 为 REPEATABLE_READ; Oracle 默认为 READ_COMMITTED。(一般默认)
2)READ_UNCOMMITTED:读未提交。未解决任何并发问题。
3)READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
4)REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
5)SERIALIZABLE:串行化。不存在并发问题。(安全级别最高)

4.2 超时时间

在这里插入图片描述

以秒为单位 , 整数值,默认是 -1。
超时时间表示一个业务方法最长的执行时间,没有到达时间没有执行完毕,spring回滚事务。

4.3 传播行为(掌握)

传播行为:业务方法在调用时,事务在方法之间的传递和使用,有7个值。
使用传播行为标识方法有无事务
在这里插入图片描述

//三个常用需要掌握
PROPAGATION_REQUIRED //一般用这个
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS

1)REQUIRED:spring默认传播行为, 方法在调用的时候,如果存在事务就是使用当前的事务,如果没有事务,则新建事务, 方法在新事务中执行。
**举例:**有两个业务方法:doSome()和doOther()。若 doSome()方法在调用 doOther()方法时就是在事务内运行的,则 doOther()方法的执行也加入到该事务内执行。若 doSome()方法在调用 doOther()方法时没有在事务内执行,则
doOther()方法会创建一个事务,并在其中执行。
在这里插入图片描述

2)SUPPORTS:支持, 方法有事务可以正常执行,没有事务也可以正常执行。---- 查询操作

3)REQUIRES_NEW:方法需要一个新事务。 如果调用方法时,存在一个事务,则原来的事务暂停,直到新事务执行完毕。 如果方法调用时,没有事务,则新建一个事务,在新事务执行代码。(两个独立的事务,总是在新事务中执行)

PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED

程序举例环境搭建

购买商品 trans_sale 项目
本例要实现购买商品,模拟用户下订单,向订单表添加销售记录,从商品表减
少库存。
实现步骤:
Step0:创建数据库表,创建两个数据库表 sale , goods
在这里插入图片描述
在这里插入图片描述
goods 表数据:
在这里插入图片描述

step1:创建一个maven项目
在这里插入图片描述
step2:加入依赖和插件

  • spring依赖、mybatis依赖、 mysql驱动、 junit依赖
  • mybatis-spring依赖(mybatis网站上提供的,用来在spring项目中,创建mybatis对象)
  • spring有关事务的依赖。
<!--spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

    <!--spring事务依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

    <!--mybatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.1</version>
    </dependency>

    <!--mybatis和spring集成-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>

    <!--mysql驱动-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.9</version>
    </dependency>

    <!--阿里的连接池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.12</version>
    </dependency>

在这里插入图片描述

step3:创建实体类 Sale ,Goods
在这里插入图片描述
在这里插入图片描述

step4:创建dao接口和其mapper文件
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

step5:主配置文件-----mybatis-config.xml(使用模板文件)
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
step6:创建service接口和它的实现类, 实现buy的方法。
在这里插入图片描述
在这里插入图片描述
实现类:

public class BuyGoodsServiceImpl implements BuyGoodsService {

    private SaleDao saleDao;
    private GoodsDao goodsDao;

    public void setSaleDao(SaleDao saleDao) {
        this.saleDao = saleDao;
    }

    public void setGoodsDao(GoodsDao goodsDao) {
        this.goodsDao = goodsDao;
    }

    @Override
    public void buy(Integer goodsId, Integer num) {
        System.out.println("====buy方法的开始====");

        //生成销售记录
        Sale sale  = new Sale();
        sale.setGid(goodsId);
        sale.setNum(num);
        saleDao.insertSale(sale);

        //查询商品
        Goods goods = goodsDao.selectById(goodsId);
        if( goods == null){
            throw new NullPointerException( goodsId+",商品不存在");
        } else if( goods.getAmount() < num ){
            throw new NotEnougthException(goodsId+",库存不足");
        }

        //更新库存
        Goods buyGoods  = new Goods();
        buyGoods.setId(goodsId);
        buyGoods.setAmount(num);
        goodsDao.updateGoods(buyGoods);
        
        System.out.println("====buy方法的完成====");
    }
}

step7:自定义异常类–excetion
在这里插入图片描述

step8:Spring配置文件
在这里插入图片描述

<!--加载外部的属性配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--声明数据源DataSource-->
    <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>


    <!--声明sqlSessionFactoryBean ,在这个类的内部创建SqlSessionFactory-->
    <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--指定数据源-->
        <property name="dataSource" ref="myDataSource"/>
        <!--指定mybatis主配置文件
            Resource可以直接使用 value属性赋值。
        -->
        <property name="configLocation" value="classpath:mybatis.xml"/>

    </bean>


    <!--MapperScannerConfigurer 创建Dao代理-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--指定SqlSessionFactory对象的名称-->
        <property name="sqlSessionFactoryBeanName" value="factory"/>
        <!--指定基本包,dao接口所在的包名-->
        <property name="basePackage" value="com.bjpowernode.dao"/>

    </bean>

    <!--声明service-->
    <bean id="buyService" class="com.bjpowernode.service.impl.BuyGoodsServiceImpl">
        <property name="goodsDao" ref="goodsDao"/>
        <property name="saleDao" ref="saleDao"/>
    </bean>

step9:测试
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果将商品id改成1005:
在这里插入图片描述
在这里插入图片描述

5 Spring框架使用自己的注解@Transactional控制事务

@Transactional注解, 使用注解的属性控制事务(隔离级别,传播行为,超时)
属性:

  1. propagation : 事务的传播行为, 使用的 Propagation类的枚举值。例如 Propagation.REQUIRED
    在IDEA 双击shift
    在这里插入图片描述
    在这里插入图片描述
  2. isolation : 表示隔离级别, 使用Isolation类的枚举值,表示隔离级别。 默认 Isolation.DEFAULT
    在这里插入图片描述
  3. readOnly: boolean类型的值,表示数据库操作是不是只读的。默认是false
  4. timeout: 事务超时,默认是-1, 整数值,单位是秒。 例如 timeout=20
  5. rollbackFor:表示回滚的异常类列表, 它的值是一个数组,每个值是异常类型的class
  6. rollbackForClassName:表示回滚的异常类列表,它的值是异常类名称,是String类型的值
  7. noRollbackFor: 不需要回滚的异常类列表, 是class类型的。
  8. noRollbackForClassName: 不需要回滚的异常类列表,是String类型的值

位置:
1)在业务方法的上面, 是在public方法的上面
2) 在类的上面。

注解的使用步骤:
1)在spring的配置文件,声明事务的内容

  • 声明事务管理器,说明使用哪个事务管理器对象
  • 声明使用注解管理事务, 开启事务注解驱动

2)在类的源代码中,加入@Transactional.

事务的控制模式:
1.编程式,在代码中编程控制事务。
2. 声明式事务。不用编码

在上面的项目中修改Spring配置文件实现事务控制

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
中间遇到一个小bug:
如果显示applicationContext.xml文件不存在时因为resources文件夹没有标记为resources root

测试:事务生效
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//第一种设置方式
@Transactional(propagation = Propagation.REQUIRED,
	isolation = Isolation.DEFAULT,
	readOnly = false, timeout = 20,
	rollbackFor ={NullPointerException.class,NotEnougthException.class})
//第二种设置方式
@Transactional(propagation = Propagation.REQUIRED,
	isolation = Isolation.DEFAULT,
	readOnly = false, timeout = 20)
//第三种方式: 使用默认值 REQUIRED , 发生运行时异常回滚。
@Transactional
@Override
public void buy(Integer goodsId, Integer num) { }

解释 rollbackFor 的使用:
1)框架首先检查方法抛出的异常是不是在 rollbackFor 的数组中, 如果在一定回滚。
2)如果方法抛出的异步不在 rollbackFor 数组, 框架会继续检查 抛出的异常 是不是 RuntimeException。如果是RuntimeException, 一定回滚。

//抛出 SQLException , IOException
rollbackFor={SQLException.class, IOException.class}

@Transactional使用的特点:
1.spring框架自己提供的事务控制
2.适合中小型项目。
3.使用方便,效率高。

6 使用Aspectj框架在spring的配置文件中,声明事务控制

使用aspectj的aop,声明事务控制叫做声明式事务。不用修改java代码,都在配置文件中进行
声明式事务优特点
1.缺点: 理解难, 配置复杂。
2.优点: 代码和事务配置是分开的。 控制事务源代码不用修改。 能快速的了解和掌控项目的全部事务。 适合大型项目。
使用步骤:
1.pom.xml加入 spring-aspects的依赖

<!--Spring-Aspects依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

2.在spring的配置文件中声明事务的内容
1)声明事务管理器

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

2)声明业务方法需要的事务属性

<!--声明业务方法的隔离属性(隔离级别,传播行为,超时)
        id:给业务方法配置事务段代码起个名称,唯一值
        transaction-manager:事务管理器的id
     -->
    <tx:advice id="serviceAdvice" transaction-manager="transactionManager">
        <!--给具体的业务方法增加事务的说明-->
        <tx:attributes>
            <!--
               给具体的业务方法,说明他需要的事务属性
               name: 业务方法名称。配置name的值: 1. 业务方法的名称; 2. 带有部分通配符(*)的方法名称; 3 使用*
               propagation:指定传播行为的值
               isolation:隔离级别
               read-only:是否只读,默认是false
               timeout:超时时间
               rollback-for:指定回滚的异常类列表,使用的异常全限定名称
            -->
            <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
                       read-only="false" timeout="20"
                       rollback-for="java.lang.NullPointerException,com.bjpowernode.excetion.NotEnougthException"/>
            <!--在业务方法有命名规则后, 可以对一些方法使用事务-->
            <tx:method name="add*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/>
            <tx:method name="modify*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
            <tx:method name="remove*" propagation="REQUIRED" rollback-for="java.lang.Exception" />

            <!--以上方法以外的 * :querySale, findSale, searchSale -->
            <tx:method name="*" propagation="SUPPORTS" read-only="true" />
            
        </tx:attributes>
    </tx:advice>

3)声明切入点表达式

<!--声明切入点表达式: 表示那些包中的类,类中的方法参与事务-->
    <aop:config>
        <!--声明切入点表达式
             expression:切入点表达式, 表示那些类和类中的方法要参与事务
             id:切入点表达式的名称,唯一值

             expression怎么写?业务类都在service包
       -->
        <aop:pointcut id="servicePointcut" expression="execution(* *..service..*.*(..))" />
        
        <!--关联切入点表达式和事务通知-->
        <aop:advisor advice-ref="serviceAdvice" pointcut-ref="servicePointcut" />
    </aop:config>

测试:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值