Quartz+Mybatis+SpringMvc+SpringBoot整合(一)

发布于 2019-11-10  265 次阅读


Quartz简介:Quartz是一个完全由java编写的开源作业调度框架,为在Java应用程序中进行作业调度提供了简单却强大的机制等等等,官网http://www.quartz-scheduler.org/

这篇文章主要讲的如何实现Quartz与SSM整合以及前端使用vue+ElementUI实现简单的任务管理页面

实现的功能:页面内添加/编辑/删除/暂停/恢复/停止/终止任务,支持页面内编辑cron,calendar,dailyTime以及simple类型的触发器及其参数。

效果如图

本文没什么讲解,如果稍微懂点的,稍微改一改就可以自己来用了,主要是自己记录一下,主要是后端代码,涉及到的都放了出来,vue就简单放了下首页及编辑页的,前端那点简单,主要是接口没问题就可以。

首先maven引入quartz,目前最新版本为2.3.2

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
    <version>2.3.2</version>
</dependency>

然后是新建quartz的配置文件,新建文件quartz.properties,位于src/main/resources/quartz.properties

# 固定前缀org.quartz
# 主要分为scheduler、threadPool、jobStore、plugin等部分
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
# 实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# threadCount和threadPriority将以setter的形式注入ThreadPool实例
# 并发个数
org.quartz.threadPool.threadCount = 5
# 优先级
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
#持久化 不需要了,在SchedulerConfig里获取了Spring的数据源
#org.quartz.jobStore.misfireThreshold = 5000
#org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#org.quartz.jobStore.tablePrefix = QRTZ_
#org.quartz.jobStore.dataSource = qzDS
#org.quartz.dataSource.qzDS.driver = com.mysql.cj.jdbc.Driver
#org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/lkAdmin?allowPublicKeyRetrieval=true&useUnicode=true&useSSL=false&allowMultiQueries=true&useAffectedRows=true&characterEncoding=utf8&serverTimezone=UTC
#org.quartz.dataSource.qzDS.user = XX
#org.quartz.dataSource.qzDS.password = XX
#org.quartz.dataSource.qzDS.maxConnections = 10

然后去新建下Quartz需要的数据库表,以下为MySql的语句,如果其他数据库去Quartz官网查询, 下载完整的文档,然后在docs目录下的dbTables文件夹里找到对应的创建表的方法。

#
# 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;

接下来请再去新建一个视图v_schedule,便于之后mybatis的mapper所调用

SELECT
`qrtz_job_details`.`JOB_NAME` AS `JobName`,
`qrtz_job_details`.`JOB_GROUP` AS `JobGroup`,
`qrtz_job_details`.`JOB_CLASS_NAME` AS `JobClassName`,
`qrtz_job_details`.`DESCRIPTION` AS `Description`,
`qrtz_triggers`.`TRIGGER_NAME` AS `TriggerName`,
`qrtz_triggers`.`TRIGGER_GROUP` AS `TriggerGroup`,
`qrtz_triggers`.`TRIGGER_TYPE` AS `TriggerType`,
`qrtz_triggers`.`TRIGGER_STATE` AS `TriggerState`,
`qrtz_triggers`.`PRIORITY` AS `Priority`,
`qrtz_simple_triggers`.`REPEAT_INTERVAL` AS `RepeatInterval`,
`qrtz_simple_triggers`.`TIMES_TRIGGERED` AS `TimesTriggered`,
`qrtz_simple_triggers`.`REPEAT_COUNT` AS `RepeatCount`,
`qrtz_cron_triggers`.`CRON_EXPRESSION` AS `CronExpression`
FROM
(
(
(
`qrtz_job_details`
LEFT JOIN `qrtz_triggers` ON ( ( ( `qrtz_job_details`.`JOB_NAME` = `qrtz_triggers`.`JOB_NAME` ) AND ( `qrtz_job_details`.`JOB_GROUP` = `qrtz_triggers`.`JOB_GROUP` ) ) )
)
LEFT JOIN `qrtz_simple_triggers` ON ( ( ( `qrtz_triggers`.`TRIGGER_NAME` = `qrtz_simple_triggers`.`TRIGGER_NAME` ) AND ( `qrtz_triggers`.`TRIGGER_GROUP` = `qrtz_simple_triggers`.`TRIGGER_GROUP` ) ) )
)
LEFT JOIN `qrtz_cron_triggers` ON ( ( ( `qrtz_triggers`.`TRIGGER_NAME` = `qrtz_cron_triggers`.`TRIGGER_NAME` ) AND ( `qrtz_triggers`.`TRIGGER_GROUP` = `qrtz_cron_triggers`.`TRIGGER_GROUP` ) ) )
)

然后通过@Configuration定义Quartz的一些配置,其中主要是数据源,新建文件SchedulerConfig

@Configuration
public class SchedulerConfig {
    /**
     * 配置JobFactory
     */
    @Bean
    public JobFactory jobFactory(ApplicationContext applicationContext) {
        AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
        jobFactory.setApplicationContext(applicationContext);
        return jobFactory;
    }

    /**
     * SchedulerFactoryBean这个类的真正作用提供了对org.quartz.Scheduler的创建与配置,并且会管理它的生命周期与Spring同步。
     * org.quartz.Scheduler: 调度器。所有的调度都是由它控制。
     *
     * @param dataSource 为SchedulerFactory配置数据源
     * @param jobFactory 为SchedulerFactory配置JobFactory
     */
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource, JobFactory jobFactory) throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        //可选,QuartzScheduler启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录
        factory.setOverwriteExistingJobs(true);
        factory.setAutoStartup(true); //设置自行启动
        factory.setDataSource(dataSource);
        factory.setJobFactory(jobFactory);
        factory.setQuartzProperties(quartzProperties());
        return factory;
    }

    /**
     * 从quartz.properties文件中读取Quartz配置属性
     */
    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }

    /**
     * 配置JobFactory,为quartz作业添加自动连接支持
     */
    public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
            ApplicationContextAware {
        private transient AutowireCapableBeanFactory beanFactory;

        @Override
        public void setApplicationContext(final ApplicationContext context) {
            beanFactory = context.getAutowireCapableBeanFactory();
        }

        @Override
        protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
            final Object job = super.createJobInstance(bundle);
            beanFactory.autowireBean(job);
            return job;
        }
    }
}

基本配置终于完成了,接下来就可以开始使用他了。首先新建一个BaseJob接口

public interface BaseJob extends Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException;
}

接下来可以新建第一个任务了,新建HelloJob类

@Schedule(name = "hello", description = "该任务会打印hello")
@Slf4j
public class HelloJob implements BaseJob {
public HelloJob() {
}
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
log.error("Hello Job执行时间: " + new Date());
}
}

其中注解@Schedule是我自定义的一个注解,这里也放一下吧,是便于前端配置任务时,可以看到任务的相关信息。

@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Schedule {
    String name() default "";
    String description() default "";
}

然后开始定义Mapper,Service,Entity以及Controller
新建SchedulerMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.loneking.admin.mapper.SchedulerMapper">
    <resultMap id="BaseResultMap" type="com.loneking.admin.entity.SchedulerEntity">
        <result column="JobName" property="jobName"/>
        <result column="JobGroup" property="jobGroup"/>
        <result column="JobClassName" property="jobClassName"/>
        <result column="TriggerName" property="triggerName"/>
        <result column="TriggerGroup" property="triggerGroup"/>
        <result column="RepeatInterval" property="repeatInterval"/>
        <result column="TimesTriggered" property="timesTriggered"/>
        <result column="CronExpression" property="cronExpression"/>
        <result column="Description" property="description"/>
        <result column="TriggerType" property="triggerType"/>
        <result column="TriggerState" property="triggerState"/>
        <result column="Priority" property="priority"/>
        <result column="RepeatInterval" property="repeatInterval"/>
        <result column="TimesTriggered" property="timesTriggered"/>
        <result column="RepeatCount" property="repeatCount"/>

    </resultMap>
    <select id="getSchedulerList" resultMap="BaseResultMap">
       SELECT JobName,JobGroup,JobClassName,TriggerName,TriggerGroup,
       RepeatInterval,TimesTriggered,CronExpression,Description,TriggerType,TriggerState,
      Priority,RepeatInterval,TimesTriggered,RepeatCount  from v_schedule
    </select>
    <select id="detail" resultMap="BaseResultMap">
       SELECT JobName,JobGroup,JobClassName,TriggerName,TriggerGroup,
       RepeatInterval,TimesTriggered,CronExpression,Description,TriggerType,TriggerState,
         Priority,RepeatInterval,TimesTriggered,RepeatCount from v_schedule where JobName=#{jobName}
       and JobGroup=#{jobGroup}
    </select>
</mapper>

新建ScheduleEntity.class

@Data
public class SchedulerEntity {
    /**
     * 任务名称
     */
    private String jobName;
    /**
     * 任务分组
     */
    private String jobGroup;
    /**
     * 任务描述
     */
    private String description;
    /**
     * 任务所在类
     */
    private String jobClassName;
    /**
     * 触发器名称
     */
    private String triggerName;
    /**
     * 触发器分组
     */
    private String triggerGroup;
    /**
     * 触发器状态
     */
    private String triggerState;
    /**
     * 触发器类型
     */
    private String triggerType;
    /**
     * 优先级
     */
    private Integer priority;
    /**
     * cron表达式
     */
    private String cronExpression;

    /**
     * simple类型触发器属性 (MILLISECOND,SECOND,MINUTE,HOUR)
     * 重复间隔
     */
    private Long repeatInterval;
    /**
     * 重复次数
     */
    private Integer repeatCount;
    /**
     * 已触发次数
     */
    private Long timesTriggered;

    /**
     * calendar,dailyTime(SECOND, MINUTE or HOUR)
     * 时间间隔
     */
    private Integer timeInterval;
    /**
     * 间隔单位
     */
    private String intervalUnit;
}

最后就是mapper,service以及controller了,就放到下篇文章Quartz+Mybatis+SpringMvc+SpringBoot整合(2)


LoneKing