进行商品表基础设计
create TABLE product(
`id` BIGINT(10) UNSIGNED COMMENT 'id',
`title` VARCHAR(20) COMMENT '商品名称',
`quntity` BIGINT(10) COMMENT '商品数量'
)
INSERT INTO product VALUES(1,'product_1',10);
INSERT INTO product VALUES(2,'product_1',10);
INSERT INTO product VALUES(3,'product_1',10);
#增加时间属性
ALTER TABLE product ADD COLUMN `created_at` Date COMMENT '创建时间';
#设置主键
ALTER TABLE product ADD primary key(id);
ALTER TABLE product MODIFY COLUMN `id` AUTO_INCREMENT;
#修改字段名称
ALTER TABLE product RENAME COLUMN `quntity` TO `quantity`;
实现方式一(Mysql 乐观锁)、
Mybatis-plus中<span class="ne-text">@version</span>
实现
@Version
private Integer version;
<span class="ne-text">@version</span>
与Sql语句<span class="ne-text">update xxx set quantity=value,version = version + 1 where xx=xx and version = #{version}</span>
同等意义。
具体代码实现如下
// 库存检查,订单创建。
Product product = productMapper.selectOne(new QueryWrapper<Product>().lambda().eq(Product::getId, orderCreateRequest.getId()));
if (product == null)
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "商品不存在");
// 检查库存是否充足
if (orderCreateRequest.getQuantity() > product.getQuantity()) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "库存不足");
}
if (product.getQuantity() <= 0) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "库存不足");
}
// 更新库存
productMapper.update(product, new UpdateWrapper<Product>().lambda()
.set(Product::getQuantity, (product.getQuantity() - orderCreateRequest.getQuantity()))
.eq(Product::getId, product.getId()));
sql语句进行实现
通过version实现乐观锁。
public interface ProductMapper extends BaseMapper<Product> {
@Update("update product set quantity = (quantity - ${orderCreateRequest.quantity}),version = version + 1 where id = #{orderCreateRequest.id} and version = version")
int updateQuantity(@Param("orderCreateRequest") OrderCreateRequest orderCreateRequest,@Param("version") int version);
}
具体代码业务实现
@Transactional
public void createOrder(OrderCreateRequest orderCreateRequest) {
// 库存检查,订单创建。
Product product = productMapper.selectOne(new QueryWrapper<Product>().lambda().eq(Product::getId, orderCreateRequest.getId()));
if (product == null)
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "商品不存在");
// 检查库存是否充足
if (orderCreateRequest.getQuantity() > product.getQuantity()) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "库存不足");
}
if (product.getQuantity() <= 0) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "库存不足");
}
// 更新库存
productMapper.updateQuantity(orderCreateRequest,product.getVersion());
}
实现方式二(Redisson分布式锁)
获取分布式锁
String lockKey = "Product_" + product.getId();
RLock lock = redissonClient.getLock(lockKey);
try {
if(lock.tryLock(0,-1, TimeUnit.MILLISECONDS)) {
// 检查库存是否充足
if (orderCreateRequest.getQuantity() > product.getQuantity()) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "库存不足");
}
if (product.getQuantity() <= 0) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "库存不足");
}
productMapper.update(product, new UpdateWrapper<Product>().lambda()
.set(Product::getQuantity, (product.getQuantity() - orderCreateRequest.getQuantity()))
.eq(Product::getId, product.getId()));
}
} catch (InterruptedException e) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR,e.getMessage());
} finally {
if(lock.isHeldByCurrentThread()){
lock.unlock();
}
}
思考
version字段到底做了什么样的操作实现了防止库存超出的问题。