Quartz的应用

版权声明:本文为博主原创文章,版权归原作者小思所有,转载或者引用本文内容请注明来源及原作者 https://blog.csdn.net/zeal9s/article/details/84403013

如果一张车票,被A乘客选中,且停留在付款页面,那么这张车票是不能出现在其他购票用户是不能购买的,因为该车票已经暂时被锁定,那么假如A乘客迟迟停留在付款页面,20秒钟过后,还未支付该订单,那么系统的定时器的功能会执行调度器去查询支付状态,如果未支付,我们的定时器工作要实现此功能,将车票解除锁定,重新改为可用的状态,那么A用户和其他的用户又能够购买该票。这时候就需要quartz自动调度器来实现。

本项目实例是基于idea开发软件进行编码,在spring boot中结合quartz使用,如果订单在20秒钟查询编号为1的车票,如果十秒之后还在付款,则取消该车票订单,将车票的锁定状态重新改为可用。

步骤:
新建Spring Boot项目
ubmV0L3plYWw5cw==,size_16,color_FFFFFF,t_70)
在这里插入图片描述
在这里插入图片描述
1.在Mysql的quartz数据库建立4表和在idea建相关包
在这里插入图片描述
在这里插入图片描述
2.使用项目内部的mybatis插件,将下列的sql导入到Mysql运行,然后借助插件自动生成实体类和mapper 使用教程

-- 需要定时器执行操作的实体类,这里是以订单表为例
create table t_order
(
  order_id int primary key auto_increment,                          -- 订单ID
  order_no varchar(50) not null,                                    -- 订单编号
  customer_id int not null,                                         -- 客户ID
  order_status int not null,                                        -- 状态:0 待付款 1 已付款 2 已取消(客户) 3 已取消(卖家) 4 已取消(付款超时)
  create_timestamp timestamp NULL default CURRENT_TIMESTAMP         -- 创建日期时间:默认为系统当前时间
);



-- 注意:job_name存放的任务类的全路径,在quartz中通过jobName和jobGroup来确定trigger的唯一性,所以这两列为联合唯一索引
create table t_schedule_trigger
(
  id int primary key auto_increment,                                -- ID
  cron varchar(200) not null,                                       -- 时间表达式
  status char(1) not null,                                          -- 使用状态 0:禁用   1:启用
  job_name varchar(200) not null,                                   -- 任务名称
  job_group varchar(200) not null,                                  -- 任务分组  
  unique index(job_name,job_group)
);

-- 额外添加到任务中的参数
create table t_schedule_trigger_param
(
  param_id int primary key auto_increment,                                -- ID
  name varchar(200) not null,                                             -- 参数名
  value varchar(512),                                                     -- 参数值
 
  schedule_trigger_id int not null,                                       -- 外键:引用t_schedule_trigger(id)
  foreign key(schedule_trigger_id) references t_schedule_trigger(id)
);

注意生成之后的xml文件中配置的包的全限定名是否和项目一致。

3.在quartz包新建PayTicketJob.java接口,在订单支付状态是正在支付的状态,如果此状态持续20秒钟,那么这个工作类会去修改此票的状态。

此类需要调用:
OrderMapper.java接口的查询订单的selectPay和updateByPrimaryKey方法
ScheduleTriggerMapper.java接口的updateByPrimaryKey方法

selectPay:根据车票的id去查询到该订单的状态
updateByPrimaryKey:修改订单的状态
updateByPrimaryKey:修改触发器的状态

这里使用的调度器是表达式触发器
触发器的表达式生成网址:在线生成cron触发器的表达式网址

PayTicketJob.java

package com.springboot.ssm.quartz;

import com.springboot.ssm.mapper.OrderMapper;
import com.springboot.ssm.mapper.ScheduleTriggerMapper;
import com.springboot.ssm.model.Order;
import com.springboot.ssm.model.ScheduleTrigger;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @author 小思
 * @PackageName:com.springboot.ssm.quartz
 * @ClassName: PayTicketJob
 * @Description:十秒钟之后如果还在支付状态,则取消改订单的调度
 * @date 2018/11/25 9:43
 */
//启用Lombok的Slf4j输出日志
@Slf4j
@Component
public class PayTicketJob implements Job {
    static int count = 0;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private ScheduleTriggerMapper scheduleTriggerMapper;


    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        System.out.println("支付检查任务正在执行。。。" + new Date().toLocaleString());
        //在付款页面的时候触发这个触发器
        //查询3号车票票的支付状态(假如3号票被1号顾客购买了)
        Order o = orderMapper.selectPay(3);
        //支付状态有三种  0:未支付  1:正在支付   2:支付成功
        //二十秒钟之后如未支付,则将车票状态修改,重新入库
        if (o.getOrderStatus() == 1 && count == 2) {
            o.setOrderStatus(0);
            int n = orderMapper.updateByPrimaryKey(o);
            //数据库影响行数大于0,则修改车票状态成功
            if (n > 0) {
                //将检查任务停止
                ScheduleTrigger scheduleTrigger=scheduleTriggerMapper.selectByPrimaryKey(1);
                scheduleTrigger.setStatus("0");
                try {
                    scheduleTriggerMapper.updateByPrimaryKey(scheduleTrigger);
                }
                catch (Exception e){

                }

                System.out.println("支付检查任务执行完成!!!" + new Date().toLocaleString());
            }
        }
        count++;
        System.out.println(count);

    }
}


4.在t_schedule_trigger表中插入刚刚新建的PayTicketJob.java接口的数据
建好数据之后,在ScheduleTriggerMapper.java添加查询所有的触发器的方法
queryAll,建的顺序是xml、java文件夹下的mapper,然后调用的工作类,自动注入mapper类,实际上应该再在service注入一层该方法。xml中的大部分增删改查的方法,都已经使用mybatis自动生成,我们根据动态sql语句变通就行。

在这里插入图片描述
5.将quartz工作时的触发器的详细信息的表导入到数据库中运行,此sql文件在quartz的官方文档有下载。下载它的demo,表存储在dbTables文件夹下。
在这里插入图片描述
tables_mysql.sql

#
# Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
#
# PLEASE consider using mysql with innodb tables to avoid locking issues
#
# In your Quartz properties file, you'll need to set 
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#

DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;


CREATE TABLE QRTZ_JOB_DETAILS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    JOB_CLASS_NAME   VARCHAR(250) NOT NULL,
    IS_DURABLE VARCHAR(1) NOT NULL,
    IS_NONCONCURRENT VARCHAR(1) NOT NULL,
    IS_UPDATE_DATA VARCHAR(1) NOT NULL,
    REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    NEXT_FIRE_TIME BIGINT(13) NULL,
    PREV_FIRE_TIME BIGINT(13) NULL,
    PRIORITY INTEGER NULL,
    TRIGGER_STATE VARCHAR(16) NOT NULL,
    TRIGGER_TYPE VARCHAR(8) NOT NULL,
    START_TIME BIGINT(13) NOT NULL,
    END_TIME BIGINT(13) NULL,
    CALENDAR_NAME VARCHAR(200) NULL,
    MISFIRE_INSTR SMALLINT(2) NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
        REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_SIMPLE_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    REPEAT_COUNT BIGINT(7) NOT NULL,
    REPEAT_INTERVAL BIGINT(12) NOT NULL,
    TIMES_TRIGGERED BIGINT(10) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CRON_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    CRON_EXPRESSION VARCHAR(200) NOT NULL,
    TIME_ZONE_ID VARCHAR(80),
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (          
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_BLOB_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    BLOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CALENDARS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    CALENDAR_NAME  VARCHAR(200) NOT NULL,
    CALENDAR BLOB NOT NULL,
    PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_GROUP  VARCHAR(200) NOT NULL, 
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_FIRED_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    ENTRY_ID VARCHAR(95) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    FIRED_TIME BIGINT(13) NOT NULL,
    SCHED_TIME BIGINT(13) NOT NULL,
    PRIORITY INTEGER NOT NULL,
    STATE VARCHAR(16) NOT NULL,
    JOB_NAME VARCHAR(200) NULL,
    JOB_GROUP VARCHAR(200) NULL,
    IS_NONCONCURRENT VARCHAR(1) NULL,
    REQUESTS_RECOVERY VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);

CREATE TABLE QRTZ_SCHEDULER_STATE
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,
    PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);

CREATE TABLE QRTZ_LOCKS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    LOCK_NAME  VARCHAR(40) NOT NULL, 
    PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);


commit;

运行结果:
在这里插入图片描述
注意这些表,quartz框架会自动管理数据,不需要手动设置。

6.新建QuartzConfigration.java和MyJobFactory.java
QuartzConfigration.java

package com.springboot.ssm.quartz;

import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import java.io.IOException;
import java.util.Properties;
/**
 * @author 小思
 * @PackageName:com.springboot.ssm.quartz
 * @ClassName: QuartzConfigration
 * @Description:调度器的配置类
 * @date 2018/11/25 9:53
 */
@Configuration
public class QuartzConfigration {

    //自定义的工作工厂,负责产生Job
    @Autowired
    private MyJobFactory myJobFactory;

    //获取工厂的调度器的工厂
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        try {
            schedulerFactoryBean.setQuartzProperties(quartzProperties());
            schedulerFactoryBean.setJobFactory(myJobFactory);
            return schedulerFactoryBean;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //指定quartz.properties
    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }

    //创建schedule调度器
    @Bean(name = "scheduler")
    public Scheduler scheduler() {
        return schedulerFactoryBean().getScheduler();
    }
}

MyJobFactory.java

package com.springboot.ssm.quartz;

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
/**
 * @author 小思
 * @PackageName:com.springboot.ssm.quartz
 * @ClassName: MyJobFactory
 * @Description:将Job类授予spring管理
 * @date 2018/11/25 10:53
 */
@Component
public class MyJobFactory extends AdaptableJobFactory {

    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        Object jobInstance = super.createJobInstance(bundle);//qz框架反射机制创建jobBean
        capableBeanFactory.autowireBean(jobInstance); //这一步解决不能spring注入bean的问题
        return jobInstance;
    }
}

7.项目的启动文件配置
application.properties

#服务器配置
server.port=8090
server.servlet.context-path=/

#数据源配置
spring.datasource.username=root
spring.datasource.data-password=
spring.datasource.url=jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver


#mybatis的配置
mybatis.type-aliases-package=com.springboot.ssm.model
mybatis.mapper-locations=classpath*:mapper/*.xml

8.SsmApplication.java启动文件的设置

package com.springboot.ssm;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
@MapperScan("com.springboot.ssm.mapper")
public class SsmApplication {

    public static void main(String[] args) {
        SpringApplication.run(SsmApplication.class, args);
    }
}

9.运行SsmApplication.java文件,触发器启动,每十秒次检查一次,定义了一个变量保存运行次数,运行第二次结束后,也就是等待了20秒,客户还没有支付,所以将订单状态改为0,默认的数据库数据是1,正在支付的状态。然后将触发器的状态改为0,0是会运行的,1是不运行的,但是这里有一个bug,也就是在运行状态中修改了触发器的状态,触发器还是照原先的运行状态运行,需要重启才行。

在这里插入图片描述

另外补充:
在执行任务的过程中可以传递参数到表中去,在触发器中可以获取该参数信息
只需要在ScheduleTriggerServiceImpl.java(传递参数信息)中添加下面代码块

    //TODO 可以添加一些额外的参数到任务的上下文中

	//参数的传递
	 //获取当前执行的任务的id
	 Integer id=scheduleJob.getId();
	 //通过任务的触发器的Id获取参数表的参数信息
	 List<ScheduleTriggerParam> scheduleTriggerParams=scheduleTriggerParamMapper.findScheduleTriggerParamById(id);
	 //保存参数信息
	 JobDataMap jobDataMap=jobDetail.getJobDataMap();
	 //遍历参数表中的信息
	 for (ScheduleTriggerParam scheduleTriggerParam : scheduleTriggerParams) {
	     jobDataMap.put(scheduleTriggerParam.getName(),scheduleTriggerParam.getValue());
	 }

PayTicketJob.java(接收参数信息)

	 //获取触发器中的参数信息
        JobDataMap  jobDataMap=jobExecutionContext.getMergedJobDataMap();
        String name=(String)jobDataMap.get("name");
        String age=(String)jobDataMap.get("age");
        System.out.println("name="+name + "______age=" + age);

这只是quartz在springboot+ssm的简单运用,在下的理解如上,欢迎大家补充和斧正!!!

完整项目已经上传,可以帮助理解项目的整体结构和理解quartz的运行机制,里面包含数据库数据和官方文档,下载地址

说在最后的话:编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~

猜你喜欢

转载自blog.csdn.net/zeal9s/article/details/84403013