数据库
orders、order_item、product
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "orders", schema = "fzs")
public class OrdersEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
@Column(name = "iid")
private int iid;
@Basic
@Column(name = "order_status")
private int orderStatus;
@Basic
@Column(name = "receiver_name")
private String receiverName;
@Basic
@Column(name = "receiver_mobile")
private String receiverMobile;
@Basic
@Column(name = "order_amount")
private BigDecimal orderAmount;
@Basic
@Column(name = "create_time")
private Date createTime;
@Basic
@Column(name = "create_user")
private String createUser;
@Basic
@Column(name = "update_time")
private Date updateTime;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "order_item")
public class OrderItemEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
@Column(name = "iid")
private int iid;
@Basic
@Column(name = "order_id")
private int orderId;
@Basic
@Column(name = "product_id")
private int productId;
@Basic
@Column(name = "purchase_price")
private BigDecimal purchasePrice;
@Basic
@Column(name = "purchase_num")
private int purchaseNum;
@Basic
@Column(name = "create_time")
private Date createTime;
@Basic
@Column(name = "create_user")
private String createUser;
@Basic
@Column(name = "update_time")
private Date updateTime;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "product")
public class ProductEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
@Column(name = "id")
private int id;
@Basic
@Column(name = "product_name")
private String productName;
@Basic
@Column(name = "price")
private BigDecimal price;
@Basic
@Column(name = "count")
private int count;
@Basic
@Column(name = "product_desc")
private String productDesc;
@Basic
@Column(name = "create_time")
private Date createTime;
@Basic
@Column(name = "create_user")
private String createUser;
@Basic
@Column(name = "update_time")
private Date updateTime;
}
@Repository
public interface OrderItemRepository extends JpaRepository<OrderItemEntity, Integer> {
}
@Repository
public interface OrderRepository extends JpaRepository<OrdersEntity, Integer> {
}
@Repository
public interface ProductRepository extends JpaRepository<ProductEntity, Integer> {
/**
* 总数减去orderCount
* @param orderCount
* @param orderId
*/
@Modifying
@Query(value = "update product set count = count - :orderCount, update_time =:update_time where id = :orderId", nativeQuery = true)
void updateProductCount(@Param("orderCount") Integer orderCount,
@Param("update_time") Date updateTime,
@Param("orderId") Integer orderId);
}
模拟实现
public interface OrderService {
int orderCreate();
}
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private ProductRepository productRepository;
@Resource
private OrderRepository orderRepository;
@Resource
private OrderItemRepository orderItemRepository;
private int purchaseProductId = 100100;
private int purchaseProductNum = 1;
@Override
@Transactional(rollbackOn = Exception.class)
public int orderCreate() {
ProductEntity productEntity = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);
if (productEntity == null) {
throw new RuntimeException("购买商品:" + purchaseProductId + "不存在");
}
//商品库存
int count = productEntity.getCount();
if (purchaseProductNum > count) {
throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + count);
}
// 计算剩余库存
Integer leftCount = count - purchaseProductNum;
// 更新商品库存
productEntity.setCount(leftCount);
productEntity.setUpdateTime(new Date());
productRepository.save(productEntity);
// 下单
//productRepository.updateProductCount(purchaseProductNum, new Date(), productEntity.getId());
//创建订单
BigDecimal multiply = productEntity.getPrice().multiply(new BigDecimal(purchaseProductNum));
OrdersEntity orderEntity = new OrdersEntity();
// 待处理 订单状态
orderEntity.setOrderStatus(1);
orderEntity.setReceiverName("测试收货人");
orderEntity.setReceiverMobile("13888888888");
orderEntity.setOrderAmount(multiply);
orderEntity.setCreateTime(new Date());
orderEntity.setCreateUser("测试用户1");
orderEntity.setUpdateTime(new Date());
orderRepository.save(orderEntity);
// 创建订单明细
OrderItemEntity orderItemEntity = new OrderItemEntity();
orderItemEntity.setOrderId(orderEntity.getIid());
orderItemEntity.setProductId(productEntity.getId());
orderItemEntity.setPurchasePrice(new BigDecimal("0"));
orderItemEntity.setPurchaseNum(purchaseProductNum);
orderItemEntity.setCreateTime(new Date());
orderItemEntity.setCreateUser("测试用户1");
orderItemEntity.setUpdateTime(new Date());
orderItemRepository.save(orderItemEntity);
System.out.println("订单创建成功");
return orderEntity.getIid();
}
}
测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class JpaTest {
@Autowired
private ProductRepository productRepository;
@Resource
private OrderService orderService;
@Autowired
private OrderRepository orderRepository;
private int purchaseProductId = 100100;
private int purchaseProductNum = 1;
@Test
public void testOrderAdd() {
ProductEntity productEntity = new ProductEntity();
productEntity.setId(purchaseProductId);
productEntity.setProductName("测试商品");
productEntity.setPrice(new BigDecimal(100));
productEntity.setCount(purchaseProductNum);
productEntity.setProductDesc("测试商品描述");
productEntity.setCreateTime(new Date());
productEntity.setCreateUser("测试用户");
productEntity.setUpdateTime(new Date());
productRepository.save(productEntity);
}
@Test
public void testOrder(){
OrdersEntity orderEntity = new OrdersEntity();
// 待处理 订单状态
orderEntity.setOrderStatus(1);
orderEntity.setReceiverName("测试收货人");
orderEntity.setReceiverMobile("13888888888");
orderEntity.setOrderAmount(new BigDecimal(purchaseProductNum));
orderEntity.setCreateTime(new Date());
orderEntity.setCreateUser("测试用户1");
orderEntity.setUpdateTime(new Date());
orderRepository.save(orderEntity);
}
@Test
public void concurrentOrder() {
CountDownLatch count = new CountDownLatch(5);
CyclicBarrier barrier = new CyclicBarrier(5);
ExecutorService pool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
pool.execute(() -> {
try {
barrier.await();
System.out.println("线程" + Thread.currentThread().getName() + "开始执行");
int orderId = orderService.orderCreate();
System.out.println("订单生成成功,订单号:" + orderId);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}finally {
count.countDown();
}
});
}
try {
count.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
pool.shutdown();
}
}
在多线程情况下,产生了超卖问题。
改用这个下面update 库存减一
但还是出现了库存变为负数。
这个方法还不能解决问题,同时下5个单,商品会变成负数。
超卖现象举例2
产生原因
并发监测库存,造成库存充足的假象。
update更新库存,导致库存变成负数。
图解
方法1(抛出异常+synchronized):
可以先使用最简单的方法,在更新商品数量后,再校验商品的库存,如果是负数,抛出异常。
但是在多线程中,还是没用。
方法2: 加锁 synchronized
在校验库存和扣减库存统一加锁,使之成为原子性操作,并发的时候,只有获取到锁的线程才能校验扣减库存。
在扣减库存结束后释放锁,确保库存不会变成负数。
- 可以使用synchronized关键字解决超卖问题,这是最原始的锁哦。
如果在创建订单的方法上加synchronized关键字,这样能够解决问题吗?
答案是不能的,因为锁住的是当前方法,但是事务没有被锁住,线程1的锁释放后,线程2进入该方法,事务可能还没提交,这就导致了查询库存的时候,还是原来的库存
那么如何控制事务呢? ```java @Service public class OrderServiceImpl implements OrderService {
@Resource private ProductRepository productRepository; @Resource private OrderRepository orderRepository; @Resource private OrderItemRepository orderItemRepository; @Autowired private PlatformTransactionManager transactionManager; @Autowired private TransactionDefinition definition;
private int purchaseProductId = 100100; private int purchaseProductNum = 1;
@Override //@Transactional(rollbackOn = Exception.class) public synchronized int orderCreate() {
TransactionStatus transaction = transactionManager.getTransaction(definition);
ProductEntity productEntity = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);
if (productEntity == null) {
transactionManager.rollback(transaction);
throw new RuntimeException("购买商品:" + purchaseProductId + "不存在");
}
//商品库存
int count = productEntity.getCount();
if (purchaseProductNum > count) {
transactionManager.rollback(transaction);
throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + count);
}
// 计算剩余库存
//Integer leftCount = count - purchaseProductNum;
//// 更新商品库存
//productEntity.setCount(leftCount);
//productEntity.setUpdateTime(new Date());
//productRepository.save(productEntity);
// 下单
productRepository.updateProductCount(purchaseProductNum, new Date(), productEntity.getId());
// 当检索商品的库存为负数,抛出异常
//ProductEntity product = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);
//int sk_count = product.getCount();
//if (sk_count < 0) {
// throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + sk_count);
//}
//创建订单
BigDecimal multiply = productEntity.getPrice().multiply(new BigDecimal(purchaseProductNum));
OrdersEntity orderEntity = new OrdersEntity();
// 待处理 订单状态
orderEntity.setOrderStatus(1);
orderEntity.setReceiverName("测试收货人");
orderEntity.setReceiverMobile("13888888888");
orderEntity.setOrderAmount(multiply);
orderEntity.setCreateTime(new Date());
orderEntity.setCreateUser("测试用户1");
orderEntity.setUpdateTime(new Date());
orderRepository.save(orderEntity);
// 创建订单明细
OrderItemEntity orderItemEntity = new OrderItemEntity();
orderItemEntity.setOrderId(orderEntity.getIid());
orderItemEntity.setProductId(productEntity.getId());
orderItemEntity.setPurchasePrice(new BigDecimal("0"));
orderItemEntity.setPurchaseNum(20);
orderItemEntity.setCreateTime(new Date());
orderItemEntity.setCreateUser("测试用户1");
orderItemEntity.setUpdateTime(new Date());
orderItemRepository.save(orderItemEntity);
System.out.println("订单创建成功");
transactionManager.commit(transaction);
return orderEntity.getIid();
}
}
<br />synchronized不同的写法<br />
<a name="GtYy4"></a>
### 方法3: 加锁 reentrantlock

```java
@Service
public class OrderServiceImpl implements OrderService {
@Resource
private ProductRepository productRepository;
@Resource
private OrderRepository orderRepository;
@Resource
private OrderItemRepository orderItemRepository;
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private TransactionDefinition definition;
//private Lock lock = new ReentrantLock();
private int purchaseProductId = 100100;
private int purchaseProductNum = 1;
@Override
//@Transactional(rollbackOn = Exception.class)
public int orderCreate() {
ProductEntity productEntity = null;
//try {
//lock.lock();
synchronized (this) {
TransactionStatus transaction = transactionManager.getTransaction(definition);
productEntity = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);
if (productEntity == null) {
transactionManager.rollback(transaction);
throw new RuntimeException("购买商品:" + purchaseProductId + "不存在");
}
//商品库存
int count = productEntity.getCount();
if (purchaseProductNum > count) {
transactionManager.rollback(transaction);
throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + count);
}
// 计算剩余库存
//Integer leftCount = count - purchaseProductNum;
//// 更新商品库存
//productEntity.setCount(leftCount);
//productEntity.setUpdateTime(new Date());
//productRepository.save(productEntity);
// 下单
productRepository.updateProductCount(purchaseProductNum, new Date(), productEntity.getId());
transactionManager.commit(transaction);
}
//} finally {
// lock.unlock();
//}
// 当检索商品的库存为负数,抛出异常
//ProductEntity product = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);
//int sk_count = product.getCount();
//if (sk_count < 0) {
// throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + sk_count);
//}
TransactionStatus transaction2 = transactionManager.getTransaction(definition);
//创建订单
BigDecimal multiply = productEntity.getPrice().multiply(new BigDecimal(purchaseProductNum));
OrdersEntity orderEntity = new OrdersEntity();
// 待处理 订单状态
orderEntity.setOrderStatus(1);
orderEntity.setReceiverName("测试收货人");
orderEntity.setReceiverMobile("13888888888");
orderEntity.setOrderAmount(multiply);
orderEntity.setCreateTime(new Date());
orderEntity.setCreateUser("测试用户1");
orderEntity.setUpdateTime(new Date());
orderRepository.save(orderEntity);
// 创建订单明细
OrderItemEntity orderItemEntity = new OrderItemEntity();
orderItemEntity.setOrderId(orderEntity.getIid());
orderItemEntity.setProductId(productEntity.getId());
orderItemEntity.setPurchasePrice(new BigDecimal("0"));
orderItemEntity.setPurchaseNum(20);
orderItemEntity.setCreateTime(new Date());
orderItemEntity.setCreateUser("测试用户1");
orderItemEntity.setUpdateTime(new Date());
orderItemRepository.save(orderItemEntity);
System.out.println("订单创建成功");
transactionManager.commit(transaction2);
return orderEntity.getIid();
}
}
结果