首页 > 编程语言 >第5章Spring 事务(测试)-Spring的事务注解(小项目中),AspectJ的AOP配置管理事务(大项目中)

第5章Spring 事务(测试)-Spring的事务注解(小项目中),AspectJ的AOP配置管理事务(大项目中)

时间:2022-11-13 00:22:19浏览次数:50  
标签:事务 Spring Transactional 配置管理 spring 注解 默认值 public

第5章Spring 事务(测试)-Spring的事务注解(小项目中),AspectJ的AOP配置管理事务(大项目中)

spring框架中提供的事务处理方案

适合中小项目使用的, 注解方案。

1.适合中小项目使用的, 注解方案。
  spring框架自己用aop实现给业务方法增加事务的功能, 使用@Transactional注解增加事务。
  @Transactional注解是spring框架自己注解,放在public方法的上面,表示当前方法具有事务。
  可以给注解的属性赋值,表示具体的隔离级别,传播行为,异常信息等等

  使用@Transactional的步骤:
  1.需要声明事务管理器对象
    <bean id="xx" class="DataSourceTransactionManager">

1. 使用 Spring 的事务注解管理事务(掌握)

通过@Transactional 注解方式,可将事务织入到相应 public 方法中,实现事务管理。

@Transactional 的所有可选属性如下所示:
➢ propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为Propagation.REQUIRED。
➢ isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为Isolation.DEFAULT。
➢ readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值为 false。
➢ timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为-1,即没有时限。
➢ rollbackFor:指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
➢ rollbackForClassName:指定需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
➢ noRollbackFor:指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
➢ noRollbackForClassName:指定不需要回滚的异常类类名。类型为 String[],默认值为空数组。
	当然,若只有一个异常类时,可以不使用数组。
	需要注意的是,@Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public
方法,如果加上了注解@Transactional,虽然 Spring 不会报错,但不会将指定事务织入到该
方法中。因为 Spring 会忽略掉所有非 public 方法上的@Transaction 注解。
	若@Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。
  • 实现注解的事务步骤:

    1. 声明事务管理器

          <!--使用spring的事务处理-->
          <!--1. 声明事务管理器-->
          <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <!--连接的数据库, 指定数据源-->
              <property name="dataSource" ref="myDataSource" />
          </bean>
      
    2. 开启注解驱动

          <!--2. 开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象
                 transaction-manager:事务管理器对象的id
          -->
          <tx:annotation-driven transaction-manager="transactionManager" />
      
    3. 业务层 public 方法加入事务属性

     @Transactional(
                propagation = Propagation.REQUIRED,
                isolation = Isolation.DEFAULT,
                readOnly = false,
                rollbackFor = {
                        NullPointerException.class,  NotEnoughException.class
                }
    
  • 此时的spring配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:tx="http://www.springframework.org/schema/tx"
           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">
    
        <!--
           把数据库的配置信息,写在一个独立的文件,编译修改数据库的配置内容
           spring知道jdbc.properties文件的位置
        -->
        <context:property-placeholder location="classpath:jdbc.properties" />
    
        <!--声明数据源DataSource, 作用是连接数据库的-->
        <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
              init-method="init" destroy-method="close">
            <!--set注入给DruidDataSource提供连接数据库信息 -->
            <!--    使用属性配置文件中的数据,语法 ${key} -->
            <property name="url" value="${jdbc.url}" /><!--setUrl()-->
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.passwd}" />
            <property name="maxActive" value="${jdbc.max}" />
        </bean>
    
        <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的
            SqlSessionFactory  sqlSessionFactory = new ..
        -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!--set注入,把数据库连接池付给了dataSource属性-->
            <property name="dataSource" ref="myDataSource" />
            <!--mybatis主配置文件的位置
               configLocation属性是Resource类型,读取配置文件
               它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
            -->
            <property name="configLocation" value="classpath:mybatis.xml" />
        </bean>
    
        <!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
            MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。
    
        -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <!--指定SqlSessionFactory对象的id-->
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
            <!--指定包名, 包名是dao接口所在的包名。
                MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
                一次getMapper()方法,得到每个接口的dao对象。
                创建好的dao对象放入到spring的容器中的。 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>
    
        <!--使用spring的事务处理-->
        <!--1. 声明事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!--连接的数据库, 指定数据源-->
            <property name="dataSource" ref="myDataSource" />
        </bean>
    
        <!--2. 开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象
               transaction-manager:事务管理器对象的id
        -->
        <tx:annotation-driven transaction-manager="transactionManager" />
    </beans>
    
  • com.bjpowernode.service.impl.BuyGoodsServiceImpl

    package com.bjpowernode.service.impl;
    
    import com.bjpowernode.dao.GoodsDao;
    import com.bjpowernode.dao.SaleDao;
    import com.bjpowernode.domain.Goods;
    import com.bjpowernode.domain.Sale;
    import com.bjpowernode.excep.NotEnoughException;
    import com.bjpowernode.service.BuyGoodsService;
    import org.springframework.transaction.annotation.Isolation;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    public class BuyGoodsServiceImpl implements BuyGoodsService {
    
        private SaleDao saleDao;
        private GoodsDao goodsDao;
    
        /**
         *
         * rollbackFor:表示发生指定的异常一定回滚.
         *   处理逻辑是:
         *     1) spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性值中
         *         如果异常在rollbackFor列表中,不管是什么类型的异常,一定回滚。
         *     2) 如果你的抛出的异常不在rollbackFor列表中,spring会判断异常是不是RuntimeException,
         *         如果是一定回滚。
         *
         */
       /* @Transactional(
                propagation = Propagation.REQUIRED,
                isolation = Isolation.DEFAULT,
                readOnly = false,
                rollbackFor = {
                        NullPointerException.class,  NotEnoughException.class
                }
        )*/
    
        //使用的是事务控制的默认值, 默认的传播行为是REQUIRED,默认的隔离级别DEFAULT
        //默认抛出运行时异常,回滚事务。
        @Transactional(rollbackFor=Exception.class)
        @Override
        public void buy(Integer goodsId, Integer nums) {
            System.out.println("=====buy方法的开始====");
            //记录销售信息,向sale表添加记录
            Sale sale  = new Sale();
            sale.setGid(goodsId);
            sale.setNums(nums);
            saleDao.insertSale(sale);
    
            //更新库存
            Goods goods  = goodsDao.selectGoods(goodsId);
            if( goods == null){
                //商品不存在
                throw  new  NullPointerException("编号是:"+goodsId+",商品不存在");
            } else if( goods.getAmount() < nums){
                //商品库存不足
                throw new NotEnoughException("编号是:"+goodsId+",商品库存不足");
            }
            //修改库存了
            Goods buyGoods = new Goods();
            buyGoods.setId( goodsId);
            buyGoods.setAmount(nums);
            goodsDao.updateGoods(buyGoods);
            System.out.println("=====buy方法的完成====");
        }
        public void setSaleDao(SaleDao saleDao) {
            this.saleDao = saleDao;
        }
        public void setGoodsDao(GoodsDao goodsDao) {
            this.goodsDao = goodsDao;
        }
    }
    
  • 测试入下:

    1. 正常查询:

    2. 查询一个不存在的一条数据

    3. 购买的数量超过库中的数据

    4. 此时我们在查询一条正常的

2.使用 AspectJ 的 AOP 配置管理事务(掌握)

适合大型项目,有很多的类,方法,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。

  1. Step1:maven 依赖 pom.xml

    新加入 aspectj 的依赖坐标
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.5.RELEASE</version>
    </dependency>
    
  2. Step:在容器中添加事务管理器

        <!--声明式事务处理:和源代码完全分离的-->
        <!--1.声明事务管理器对象-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="myDataSource" />
        </bean>
    
  3. 修改spring配置文件(重点)

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
           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 http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--
           把数据库的配置信息,写在一个独立的文件,编译修改数据库的配置内容
           spring知道jdbc.properties文件的位置
        -->
        <context:property-placeholder location="classpath:jdbc.properties" />
    
        <!--声明数据源DataSource, 作用是连接数据库的-->
        <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
              init-method="init" destroy-method="close">
            <!--set注入给DruidDataSource提供连接数据库信息 -->
            <!--    使用属性配置文件中的数据,语法 ${key} -->
            <property name="url" value="${jdbc.url}" /><!--setUrl()-->
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.passwd}" />
            <property name="maxActive" value="${jdbc.max}" />
        </bean>
    
        <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的
            SqlSessionFactory  sqlSessionFactory = new ..
        -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!--set注入,把数据库连接池付给了dataSource属性-->
            <property name="dataSource" ref="myDataSource" />
            <!--mybatis主配置文件的位置
               configLocation属性是Resource类型,读取配置文件
               它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
            -->
            <property name="configLocation" value="classpath:mybatis.xml" />
        </bean>
    
        <!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
            MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。
    
        -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <!--指定SqlSessionFactory对象的id-->
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
            <!--指定包名, 包名是dao接口所在的包名。
                MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
                一次getMapper()方法,得到每个接口的dao对象。
                创建好的dao对象放入到spring的容器中的。 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>
    
        <!--声明式事务处理:和源代码完全分离的-->
        <!--1.声明事务管理器对象-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="myDataSource" />
        </bean>
    
        <!--2.声明业务方法它的事务属性(隔离级别,传播行为,超时时间)
              id:自定义名称,表示 <tx:advice> 和 </tx:advice>之间的配置内容的
              transaction-manager:事务管理器对象的id
        -->
        <tx:advice id="myAdvice" transaction-manager="transactionManager">
            <!--tx:attributes:配置事务属性-->
            <tx:attributes>
                <!--tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
                    name:方法名称,1)完整的方法名称,不带有包和类。
                                  2)方法可以使用通配符,* 表示任意字符
                    propagation:传播行为,枚举值
                    isolation:隔离级别
                    rollback-for:你指定的异常类名,全限定类名。 发生异常一定回滚
                -->
                <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
                           rollback-for="java.lang.NullPointerException,com.bjpowernode.excep.NotEnoughException"/>
    
                <!--使用通配符,指定很多的方法-->
                <tx:method name="add*" propagation="REQUIRES_NEW" />
                <!--指定修改方法-->
                <tx:method name="modify*" />
                <!--删除方法-->
                <tx:method name="remove*" />
                <!--查询方法,query,search,find-->
                <tx:method name="*" propagation="SUPPORTS" read-only="true" />
            </tx:attributes>
        </tx:advice>
    
        <!--配置aop-->
        <aop:config>
            <!--配置切入点表达式:指定哪些包中类,要使用事务
                id:切入点表达式的名称,唯一值
                expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象
    
                com.bjpowernode.service
                com.crm.service
                com.service
            -->
            <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
    
            <!--配置增强器:关联adivce和pointcut
               advice-ref:通知,上面tx:advice哪里的配置
               pointcut-ref:切入点表达式的id
            -->
            <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt" />
        </aop:config>
    
    
    </beans>
    
  4. 此时达到的效果和上面spring的注解完成的效果是一样的,这里就不测试了。

标签:事务,Spring,Transactional,配置管理,spring,注解,默认值,public
From: https://www.cnblogs.com/atao-BigData/p/16885215.html

相关文章

  • Spring Cloud Loadbalancer
    SpringCloudLoadbalancer---客户端负载均衡器springcloud2020.0.1版本之后删除了eureka中的ribbon,替代ribbon的是springcloud自带的LoadBalancer,但公司开发中并没有那......
  • Spring Cloud Loadbalancer
    SpringCloudLoadbalancer---客户端负载均衡器springcloud2020.0.1版本之后删除了eureka中的ribbon,替代ribbon的是springcloud自带的LoadBalancer,但公司开发中并没有......
  • Spring 事务(测试)--在这个笔记中记录的是没有添加事务,数据库返回的效果。
    第5章Spring事务(测试)--在这个笔记中记录的是没有添加事务,数据库返回的效果。1.首先搞两张表,商品表和订单表举例:购买商品trans_sale项目本例要实现购买商品,模拟用......
  • SpringMVC-解析@ResponseBody
    ServletInvocableHandlerMethod.invokeAndHandle处理完request得到结果后调用returnValueHandlers.handleReturnValue处理返回值。HandlerMethodReturnValueHandlerCompos......
  • spring底层核心概念解析
    1.BeanDefinition包含bean的一些基本元信息,如bean的类型,作用域,初始化方法...等等。申明式的定义,如@Bean,等等<beanclass="com.test.service.UserService"id="userSe......
  • 【SpringBoot】分布式RPC+Zokeeper+SpringBoot练手
    RPCRPC两个核心模块:通讯,序列化序列化:数据传输需要转换DubboApacheDubbo|ˈdʌbəʊ|是一款高性能、轻量级的开源JavaRPC框架,它提供了三大核心能力:面向接口的远程......
  • # SpringBoot 整合 Swagger
    SpringBoot整合Swagger是什么不介绍,这个东西很简单,主要看点底层源码即可,注意:这个东西很容易造成版本错乱的部分引入狂神说的代码段,因为我有些东西懒得写引入<sp......
  • Spring set注入-级联属性赋值
    举例说明:有一个Student类,一个Clazz类。StudentpublicclassStudent{privateStringname;//学生属于哪个班级privateClazzclazz;//使......
  • 设计模式学习(二十四):Spring 中使用到的设计模式
    设计模式学习(二十四):Spring中使用到的设计模式作者:Grey原文地址:博客园:设计模式学习(二十四):Spring中使用到的设计模式CSDN:设计模式学习(二十四):Spring中使用到的设计模式......
  • 【SpringBoot】必须掌握的45个注解
    1、SpringBoot/spring@SpringBootApplication:包含@Configuration、@EnableAutoConfiguration、@ComponentScan通常用在主类上;@Repository:用于标注数据访问组件,即DAO组件......