一、效果/要求/目的
1.目的/解决
       实现电商项目中,由于多个用户某一时间段同时下单时,解决购买同一种商品情况下,扣减库存为负的问题。
2.要求
  创建数据库时,MySQL 的数据库要求:
 1 )MySQL 表引擎 Innodb                
2 )开启事务
项目在业务接口中类头出开启事务监控模式(@Transactional)
 3 )Jmeter
            
4)Postman
 java 环境下安装工具:
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 当前锁 查看】 -----参考]
行锁(Record Locks) 间隙锁(Gap Locks) 临键锁(Next-key Locks)共享锁/排他锁(Shared and Exclusive Locks)
意向共享锁/意向排他锁(Intention Shared and Exclusive Locks)
插入意向锁(Insert Intention Locks)自增锁(Auto-inc Locks)
三、实现方式/应用/环境配置
1、环境配置
2、创建项目
 1)配置文件
 ########################################################### tomcat  配置######################################################### 监听端口server.port=8090server.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=falsespring.datasource.username=rootspring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.tomcat.max-active=1000spring.datasource.tomcat.max-idle=2000spring.datasource.tomcat.initialSize=1000# Hibernate ddl auto (create, create-drop, update)spring.jpa.properties.hibernate.hbm2ddl.auto=updatespring.jpa.properties.hibernate.show_sql=true########################################################### Java Persistence Api 自动进行建表######################################################### Specify the DBMSspring.jpa.database = MYSQL# Show or not log for each sqlqueryspring.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.MySQL5Dialectspring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL55Dialectspring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
   复制代码
 
【扩展】:
      数据库方言:MySQL5Dialect 、MySQL55Dialect 区别:
2)代码实现
 @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);
   }
}
   复制代码
 
 @Transactional(propagation = Propagation.REQUIRED)public interface CustomerOrderService {    /**   * 创建新订单   *    **/  @Transactional(propagation = Propagation.REQUIRED)  Map<String, Object> SaveWxOrder(String wxMemberId, String wxMemberCartLis ) throws Exception;
   }
   复制代码
 
 @Service("CustomerOrderService")public class CustomerOrderServiceImpl extends SysConfigModel implements CustomerOrderService {     @Autowired  WxOrderRepository wxOrderRepository;     @Override  @Transactional(rollbackFor={Exception.class, RuntimeException.class, Error.class})  public Map<String, Object> SaveWxOrder(String memberId, String memberCartList ) throws Exception {    Map<String, Object> resMap = new HashMap<>();         // 处理库存和销售量     int editNum = productRepository.ReduceInventory(AAAA.getProductId(), productNum);     if (editNum != 1) {        log.error("库存更新失败:ProductId : " + detail.getProductId() + "--" + detail.getProductName() + "--productNum : " + productNum);//TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();throw new Exception(detail.getProductName() + "  " + ErrorMessage.error_order_save_inventory_one);// 更新库存应该返回更新1条记录否则视为失败     }        WxOrder order = new WxOrder();      order.setOrderId(orderId);      order.setWx_memberId(memberId);//会员编号      order.setCreateTime(new Date());      this.wxOrderRepository.saveAndFlush(order);         return resMap;  }
 
}
   复制代码
   ①)异常
②)jpa 中行级锁使用方法:
       行级锁,对于 mysql,InnoDB 预设的是 Row-level Lock,但是,需要明确的指定主键,才会执行行级锁,否则执行的为表锁。
       锁解释:[ MySQL中select * for update锁表的问题]
 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)商品实体层
 @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;//商品库存数    }
   复制代码
 行级锁的实现过程及相关原理,供参考以后项目可以参考使用。
转载声明:本文为博主原创文章,未经博主允许不得转载
⚠️注意 ~
?本期内容就结束了,如果内容有误,麻烦大家评论区指出!
如有疑问❓可以在评论区或私信,尽我最大能力帮大家解!
如果我的文章有帮助到您,欢迎点赞+关注✔️鼓励博主,您的鼓励是我分享的动力
评论