2018.8.2 java电商从1到2--chapter12 SpringSchedule实现定时关单

目录

chapter12 SpringSchedule实现定时关单

12.1 总览

12.2 cron表达式

12.3 Spring Schedule的使用

12.3.1 配置文件applicationContext.xml

12.3.2 @Schedule注解

12.3.3 真正的关单操作

12.4 mysql的行锁和表锁


12.1 总览

  • SpringSchedule的使用
  • cron表达式
  • mysql的表锁与行锁

 

12.2 cron表达式

(1)格式

秒 分 时 日 月 周  年(可选)

详细的见文档。

 

(2)常用表达式

0 */1 * * * ?

注意这里*/1表示每个一分钟,不是每隔一分钟。比如启动时是12:20:29,那么第一次定时任务执行的时间不是一分钟后,而是12:21。

 

(3)cron表达式生成器

直接百度生成器即可。

 

12.3 Spring Schedule的使用

12.3.1 配置文件applicationContext.xml

注意别导入错Xmlns等参数,因为有狠毒标签。




    
        
    

    

    
    
    


    
    

 

12.3.2 @Schedule注解

这里方法命名为closeOrderTaskV1,是因为后续还要改进,这里显然没考虑到集群场景。集群时不需要每台服务器都执行定时任务。

@Component
@Slf4j
public class CloseOrderTask {

    @Autowired
    private IOrderService iOrderService;

    @Scheduled(cron="0 */1 * * * ?")//每1分钟(每个1分钟的整数倍)
    public void closeOrderTaskV1(){
        log.info("关闭订单定时任务启动");
        int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
        iOrderService.closeOrder(hour);
        log.info("关闭订单定时任务结束");
    }
}

 

12.3.3 真正的关单操作

这里提到的一定要用where主键防止锁表将在下节详细说明。

@Service("iOrderService")
@Slf4j
public class OrderServiceImpl implements IOrderService {
    @Override
    public void closeOrder(int hour) {
        Date closeDateTime = DateUtils.addHours(new Date(),-hour);
        List orderList = orderMapper.selectOrderStatusByCreateTime(Const.OrderStatusEnum.NO_PAY.getCode(),DateTimeUtil.dateToStr(closeDateTime));

        for(Order order : orderList){
            List orderItemList = orderItemMapper.getByOrderNo(order.getOrderNo());
            for(OrderItem orderItem : orderItemList){

                //一定要用主键where条件,防止锁表。同时必须是支持MySQL的InnoDB。
                Integer stock = productMapper.selectStockByProductId(orderItem.getProductId());

                //考虑到已生成的订单里的商品,被删除的情况
                if(stock == null){
                    continue;
                }
                Product product = new Product();
                product.setId(orderItem.getProductId());
                product.setStock(stock+orderItem.getQuantity());
                productMapper.updateByPrimaryKeySelective(product);
            }
            orderMapper.closeOrderByOrderId(order.getId());
            log.info("关闭订单OrderNo:{}",order.getOrderNo());
        }
    }

}
public interface ProductMapper {
    //这里一定要用Integer,因为int无法为NULL,考虑到很多商品已经删除的情况。
    Integer selectStockByProductId(Integer id);
}

  

 

12.4 mysql的行锁和表锁

上文的sql语句中使用了select ... for update。这是为了加锁,防止刚刚查询出来的结果是过时的值(中间又有其他线程修改这个记录),后续进行的set语句使用过时的值处理,会覆盖中间的修改。

注意:使用mysql的select ... for update需要使用InnoDB引擎。

  • 当查询条件中明确指定主键时,是行锁。
  • 当查询条件中不明确指定主键时,是表锁。
  • 如果查询结果为空,不加锁。

示例:

select * from user where id=#{id} for update

有结果集:行锁。无结果集:不加锁。

select * from user where name=#{name} for update

有结果集:表锁。无结果集:不加锁。

select * from user where id<>#{id} for update

有结果集:表锁。无结果集:不加锁。

 

你可能感兴趣的:(java电商从1到2)