1.数据库表
a.库存表
CREATE TABLE `stock` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL DEFAULT '' COMMENT '名称',
`count` int(11) NOT NULL COMMENT '库存',
`sale` int(11) NOT NULL COMMENT '已售',
`version` int(11) NOT NULL COMMENT '乐观锁,版本号',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
b.订单表
CREATE TABLE `stock_order` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`sid` int(11) NOT NULL COMMENT '库存ID',
`name` varchar(30) NOT NULL DEFAULT '' COMMENT '商品名称',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1072 DEFAULT CHARSET=utf8
2.订单服务类
package com.allen.modules.api.service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.allen.modules.api.bo.OrderInfo;
import com.allen.modules.api.dao.StockMapper;
import com.allen.modules.api.dao.StockOrderMapper;
import com.allen.modules.api.po.Stock;
import com.allen.modules.api.po.StockOrder;
@Service
@Transactional
public class OrderInfoService {
@Resource
private StockMapper stockMapper;
@Resource
private StockOrderMapper stockOrderMapper;
/**
* 下单服务
*/
public Integer createOrder(Integer sid){
//检查库存
Stock stock=this.checkStock(sid);
//扣减库存
//this.saleStock(stock);
//扣减库存(乐观锁)(防止超卖)
this.saleStockByCAS(stock);
//下单
return this.newOrder(stock);
}
/**
* 检查库存
*/
public Stock checkStock(Integer sid){
Stock stock=this.stockMapper.selectByPrimaryKey(sid);
if(stock.getCount().compareTo(stock.getSale())<=0){
throw new RuntimeException("库存不足!");
}
return stock;
}
/**
* 扣减库存(常规方式,会出现超卖情况)
*/
public void saleStock(Stock stock){
Stock updateEntity=new Stock();
updateEntity.setSale(stock.getSale()+1);
updateEntity.setId(stock.getId());
this.stockMapper.updateByPrimaryKeySelective(updateEntity);
}
/**
* 扣减库存(乐观锁)
*/
public void saleStockByCAS(Stock stock){
Stock updateEntity=new Stock();
updateEntity.setSale(stock.getSale()+1);
updateEntity.setId(stock.getId());
updateEntity.setVersion(stock.getVersion());
int count=this.stockMapper.updateByCAS(updateEntity);
if(count==0)
throw new RuntimeException("并发更新失败!");
}
/**
* 下单
*/
public Integer newOrder(Stock stock){
StockOrder order=new StockOrder();
order.setSid(stock.getId());
order.setName(stock.getName());
order.setCreateTime(new Date());
return this.stockOrderMapper.insertSelective(order);
}
}
3.库存持久化类
package com.allen.modules.api.dao;
import com.allen.modules.api.po.Stock;
public interface StockMapper {
int deleteByPrimaryKey(Integer id);
int insert(Stock record);
int insertSelective(Stock record);
Stock selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(Stock record);
int updateByPrimaryKey(Stock record);
/**
* 根据乐观锁更新
*/
int updateByCAS(Stock record);
}
4.Mybatis乐观锁更新库存SQL
<!-- 根据乐观锁更新 -->
<update id="updateByCAS" parameterType="com.allen.modules.api.po.Stock" >
update stock
<set >
<if test="name != null" >
name = #{name,jdbcType=VARCHAR},
</if>
<if test="count != null" >
count = #{count,jdbcType=INTEGER},
</if>
<if test="sale != null" >
sale = #{sale,jdbcType=INTEGER},
</if>
version = version+1
</set>
where id = #{id,jdbcType=INTEGER} and version = #{version,jdbcType=INTEGER}
</update>
5.API代码
/**
* 下单
*/
@RequestMapping(value="/api/order/create",method=RequestMethod.POST)
@ResponseBody
public Result<Object> createOrder(@RequestBody CreateOrder request){
return new Result<>(this.orderInfoService.createOrder(request.getSid()));
}