SpringBoot2.0+MySQL事务回滚/行级锁实现修改商品下单减库存业务

举报
写程序的小王叔叔 发表于 2022/05/15 13:37:51 2022/05/15
【摘要】 主页:小王叔叔的博客

主页写程序的小王叔叔的博客

支持:点赞👍关注✔️收藏💖


目录

一、效果/要求/目的

二、基础知识/原理

三、实现方式/应用/环境配置


  • 一、效果/要求/目的

    • 1.目的/解决并发商品库存超卖问题
    • 2.要求
    • 3.效果
  • 二、基础知识/原理

    • 1)springboot2.0基础知识,
    • 2)MySQL锁:
  • 三、实现方式/应用/环境配置

    • 3、环境配置:

一、效果/要求/目的

1.目的/解决并发商品库存超卖问题:

       实现电商项目中,由于多个用户某一时间段同时下单时,解决购买同一种商品情况下,扣减库存为负的问题。

2.要求:

  创建数据库时,mysql的数据库要求:

 1 )MySQL表引擎Innodb

                                  
2 )SpringBoot2.0+ 开启事务

springboot2.0项目在业务接口中类头出开启事务监控模式(@Transactional)
 3 )压测工具Apache Jmeter

  java环境下安装压力测试(资源链接:https://download.csdn.net/download/qq_31653405/12346485

               

 4)测试工具Postman

 java环境下安装工具:(资源地址:postman.zip-Java工具类资源-CSDN下载

3.效果


二、基础知识/原理

1)springboot2.0基础知识,jpa使用,a.yml,配置,事务处理,异常回滚等

2)MySQL锁:

          实验环境:mysql5.6

          存储引擎:InnoDB

          使用锁:行级锁

【扩展】:

- MyISAM:它不支持事务,也不支持外键,尤其是访问速度快,对事务完整性没有要求或者以SELECT、INSERT为主的应用基本都可以使用这个引擎来创建表。

- InnoDB :InnoDB是一个健壮的事务型存储引擎,这种存储引擎已经被很多互联网公司使用,为用户操作非常大的数据存储提供了一个强大的解决方案。InnoDB就是作为默认的存储引擎。InnoDB还引入了行级锁定和外键约束,在以下场合下,使用InnoDB是最理想的选择:

          1.更新密集的表。InnoDB存储引擎特别适合处理多重并发的更新请求。
          2.事务。InnoDB存储引擎是支持事务的标准MySQL存储引擎。
          3.自动灾难恢复。与其它存储引擎不同,InnoDB表能够自动从灾难中恢复。
          4.外键约束。MySQL支持外键的存储引擎只有InnoDB。
          5.支持自动增加列AUTO_INCREMENT属性。

       一般来说,如果需要事务支持,并且有较高的并发读取频率,InnoDB是不错的选择

- MEMORY:使用MySQL Memory存储引擎的出发点是速度。为得到最快的响应时间,采用的逻辑存储介质是系统内存。虽然在内存中存储表数据确实会提供很高的性能,但当mysqld守护进程崩溃时,所有的Memory数据都会丢失。获得速度的同时也带来了一些缺陷。它要求存储在Memory数据表里的数据使用的是长度不变的格式,这意味着不能使用BLOB和TEXT这样的长度可变的数据类型,VARCHAR是一种长度可变的类型,但因为它在MySQL内部当做长度固定不变的CHAR类型,所以可以使用。

MySQL锁:[ 【mysql 当前锁 查看】-博文推荐-CSDN博客 ----------- 参考博客]

行锁(Record Locks) 间隙锁(Gap Locks) 临键锁(Next-key Locks)共享锁/排他锁(Shared and Exclusive Locks)

意向共享锁/意向排他锁(Intention Shared and Exclusive Locks)

插入意向锁(Insert Intention Locks)自增锁(Auto-inc Locks)


 


三、实现方式/应用/环境配置

1、java+maven环境配置(省略)

2、创建springboot2.0+web项目(本博客使用下单减库存模块的代码)

3、环境配置:

     1)spingboot2.0.xml配置文件

########################################################
### tomcat  配置
########################################################
# 监听端口
server.port=8090
server.address=0.0.0.0
# tomcat最大线程数,默认为200
#server.tomcat.max-threads=800
# tomcat的URI编码
server.tomcat.uri-encoding=UTF-8
########################################################
###datasource 数据库 配置MySQL数据源
########################################################## 
spring.datasource.url=
jdbc:mysql://localhost:3306/vcoo_fresh_store?useOldAliasMetadataBehavior=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
spring.datasource.username=root
spring.datasource.password=root

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.tomcat.max-active=1000
spring.datasource.tomcat.max-idle=2000
spring.datasource.tomcat.initialSize=1000
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.show_sql=true
########################################################
### Java Persistence Api 自动进行建表
########################################################
# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sqlquery
spring.jpa.show-sql = true
# hibernate ddl auto (create,create-drop, update)
spring.jpa.hibernate.ddl-auto = update
# stripped before adding them tothe entity manager)
#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL55Dialect
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect

【扩展】:

      数据库方言:MySQL5Dialect 、MySQL55Dialect区别:

                   参考 [ 关于数据库方言MySQLDialectt ] 

     2)springboot jpa代码实现

           ①controller.java  ----- CustomerOrderController.java

@CrossOrigin
@RestController
@RequestMapping("/CustomerOrder")
public class CustomerOrderController {

	@Autowired
	CustomerOrderService orderService;

	//添加AOP注解日志管理 
              ---------使用见博客:https://mp.csdn.net/console/editor/html/105488678
	@SysLogAspectValue(
			describtion = "客户创建订单",
			logType = "2",
			type = "POST",
			url = "/SaveOrder",
			table = "super_wx_order",
			params = "body",
			method = "POST"
	)
	@ApiOperation(value = "客户创建订单", notes = "/SaveOrder")
	@PostMapping(value = "/SaveOrder")
	public Object SaveOrder(@RequestBody Map<String, String> body) {		 
	 
            return  orderService.SaveWxOrder(wxMemberId, wxMemberCartList);

	 }



}

           ②业务实现层 ----------------- CustomerOrderService.java

 
@Transactional(propagation = Propagation.REQUIRED)
public interface CustomerOrderService {
	
	/**
	 * 创建新订单
	 * 
	 **/
	@Transactional(propagation = Propagation.REQUIRED)
	Map<String, Object> SaveWxOrder(String wxMemberId, String wxMemberCartLis ) throws Exception;

	 
}
	

 【扩展】:

                   Springboot注解@Transactional(propagation)

                                                                             (参考:Springboot注解@Transactional(propagation_梵高的夏天的博客-CSDN博客_@transactional(propagation

           ③接口实现类 -  CustomerOrderServiceImpl.java


 【扩展】:

       ①)异常

              ②)jpa中行级锁使用方法:

                       行级锁,对于mysql,InnoDB预设的是Row-level Lock,但是,需要明确的指定主键,才会执行行级锁,否则执行的为表锁。

                       锁解释:[ MySQL中select * for update锁表的问题_一直在发呆_新浪博客 博客]

    

      ④Jpa实现层 - ------WxProductRepository.java

public interface WxProductRepository  extends JpaRepository<WxProduct, Long>{	  

	@Modifying(clearAutomatically = true)
	@Query(value = "update super_wx_product_table set `wx_product_invent` = `wx_product_invent`-:productInvent, `wx_buy_num`=`wx_buy_num` + :productInvent where `wx_product_id` = :productId and `wx_product_invent` >= :productInvent", nativeQuery = true)
	int ReduceInventory(@Param("productId")String productId, @Param("productInvent")int productInvent);
 

}

【扩展】:

             ①)疑问:

实现方式原因:在查询数据同时进行更改数据信息

            ②    @Modifying(clearAutomatically = true) 注解解释:

 底层:

     3)商品实体层 ------WxProduct.java

@Entity
@Table(name = "s_wx_product")
@Component
@org.hibernate.annotations.Table(comment="小程序商品信息表", appliesTo = "s_wx_product")
public class WxProduct extends BaseModel implements java.io.Serializable {

	@ApiModelProperty("商品编号")
	@Column(name="wx_product_id",columnDefinition = "varchar(255)  comment '商品编号'")
	private String wxProductId;//商品编号
	
	@ApiModelProperty("商品名称")
	@Column(name="wx_product_name",columnDefinition = "varchar(255)  comment '商品名称'")
	private String wxProductName;//商品名称
	
	@ApiModelProperty("商品规格")
	@Column(name="wx_product_palate",columnDefinition = "varchar(255)  comment '商品规格'")
	private String wx_productPalate;//商品名称
	
	@ApiModelProperty("销售价格")
	@Column(name="wx_product_price",columnDefinition = "decimal(18,2) DEFAULT '0.00'  comment '销售价格'")
	private BigDecimal wx_productPrice;//销售价格
	
	@ApiModelProperty("优惠价格")
	@Column(name="wx_product_cost",columnDefinition = "decimal(18,2) DEFAULT '0.00'  comment '优惠价格'")
	private BigDecimal wx_productCost;//销售价格
	
	@ApiModelProperty("商品购买次数")
	@Column(name="wx_buy_num",columnDefinition = "bigint(5)  comment '商品购买次数'")
	private Integer wx_buyNum;//商品购买次数
	
	@ApiModelProperty("商品评论")
	@Column(name="wx_goods_comment",columnDefinition = "varchar(255)  comment '商品评论'")
	private  String wx_goodsComment;//商品评论
	
	 
	@ApiModelProperty("商品原图")
	@Column(name="wx_original",columnDefinition = "varchar(255)  comment '商品原图'")
	private String  wx_original; //商品原图
	
	@ApiModelProperty("是否上架(1:上架2:下架)")
	@Column(name="wx_market_enable",columnDefinition = "int(5)  comment '是否上架(0:上架 0:下架)'")
	private String wx_marketEnable;//是否上架(1:上架2:下架)

	@ApiModelProperty("商品类型(1:团购2:零售)")
	@Column(name="wx_product_type",columnDefinition = "int(5)  comment '商品类型(1:团购2:零售)'")
	private Integer wx_productType;//订单类型(1:团购2:零售)
	
	@ApiModelProperty("商品详情-1")
	@Column(name="wx_intro",columnDefinition = "text  comment '商品详情-1'")
	private String  wx_intro;//商品详情
	
	@ApiModelProperty("商品详情-简介图2")
	@Column(name="wx_intro_img2",columnDefinition = "text  comment '商品详情-简介图2'")
	private String  wx_introImg2;//商品详情

	@ApiModelProperty("排序")
	@Column(name="wx_product_sort",columnDefinition = "int(5) comment '商品排序'")
	private String wxProductSort;//订单类型(1:团购2:零售)
	
	
	@ApiModelProperty("商品库存数")
	@Column(name="wx_product_invent",columnDefinition = "int comment '商品库存数'")
	private int wx_productInvent = 0;//商品库存数
	
	
}

以上就是springboot2.0+MySQL行级锁的实现过程及相关原理,提供参考,以后项目可以参考使用。


以上是自己整理的,并测试过,可以直接用

转载声明:本文为博主原创文章,未经博主允许不得转载

⚠️注意⚠️~

💯本期内容就结束了,如果内容有误,麻烦大家评论区指出

如有疑问❓可以在评论区留言💬或私信留言💬,尽我最大能力🏃‍♀️帮大家解决👨‍🏫!

如果我的文章有帮助,点赞👍关注💖,您的鼓励是我分享的动力🏃🏃🏃~









【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。