美文网首页
5.2模拟电商示例--第5章Spring事务

5.2模拟电商示例--第5章Spring事务

作者: 努力学习的lfk | 来源:发表于2021-08-24 10:36 被阅读0次

Step0:新建数据库和表

电商实例-新建表sale
电商实例-新建表goods

Step1:maven依赖pom.xml

  <!--jdk属性信息-->
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>

    <!--单元测试-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <!--spring依赖-核心ioc-->
    <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>8.0.23</version>
    </dependency>

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

  </dependencies>


  <build>

    <!--加入maven插件,编译时扫描src/main/java目录中的xml文件-->
    <resources>
      <resource>
        <directory>src/main/java</directory><!--所在的目录-->
        <includes><!--包括目录下的.properties、xml文件都会扫描到-->
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
      </resource>
    </resources>

    <!--指定jdk的版本-->
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>

  </build>

Step2:创建实体类

public class Sale {
    private Integer id;
    private Integer gid;
    private Integer nums;
    创建set和get方法
}
public class Goods {
    private Integer id;
    private String  name;
    private Integer amount;
    private Float price;
    创建set和get方法
}

Step3:定义dao接口

public interface SaleDao {
    //增加销售记录
    int insertSale(Sale sale);
}
public interface GoodsDao {
    //更行库存
    //表示本次用户购买的商品信息,id、购买数量
    int updateGoods(Goods goods);
    //查询商品的信息
    Goods selectGoods(Integer id);
}

Step4:定义dao接口对应的sql映射文件

SaleDao.xml
<!--唯一值的,可以自定义,但要求:使用dao接口的全限定名称。(包括包名、类名)-->
<mapper namespace="com.bjpowernode.dao.SaleDao">
    <insert id="insertSale">
        insert into sale(gid,nums) values (#{gid},#{nums})
    </insert>
</mapper>
GoodsDao.xml
<!--唯一值的,可以自定义,但要求:使用dao接口的全限定名称。(包括包名、类名)-->
<mapper namespace="com.bjpowernode.dao.GoodsDao">
    <select id="selectGoods" resultType="com.bjpowernode.domain.Goods">
        select id,name,amount,price from goods where id=#{gid}
    </select>

    <update id="updateGoods">
        update goods set amount = amount - #{amount} where id=#{id}
    </update>
</mapper>

Step5:定义异常类

package com.bjpowernode.excep;
//自定义的运行时异常,继承RuntimeException
public class NotEnoughException extends RuntimeException{
    //Ctrl+O键重写有参和无参的构造方法
    public NotEnoughException() {
        super();
    }

    public NotEnoughException(String message) {
        super(message);
    }
}

Step6:定义Service接口

package com.bjpowernode.service;

public interface BuyGoodsService {
    //购买商品的方法,goodsId购买商品的id,nums购买商品的数量
    void buy(Integer goodsId,Integer nums);
}

Step7:定义Service的实现类

public class BuyGoodsServiceImpl implements BuyGoodsService {

    //因为以下两个功能需要dao的支持,故需要声明dao
    private SaleDao saleDao;
    private GoodsDao goodsDao;
    //需要set方法完成属性赋值
    public void setSaleDao(SaleDao saleDao) {
        this.saleDao = saleDao;
    }
    public void setGoodsDao(GoodsDao goodsDao) {
        this.goodsDao = goodsDao;
    }

    @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方法的结束=====");
    }
}

Step8:修改spriong配置文件的内容

<!--
        把数据库的配置信息,写在一个独立的文件,编译修改数据库的配置内容
        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}" />
      <property name="username" value="${jdbc.username}" />
      <property name="password" value="${jdbc.password}" />
      <property name="maxActive" value="${jdbc.maxActive}"/>
    </bean>


    <!--声明mybatis中所提供能SqlSessionFactoryBean类,
    这个类的内部是创建SqlSessionFactory的-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--set注入,把数据库的连接池赋给dataSource属性-->
        <property name="dataSource" ref="myDataSource"/>
        <!--value:mybatis主配置文件的位置
            configLocation是Resource类型的
        -->
        <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的容器中。-->
        <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:定义测试类

public class MyTest {
    @Test
    public void test01(){
        //读取配置文件
        String config="applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //获取service对象
        BuyGoodsService service= (BuyGoodsService) ctx.getBean("buyService");
        //调用buy该方法
        service.buy(1001,10);
    }
}

Step10:aop给已经存在的代码额外增加事务功能

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

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

spring框架用aop功能实现给业务方法增加事务的功能,使用@Transactional注解增加事务。
@Transactional注解是spring框架的注解,放在public方法的上面,表示当前方法具有事务。可以给注解的属性赋值,表示具体的隔离级别、传播行为、异常信息等。
@Transactional的所有可选属性如下所示:
(1)propagation:用于设置事务传播属性。该属性类型为Propagation枚举,默认值为Propagation.REQUIRED。
(2))isolation:用于设置事务的隔离界别。该属性类型为Isolation枚举,默认值为Isolation.DEFAULT。
(3)readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为boolean,默认值为false。
(4)timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为inr,默认值为-1,即没有时限。
(5)rollbackFor:指定需要回滚的异常类。类型为Class[],默认值为空数组。当然,若只有一个异常类时,可以不适用数组。
(6)rollbackForClassName:指定需要回滚的异常类类名。类型为String[],默认值为空数组。

使用@Transactional的步骤:

(1)需要声明事务管理器对象
<bean id="xx" class="DataSourceTransactionManager">

<!--在applicationContext.xml文件-->
    <!--使用spring的事务处理-->
    <!--1.声明事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--连接的数据库,指定数据源-->
        <property name="dataSource" ref="myDataSource"/>
    </bean>

(2)开启事务注解驱动,告诉pring框架使用注解的方式管理事务。
spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务的功能。
(使用aop的环绕通知,在业务方法执行前,先开启事务,在业务方法之后提交或回滚事务)
@Around("要增加的事务功能的业务方法名")
object myAround(){
//spring开启事务
try{
buy(1001,10);
spring的事务管理器.commit();
}catch(Exception e){
spring的事务管理器.rollback();
}
}

    <!--2.开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象
          transaction-manager:事务管理器对象的id
    -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
开启事务注解驱动
annotation-ariven选错时-删除约束文件即可

(3)在方法的上面加入@Transactional

    /*
    *   rollbackFor:表示发生指定的异常一定回滚
    *   处理逻辑:
    *       1)spring框架先检查方法抛出的异常是不是在rollbackFor的属性值中,
    *          如果异常在rollbackFor列表中,则回滚。
    *       2)如果抛出的异常不在rollbackFor列表中,spring会判断异常是不是RuntimeException,
    *          如果是,则回滚。
    * */
    @Transactional(
            propagation = Propagation.REQUIRED,
            isolation = Isolation.DEFAULT,
            readOnly = false,
            rollbackFor = {
                    NullPointerException.class,
            }
    )

    /*
    *   工作中常用使用默认值做事务
    * */
 @Transactional

使用@Transactional注解

2.在大型项目中使用AspectJ的AOP配置管理事务

实现步骤:都是在xml配置文件中实现。
1)使用AspectJ框架,需要加入依赖

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

2)声明事务管理器对象
<bean id="xx" class="DataSourceTransactionManager">

<!--声明式事务处理:和源代码完全分离的-->
    <!--1.声明事务管理器对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
         <!--声明数据源-->
        <property name="dataSource" ref="myDataSource"/>
    </bean>

3)声明方法需要的事务类型(配置方法的事务属性[隔离级别、传播行为、超时])

使用aspectJ开启事务注册驱动
advice选错时-删除约束文件即可

4)配置方法的事务属性

    <!--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:attributes>
    </tx:advice>
tx:method使用通配符指定多个方法

spring框架进行事务匹配的顺序:
先找完全匹配方法名,如上图 <tx:method name="buy"
再找带有通配符的,如上图<tx:method name="add* "
最后找只有通配符的,如上图<tx:method name=" * "

5)配置aop:指定哪些类要创建代理。

    <!--配置aop-->
    <aop:config>
        <!--配置切入点表达式:指定哪些包中类要使用事务
            id:切入点表达式的名称,唯一值
            expression:切入点表达式,指定哪些类要使用事务,aspectJ会创建代理对象
            *..service..*.*(..)表示:任意包及子包下的service包及子包的任意类的任意参数的任意方法
        -->
        <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>

        <!--配置增强器:关联advice和pointcut
                advice-ref:通知,上面tx:advice的配置
                pointcut-ref:切入点表达式的id
        -->
        <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
    </aop:config>

笔记来源:B站动力节点 spring学习视频

视频链接:https://www.bilibili.com/video/BV1nz4y1d7uy

相关文章

网友评论

      本文标题:5.2模拟电商示例--第5章Spring事务

      本文链接:https://www.haomeiwen.com/subject/tgaxvltx.html