声明式事务

文章目录

    • 1.事务分类
        • 1.传统方式解决事务
        • 2.声明式事务
    • 2.声明式事务案例
        • 1.需求分析
        • 2.解决方案分析
        • 3.数据表创建
        • 4.编写GoodsDao.java
          • 1.编写配置文件JdbcTemplate_ioc.xml
          • 2.单元测试
        • 5.编写GoodsService.java
        • 6.配置事务管理器JdbcTemplate_ioc.xml
        • 7.进行测试
    • 3.debug事务管理器DataSourceTransactionManager
        • 1.基本步骤解释
          • 1.配置文件
          • 2.注解
          • 3.具体AOP示意图
        • 2.debug出现异常情况
          • 1.下断点
          • 2.执行
          • 3.下一个断点
          • 4.再打一个断点,跳过去
          • 5.下一个断点
          • 6.在con.rollback();打一个断点,跳过去然后下一步
    • 4.声明式事务传播机制
        • 1.基本介绍
        • 2.事务传播机制种类
        • 3.图解
          • 1.默认事务传播机制(REQUIRED)
          • 2.REQUIRES_NEW事务传播机制
          • 3.两种事务传播机制的区别
        • 4.声明式事务传播机制的设置方法
        • 5.应用实例
          • 1.文件目录
          • 2.GoodsDao.java
          • 3.GoodsService.java
          • 4.MultiplyService.java
          • 5.默认传播机制测试
            • 1.故意修改GoodsDao.java第一套的updateBalance方法
            • 2.初始数据表
            • 3.测试代码
            • 4.结果展示
          • 6.REQUIRES_NEW传播机制演示
            • 1.故意修改GoodsDao.java第二套的updateBalance方法(第一套的改回来)
            • 2.修改GoodsService.java的两个事务传播机制
            • 3.修改MultiplyService.java的一个事务传播机制
            • 4.初始数据表
            • 5.测试代码
            • 6.结果展示
            • 7.注意事项
    • 5.事务隔离级别
        • 1.基本介绍
        • 2.应用实例
          • 1.简要介绍
          • 2.结果展示
            • 1.原goods表
            • 2.启动测试程序
            • 3.修改id为1的价格
            • 4.打开断点,查看第二次结果
        • 3.修改事务隔离级别
    • 6.事务超时回滚
        • 1.基本介绍
        • 2.代码实例
          • 1.GoodsService.java的buyGoodsByTxSOLATION方法
          • 2.测试程序
          • 3.原账户表
          • 4.执行测试程序
    • 7.课后练习
        • 1.要求
        • 2.数据表设计
        • 3.代码实现
          • 1.文件目录
          • 2.jdbc.properties
          • 3.txhomework.xml
          • 4.HomeWorkDao.java
          • 5.HomeWorkService.java
          • 6.测试类
          • 7.原数据表
          • 8.执行测试类
            • 1.执行失败
            • 2.执行成功

1.事务分类

1.传统方式解决事务

image-20240223103145604

2.声明式事务

2.声明式事务案例

1.需求分析

image-20240223103404559

2.解决方案分析

image-20240223103746418

image-20240223103759610

3.数据表创建
-- 声明式事务要创建的表

-- 账户表
CREATE TABLE `user_account`(
user_id INT UNSIGNED PRIMARY KEY auto_increment,
user_name VARCHAR(32) NOT NULL DEFAULT'',
money DOUBLE NOT NULL DEFAULT 0.0
);
INSERT INTO `user_account` VALUES(NULL, '张三', 1000);
INSERT INTO `user_account` VALUES(NULL, '李四', 2000);

SELECT * FROM user_account;

-- 商品表
CREATE TABLE `goods`(
goods_id INT UNSIGNED PRIMARY KEY auto_increment,
goods_name VARCHAR(32) NOT NULL DEFAULT'',
price DOUBLE NOT NULL DEFAULT 0.0
);
INSERT INTO goods VALUES(NULL, '小风扇',10.00);
INSERT INTO goods VALUES(NULL, '小台灯',12.00);
INSERT INTO goods VALUES(NULL, '可口可乐',1.00);

SELECT * FROM goods;

-- 商品库存表
CREATE TABLE `goods_amount`(
goods_id INT UNSIGNED PRIMARY KEY auto_increment,
goods_num INT UNSIGNED DEFAULT 0
);
INSERT INTO goods_amount VALUES(NULL, 200);
INSERT INTO goods_amount VALUES(NULL, 20);
INSERT INTO goods_amount VALUES(NULL, 23);

SELECT * FROM goods_amount;
4.编写GoodsDao.java
package com.sxs.spring.tx;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

/**
 * @author 孙显圣
 * @version 1.0
 */
//反射创建bean对象
@Repository
public class GoodsDao {
    //依赖注入
    @Resource
    private JdbcTemplate jdbcTemplate;

    /**
     * 根据商品id返回价格
     *
     * @param id
     * @return
     */
    public Double queryPriceById(Integer id) {
        String sql = "select price from goods where goods_id = ?";
        Double price = jdbcTemplate.queryForObject(sql, Double.class, id);
        System.out.println(id + "号商品价格为" + price);
        return price;
    }

    /**
     * 根据用户名id减少用户余额
     *
     * @param user_id
     * @param money
     */
    public void updateBalance(Integer user_id, Double money) {
        String sql = "update user_account set money = money - ? where user_id = ?";
        int update = jdbcTemplate.update(sql, money, user_id);
        if (update == 1) {
            System.out.println("用户余额减少成功");
        }
    }

    /**
     * 修改商品库存
     *
     * @param goods_id
     * @param amount
     */
    public void updateAmount(Integer goods_id, Integer amount) {
        String sql = "update goods_amount set goods_num = goods_num - ? where goods_id = ?";
        int update = jdbcTemplate.update(sql, amount, goods_id);
        if (update == 1) {
            System.out.println("商品库存减少成功!");
        }
    }

}

1.编写配置文件JdbcTemplate_ioc.xml

image-20240223113504708

2.单元测试
package com.sxs.spring.tx;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

/**
 * @author 孙显圣
 * @version 1.0
 */
//反射创建bean对象
@Repository
public class GoodsDao {
    //依赖注入
    @Resource
    private JdbcTemplate jdbcTemplate;

    /**
     * 根据商品id返回价格
     *
     * @param id
     * @return
     */
    public Double queryPriceById(Integer id) {
        String sql = "select price from goods where goods_id = ?";
        Double price = jdbcTemplate.queryForObject(sql, Double.class, id);
        System.out.println(id + "号商品价格为" + price);
        return price;
    }

    /**
     * 根据用户名id减少用户余额
     *
     * @param user_id
     * @param money
     */
    public void updateBalance(Integer user_id, Double money) {
        String sql = "update user_account set money = money - ? where user_id = ?";
        int update = jdbcTemplate.update(sql, money, user_id);
        if (update == 1) {
            System.out.println("用户余额减少成功");
        }
    }

    /**
     * 修改商品库存
     *
     * @param goods_id
     * @param amount
     */
    public void updateAmount(Integer goods_id, Integer amount) {
        String sql = "update goods_amount set goods_num = goods_num - ? where goods_id = ?";
        int update = jdbcTemplate.update(sql, amount, goods_id);
        if (update == 1) {
            System.out.println("商品库存减少成功!");
        }
    }

}

5.编写GoodsService.java
package com.sxs.spring.tx.service;

import com.sxs.spring.tx.GoodsDao;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * @author 孙显圣
 * @version 1.0
 */

@Service
public class GoodsService {
    @Resource
    private GoodsDao goodsDao;
    
    @Transactional //会将方法中对数据库的操作作为一个事务管理
    public void buyGoodsByTx(Integer userId, Integer goodsId, Integer amount) {
        //得到商品的价格
        Double price = goodsDao.queryPriceById(goodsId);
        //减少用户的余额
        goodsDao.updateBalance(userId, amount * price);
        //减少库存
        goodsDao.updateAmount(goodsId, amount);
    }
}

6.配置事务管理器JdbcTemplate_ioc.xml
    <!--配置事务管理器对象-->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <!--需要与数据源关联,因为操作数据库的jdbcTemplate要与dataSource关联,他们要一致-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--配置启动注解的声明式事务管理功能-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

image-20240223124447662

7.进行测试
    //通过声明式事务管理测试
    @org.junit.jupiter.api.Test
    public void byGoodsByTx() {
        GoodsService bean1 = ioc.getBean(GoodsService.class);
        bean1.buyGoodsByTx(1, 1, 3);
    }

执行前后并没有出现数据不一致

image-20240223124633636

3.debug事务管理器DataSourceTransactionManager

1.基本步骤解释
1.配置文件
  • 首先配置了一个事务管理器对象,绑定与jdbcTemplate一致的数据源

  • 然后启动tx注解,绑定这个事务管理器对象

image-20240223130513649

2.注解
  • 当使用这个注解时,表名这个方法就会被AOP作为一个事务管理

image-20240223130805179

3.具体AOP示意图

image-20240223131706489

2.debug出现异常情况
1.下断点

image-20240223134007045

image-20240223134020750

2.执行

image-20240223134201189

3.下一个断点

image-20240223134248093

4.再打一个断点,跳过去

image-20240223134419259

5.下一个断点

image-20240223134520652

6.在con.rollback();打一个断点,跳过去然后下一步

image-20240223134647924

4.声明式事务传播机制

1.基本介绍

image-20240223134933511

image-20240223134943012

2.事务传播机制种类

image-20240223135400830

3.图解
1.默认事务传播机制(REQUIRED)
  • 可以理解为将方法1和方法2的事务功能去掉了,将方法1和方法2的代码合并交给Tx事务管理
  • 具体例子
    • 方法1出错:方法1和方法2和Tx事务全部回滚
    • 方法2出错:方法1和方法2和Tx事务全部回滚
    • Tx事务出错(除了方法1和方法2的部分):方法1和方法2和Tx事务全部回滚

image-20240223140441185

2.REQUIRES_NEW事务传播机制
  • 可以理解为Tx事务、方法1事务、方法2事务都是各自独立
  • 具体例子
    • 方法1出错:方法1事务回滚
    • 方法2出错:方法2事务回滚
    • Tx事务出错(除了方法1和方法2的部分):Tx事务(除了方法1和方法2的部分)回滚

image-20240223140854861

3.两种事务传播机制的区别

image-20240223141541267

4.声明式事务传播机制的设置方法

image-20240223141421702

5.应用实例
1.文件目录

image-20240223144431435

2.GoodsDao.java
package com.sxs.spring.tx;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

/**
 * @author 孙显圣
 * @version 1.0
 */
//反射创建bean对象
@Repository
public class GoodsDao {
    //依赖注入
    @Resource
    private JdbcTemplate jdbcTemplate;

    //第一套方法

    /**
     * 根据商品id返回价格
     *
     * @param id
     * @return
     */
    public Double queryPriceById(Integer id) {
        String sql = "select price from goods where goods_id = ?";
        Double price = jdbcTemplate.queryForObject(sql, Double.class, id);
        System.out.println(id + "号商品价格为" + price);
        return price;
    }

    /**
     * 根据用户名id减少用户余额
     *
     * @param user_id
     * @param money
     */
    public void updateBalance(Integer user_id, Double money) {
        String sql = "update user_account set money = money - ? where user_id = ?";
        int update = jdbcTemplate.update(sql, money, user_id);
        if (update == 1) {
            System.out.println("用户余额减少成功");
        }
    }

    /**
     * 修改商品库存
     *
     * @param goods_id
     * @param amount
     */
    public void updateAmount(Integer goods_id, Integer amount) {
        String sql = "update goods_amount set goods_num = goods_num - ? where goods_id = ?";
        int update = jdbcTemplate.update(sql, amount, goods_id);
        if (update == 1) {
            System.out.println("商品库存减少成功!");
        }
    }

    //第二套方法

    /**
     * 根据商品id返回价格
     *
     * @param id
     * @return
     */
    public Double queryPriceById2(Integer id) {
        String sql = "select price from goods where goods_id = ?";
        Double price = jdbcTemplate.queryForObject(sql, Double.class, id);
        System.out.println(id + "号商品价格为" + price);
        return price;
    }

    /**
     * 根据用户名id减少用户余额
     *
     * @param user_id
     * @param money
     */
    public void updateBalance2(Integer user_id, Double money) {
        String sql = "update user_account set money = money - ? where user_id = ?";
        int update = jdbcTemplate.update(sql, money, user_id);
        if (update == 1) {
            System.out.println("用户余额减少成功");
        }
    }

    /**
     * 修改商品库存
     *
     * @param goods_id
     * @param amount
     */
    public void updateAmount2(Integer goods_id, Integer amount) {
        String sql = "update goods_amount set goods_num = goods_num - ? where goods_id = ?";
        int update = jdbcTemplate.update(sql, amount, goods_id);
        if (update == 1) {
            System.out.println("商品库存减少成功!");
        }
    }

}

3.GoodsService.java
package com.sxs.spring.tx.service;

import com.sxs.spring.tx.GoodsDao;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * @author 孙显圣
 * @version 1.0
 */

@Service
public class GoodsService {
    @Resource
    private GoodsDao goodsDao;

    //调用第一套方法
    @Transactional //会将方法中对数据库的操作作为一个事务管理
    public void buyGoodsByTx(Integer userId, Integer goodsId, Integer amount) {
        //得到商品的价格
        Double price = goodsDao.queryPriceById(goodsId);
        //减少用户的余额
        goodsDao.updateBalance(userId, amount * price);
        //减少库存
        goodsDao.updateAmount(goodsId, amount);
    }

    //调用第二套方法
    @Transactional //会将方法中对数据库的操作作为一个事务管理
    public void buyGoodsByTx2(Integer userId, Integer goodsId, Integer amount) {
        //得到商品的价格
        Double price = goodsDao.queryPriceById2(goodsId);
        //减少用户的余额
        goodsDao.updateBalance2(userId, amount * price);
        //减少库存
        goodsDao.updateAmount2(goodsId, amount);
    }
}

4.MultiplyService.java
package com.sxs.spring.tx.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * @author 孙显圣
 * @version 1.0
 */
@Service
public class MultiplyService {
    @Resource
    private GoodsService goodsService;

    @Transactional //默认事务传播机制
    public void multiBuyGoodsByTx() {
        //默认事务传播机制
        goodsService.buyGoodsByTx(1,1,1);
        goodsService.buyGoodsByTx2(1,1,1);
    }
}

5.默认传播机制测试
1.故意修改GoodsDao.java第一套的updateBalance方法

image-20240223144741663

2.初始数据表
  • 账户表

image-20240223144946645

  • 库存表

image-20240223144913270

3.测试代码
  • 此时是默认传播机制,所以如果第一套方法出现异常,则第二套方法也会回滚
  • 所以结果应该是数据表没有变化
    //事务传播机制测试
    @org.junit.jupiter.api.Test
    public void multiBuyGoodsByTx() {
        MultiplyService bean1 = ioc.getBean(MultiplyService.class);
        bean1.multiBuyGoodsByTx();
    }
4.结果展示

image-20240223145236270

image-20240223145243679

6.REQUIRES_NEW传播机制演示
1.故意修改GoodsDao.java第二套的updateBalance方法(第一套的改回来)

image-20240223151004668

2.修改GoodsService.java的两个事务传播机制

image-20240223145623684

3.修改MultiplyService.java的一个事务传播机制

image-20240223145648252

4.初始数据表
  • 账户表

image-20240223144946645

  • 库存表

image-20240223144913270

5.测试代码
  • 此时是REQUIRES_NEW传播机制,所以如果第二套方法出现异常,第一套方法不会回滚
  • 所以结果应该是第一套方法执行成功,张三购买一件商品1成功
    //事务传播机制测试
    @org.junit.jupiter.api.Test
    public void multiBuyGoodsByTx() {
        MultiplyService bean1 = ioc.getBean(MultiplyService.class);
        bean1.multiBuyGoodsByTx();
    }
6.结果展示

image-20240223150835997

image-20240223150825770

7.注意事项
  • 第一次我将第一套方法改错了,本来以为第二套方法会正常执行
  • 但是虽然事务是独立的,但是第一套方法报错了后面的就不继续执行了
  • 最外面的事务设置成默认的还是REQUIRES_NEW类型都不会影响里面的两个事务的独立性

5.事务隔离级别

1.基本介绍

image-20240223152325593

2.应用实例
1.简要介绍

image-20240223153156103

2.结果展示
1.原goods表

image-20240223153845855

2.启动测试程序

image-20240223153939973

3.修改id为1的价格

image-20240223154858766

4.打开断点,查看第二次结果

image-20240223154937502

3.修改事务隔离级别

image-20240223155307494

6.事务超时回滚

1.基本介绍

image-20240223155506448

image-20240223155716193

2.代码实例
1.GoodsService.java的buyGoodsByTxSOLATION方法
    @Transactional(timeout = 2) //设置超时回滚时间为2秒
    public void buyGoodsByTxSOLATION(){
        //查询两次商品的价格
        Double aDouble = goodsDao.queryPriceById(1);
        //减少用户的余额
        goodsDao.updateBalance2(1, 1000.0);
        try {
            //设置4秒休眠时间
            Thread.sleep(4000);

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        Double aDouble1 = goodsDao.queryPriceById(1);
        System.out.println(aDouble1);
    }
2.测试程序
    @org.junit.jupiter.api.Test
    public void buyGoodsByTxSOLATION() {
        GoodsService bean1 = ioc.getBean(GoodsService.class);
        bean1.buyGoodsByTxSOLATION();
    }
3.原账户表
  • 如果没有回滚则money应该减少1000

image-20240223160316654

4.执行测试程序

image-20240223160528891

image-20240223160545038

7.课后练习

1.要求

image-20240223160756954

2.数据表设计
-- seller
CREATE TABLE seller (
seller_id INT UNSIGNED PRIMARY KEY auto_increment,
seller_name VARCHAR(32) NOT NULL DEFAULT'',
money DOUBLE NOT NULL DEFAULT 0.0
);

INSERT INTO seller VALUES(NULL, '李白', 100.0)
SELECT * FROM seller


-- buyer
CREATE TABLE buyer (
buyer_id INT UNSIGNED PRIMARY KEY auto_increment,
buyer_name VARCHAR(32) NOT NULL DEFAULT'',
money DOUBLE NOT NULL DEFAULT 0.0
);

INSERT INTO buyer VALUES(NULL, '杜甫', 100.0)
SELECT * FROM buyer


-- goods
CREATE TABLE goods (
goods_id INT UNSIGNED PRIMARY KEY auto_increment,
goods_name VARCHAR(32) NOT NULL DEFAULT'',
price DOUBLE NOT NULL DEFAULT 0.0,
goods_num INT UNSIGNED DEFAULT 0
);


INSERT INTO goods VALUES(NULL, 100.0, 10, '酒')
SELECT * FROM goods


-- taoBao
CREATE TABLE taoBao (
taoBao_id INT UNSIGNED PRIMARY KEY auto_increment,
goods_name VARCHAR(32) NOT NULL DEFAULT'',
money DOUBLE NOT NULL DEFAULT 0.0
)

SELECT * FROM taoBao

3.代码实现
1.文件目录

image-20240223185143537

2.jdbc.properties
jdbc.user=root
jdbc.pwd=root
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
3.txhomework.xml
<?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">
    <!--容器扫描-->
    <context:component-scan base-package="com.sxs.spring.tx"/>


    <!--数据库配置-->
    <!--引入外部配置文件,读取数据源信息-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--配置数据源-->
    <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.pwd}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="driverClass" value="${jdbc.driver}"/>
    </bean>
    <!--配置JdbcTemplate对象-->
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <!--配置数据源属性-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--配置事务管理器对象-->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--启动事务管理器注解-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>
4.HomeWorkDao.java
package com.sxs.spring.tx.homework.dao;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

/**
 * @author 孙显圣
 * @version 1.0
 */
@Repository
public class HomeWorkDao {
    @Resource
    private JdbcTemplate jdbcTemplate;

    //根据卖家id增加money
    public int addMoneyBySellerId(Integer seller_id, Double money) {
        String sql = "update seller set money = money + ? where seller_id = ?";
        return jdbcTemplate.update(sql, money, seller_id);
    }
    //根据买家id减少money
    public int subMoneyByBuyerId(Integer buyer_id, Double money) {
        String sql = "update buyer set money = money - ? where buyer_id = ?";
        return jdbcTemplate.update(sql, money, buyer_id);
    }
    //根据id减少商品库存量
    public int subGoodsNumByGoodsId(Integer goods_id, Integer goods_num) {
        String sql = "update goods se goods_num = goods_num - ? where goods_id = ?";
        return jdbcTemplate.update(sql, goods_num, goods_id);
    }
    //根据id查找商品价格
    public Double findPriceById(Integer goods_id) {
        String sql = "select price from goods where goods_id = ?";
        //public <T> T queryForObject(String sql, @Nullable Object[] args, Class<T> requiredType)
        //传入sql, Object[]填充sql的值,Class<T> requiredType传入要返回的类型
        Double aDouble = jdbcTemplate.queryForObject(sql, new Object[]{goods_id}, Double.class);
        return aDouble;
    }
    //给taoBao添加一条记录
    public int addtoTaoBao(Double money) {
        String sql = "insert into taoBao values(?, ?, ?)";
        return jdbcTemplate.update(sql, null, "name", money);
    }
}

5.HomeWorkService.java
package com.sxs.spring.tx.homework.service;

import com.sxs.spring.tx.homework.dao.HomeWorkDao;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * @author 孙显圣
 * @version 1.0
 */
@Service
public class HomeWorkService {
    @Resource
    private HomeWorkDao homeWorkDao;
    //买家买卖家的商品
    @Transactional //启动事务管理器
    public boolean getGoods(Integer seller_id, Integer buyer_id, Integer goods_id) {
        try {
            //1.根据id查找商品价格
            Double priceById = homeWorkDao.findPriceById(goods_id);
            //2.计算给淘宝的money和卖家得到的money
            Double taobao_money = priceById * 0.1;
            Double seller_money = priceById * 0.9;
            //3.根据id修改卖家的money
            homeWorkDao.addMoneyBySellerId(seller_id, seller_money);
            //4.根据id修改买家的money
            homeWorkDao.subMoneyByBuyerId(buyer_id, priceById);
            //5.增加taobao表的记录
            homeWorkDao.addtoTaoBao(taobao_money);
            //6.减少库存
            homeWorkDao.subGoodsNumByGoodsId(goods_id, 1);
        } catch (Exception e) {
            System.out.println("购买商品逻辑出现问题!");
            throw new RuntimeException(e);
        }
        return true;

    }
}

6.测试类
package com.sxs.spring.tx.homework;

import com.sxs.spring.tx.homework.service.HomeWorkService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class Test {
    public static void main(String[] args) {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("txhomework.xml");
        HomeWorkService bean = ioc.getBean(HomeWorkService.class);
        bean.getGoods(1, 1, 1);
    }
}

7.原数据表
  • seller

image-20240223185442943

  • buyer

image-20240223185448553

  • goods

image-20240223185454120

  • taoBao

image-20240223185500052

8.执行测试类
  • 如果执行成功
    • 李白money = 190
    • 杜甫money = 0
    • goods表goods_num = 7
    • taoBao表有一条新记录
  • 如果执行失败
    • 事务回滚
1.执行失败

image-20240223185843899

2.执行成功

image-20240223190012328

image-20240223190029665

image-20240223190035549

image-20240223190041325

image-20240223190047393

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/565030.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HubSpot流量转化:从访客到客户的转化策略

在当今数字化时代&#xff0c;企业营销获客的关键在于如何将网站访客转化为实际客户。作为HubSpot的合作伙伴&#xff0c;我们深知HubSpot软件在流量转化方面的强大功能。今天运营坛将带领大家深入探讨HubSpot流量转化的核心原理&#xff0c;并介绍如何利用个性化营销策略、构建…

实验2 NFS部署和配置

一、实训目的 1.了解NFS基本概念 2.实现NFS的配置和部署 二、实训准备 1.准备一台能够安装OpenStack的实验用计算机&#xff0c;建议使用VMware虚拟机。 2.该计算机应安装CentOS 7&#xff0c;建议采用CentOS 7.8版本。 3.准备两台虚拟机机&#xff08;客户机和服务器机&…

在React Router 6中使用useRouteLoaderData钩子获取自定义路由信息

在 React Router 6 中怎么像vueRouter一样&#xff0c;可以在配置路由的时候&#xff0c;定义路由的元信息(附加信息)&#xff1f;答案是可以的。稍有些复杂。核心是通过为每个路由定义了一个 loader 函数,用于返回自定义的路由信息&#xff0c;然后通过useRouteLoaderData 钩子…

机器人实验室LAAS-CNRS介绍

一、LAAS-CNRS介绍 1、缩写介绍 同样的&#xff0c;给出英文缩写的全称&#xff0c;以便理解。这里的LAAS&#xff08;Laboratory for Analysis and Architecture of Systems&#xff09;指法国的系统分析与架构实验室&#xff0c;CNRS&#xff08;Centre National de la Rec…

docker容器内ping外网能通,curl不通

排查原因是因为&#xff0c;在服务器上查看ifconfig&#xff0c;显示docker0的mtu是1500&#xff0c;网卡的mtu是1450。 mtu是指在网络通信中能够承载的最大数据包大小。一般情况下&#xff0c;docker的mtu默认为1500字节。 然而&#xff0c;不同的网络设备和网络配置可能会导…

Web3安全性:保护去中心化应用和用户的最佳实践

引言 随着Web3和去中心化应用&#xff08;DApps&#xff09;的迅速发展&#xff0c;我们进入了一个充满无限可能性的新世界。然而&#xff0c;这个数字天堂也伴随着一系列复杂的安全挑战。本文将深入探讨这些挑战&#xff0c;并提供一系列实用的安全建议&#xff0c;帮助你在W…

C++初阶学习第二弹——C++入门(下)

C入门&#xff08;上&#xff09;&#xff1a;C初阶学习第一弹——C入门&#xff08;上&#xff09;-CSDN博客 目录 一、引用 1.1 引用的实质 1.2 引用的用法 二、函数重载 三、内敛函数 四、auto关键字 五、总结 前言&#xff1a; 在上面一章我们已经讲解了C的一些基本…

深度剖析图像处理—边缘检测

什么是边缘检测 边缘检测(Edge Detection)就是提取图像中的边缘点(Edge Point)。边缘点是与周围像素相比灰度值有阶跃变化或屋顶状变化的像素。边缘常存在于目标与背景之间、目标与目标之间、目标与其影子之间。 ​ 在图像处理和图像分析中&#xff0c;经常要用到边缘(Edge)、边…

【学习】对于加密接口、签名接口如何进行性能测试

随着科技的飞速发展&#xff0c;加密接口和签名接口在我们的日常生活中扮演着越来越重要的角色。从在线支付到信息安全&#xff0c;它们始终默默地守护着我们的数字世界。然而&#xff0c;随着应用场景的不断扩展&#xff0c;性能测试变得尤为重要。今天&#xff0c;让我们一起…

单例模式与反射创建对象

单例模式 饿汉式单例模式 单例模式&#xff0c;就是自己先把自己创建了&#xff0c;整个程序都只有这一个实例&#xff0c;别人都没有办法创建实例&#xff0c;因为他的构造方法是private的 一次性把全部都创建了 public class HungryMan {private static int [][] s new …

[lesson48]同名覆盖引发的问题

同名覆盖引发的问题 父子间的赋值兼容 子类对象可以当做父类对象使用(兼容性) 子类对象可以直接赋值给父类对象(<font color>兼容性)子类对象可以直接初始化父类对象父类指针可以直接指向子类对象父类引用可以直接引用子类对象 当使用父类指针(引用)指向子类对象时 子类…

安装zabbix server

目录 1、实验环境 2、yum 安装zabbix server 2.1 解决权限问题和放行流量 2.2 安装zabbix-server 1、实验环境 操作系统rhel8zabbix6.0TLS数据库mysql8.0.30IP地址192.168.81.131时间配置NTP时间服务器同步 2、yum 安装zabbix server 如果通过yum源安装&#xff0c;操作系…

《ElementUI 基础知识》png 图片扩展 icon用法

前言 UI 设计给的切图是 .png 格式。但想与 Element UI icon 用法类似&#xff0c;方案如下。 实现 步骤一 准备图片 步骤二 新建文件&#xff0c;可使用 CSS 预处理语言 styl 或 scss。 stylus 方式 文件 icon.styl /* 定义一个混合 */ cfgIcon(w, h) {display: inlin…

滑动窗口做题思路

什么是滑动窗口&#xff1f;就是一个队列,然后通过在这个队列中的各种移除和添加满足题目需求 题目: 209. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; class Solution {public int minSubArrayLen(int target, int[] nums) {int left 0;int sum 0;int n nu…

面向对象设计与分析40讲(25)中介模式、代理模式、门面模式、桥接模式、适配器模式

文章目录 门面模式代理模式中介模式 之所以把这几个模式放到一起写&#xff0c;是因为它们的界限比较模糊&#xff0c;结构上没有明显的差别&#xff0c;差别只是语义上。 这几种模式在结构上都类似&#xff1a; 代理将原本A–>C的直接调用变成&#xff1a; A–>B–>…

Java面试八股之marshalling和demarshalling

marshalling和demarshalling Marshalling&#xff08;序列化&#xff09;是将内存中的对象状态转化为适合传输或存储的格式&#xff08;如字节流、JSON、XML&#xff09;&#xff0c;以便进行网络通信、持久化存储或跨平台/语言交互操作。Demarshalling&#xff08;反序列化&a…

spring aop介绍

Spring AOP&#xff08;面向切面编程&#xff09;是一种编程范式&#xff0c;它允许开发者将横切关注点&#xff08;cross-cutting concerns&#xff09;从业务逻辑中分离出来&#xff0c;从而提高代码的模块化。在传统的对象导向编程中&#xff0c;这些横切关注点&#xff0c;…

【分治】Leetcode 颜色分类

题目讲解 75. 颜色分类 这道题的本质就是数组分三块 算法讲解 使用三个指针&#xff0c;i遍历数组&#xff0c;left标记0的最右侧&#xff0c;right标记2的最左侧 如果当前的nums[i] 0,我们就让nums[left] 和 nums[i]位置上的数字做交换&#xff0c;这里的i是可以向前移…

基于51单片机的宠物自动喂食语音播报,有实物

1. 51仿真&#xff1a; LCD第一屏显示食物重量&#xff0c;当前时间&#xff0c;温湿度。第二屏显示喂食时间&#xff0c;第三屏显示喂食重量。可通过点击查看喂食时间翻转屏幕显示。 点击查看喂食时间后&#xff0c;显示喂食时间&#xff0c;可以设置三个时间&#xff0c;再点…

【做一名健康的CSDNer】程序员哪几种行为最伤肾(程序员必看)

虽然没有专门针对程序员这一职业群体特有的伤肾行为的研究报道&#xff0c;但根据一般人群的健康风险和生活习惯&#xff0c;程序员由于其特殊的工作模式和环境&#xff0c;可能更容易出现如下伤肾的行为&#xff1a; 熬夜加班&#xff1a; 程序员由于项目进度、bug修复等原因&…