springboot integrates quartz persistent tasks into the database

Add quartz related dependencies

		<dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz-jobs</artifactId>
            <!-- <version>2.3.0</version> -->
        </dependency>
        <!--定时任务需要依赖context模块-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

mysql table creation statement

/*
Navicat MySQL Data Transfer
Target Server Type    : MYSQL
Target Server Version : 50725
File Encoding         : 65001
Date: 2019-06-29 15:40:40
*/
 
SET FOREIGN_KEY_CHECKS=0;
 
-- ----------------------------
-- Table structure for QRTZ_BLOB_TRIGGERS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_BLOB_TRIGGERS`;
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,
  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
  KEY `SCHED_NAME` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
  CONSTRAINT `QRTZ_BLOB_TRIGGERS_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
-- ----------------------------
-- Table structure for QRTZ_CALENDARS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_CALENDARS`;
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`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
-- ----------------------------
-- Table structure for QRTZ_CRON_TRIGGERS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_CRON_TRIGGERS`;
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(120) NOT NULL,
  `TIME_ZONE_ID` varchar(80) DEFAULT NULL,
  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
  CONSTRAINT `QRTZ_CRON_TRIGGERS_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
-- ----------------------------
-- Table structure for QRTZ_FIRED_TRIGGERS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_FIRED_TRIGGERS`;
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` int(11) NOT NULL,
  `STATE` varchar(16) NOT NULL,
  `JOB_NAME` varchar(200) DEFAULT NULL,
  `JOB_GROUP` varchar(200) DEFAULT NULL,
  `IS_NONCONCURRENT` varchar(1) DEFAULT NULL,
  `REQUESTS_RECOVERY` varchar(1) DEFAULT NULL,
  PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`),
  KEY `IDX_QRTZ_FT_TRIG_INST_NAME` (`SCHED_NAME`,`INSTANCE_NAME`),
  KEY `IDX_QRTZ_FT_INST_JOB_REQ_RCVRY` (`SCHED_NAME`,`INSTANCE_NAME`,`REQUESTS_RECOVERY`),
  KEY `IDX_QRTZ_FT_J_G` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),
  KEY `IDX_QRTZ_FT_JG` (`SCHED_NAME`,`JOB_GROUP`),
  KEY `IDX_QRTZ_FT_T_G` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
  KEY `IDX_QRTZ_FT_TG` (`SCHED_NAME`,`TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
-- ----------------------------
-- Table structure for QRTZ_JOB_DETAILS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_JOB_DETAILS`;
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) DEFAULT 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,
  PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),
  KEY `IDX_QRTZ_J_REQ_RECOVERY` (`SCHED_NAME`,`REQUESTS_RECOVERY`),
  KEY `IDX_QRTZ_J_GRP` (`SCHED_NAME`,`JOB_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
-- ----------------------------
-- Table structure for QRTZ_LOCKS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_LOCKS`;
CREATE TABLE `QRTZ_LOCKS` (
  `SCHED_NAME` varchar(120) NOT NULL,
  `LOCK_NAME` varchar(40) NOT NULL,
  PRIMARY KEY (`SCHED_NAME`,`LOCK_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
-- ----------------------------
-- Table structure for QRTZ_PAUSED_TRIGGER_GRPS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_PAUSED_TRIGGER_GRPS`;
CREATE TABLE `QRTZ_PAUSED_TRIGGER_GRPS` (
  `SCHED_NAME` varchar(120) NOT NULL,
  `TRIGGER_GROUP` varchar(200) NOT NULL,
  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
-- ----------------------------
-- Table structure for QRTZ_SCHEDULER_STATE
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_SCHEDULER_STATE`;
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`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
-- ----------------------------
-- Table structure for QRTZ_SIMPLE_TRIGGERS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_SIMPLE_TRIGGERS`;
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`),
  CONSTRAINT `QRTZ_SIMPLE_TRIGGERS_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
-- ----------------------------
-- Table structure for QRTZ_SIMPROP_TRIGGERS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_SIMPROP_TRIGGERS`;
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) DEFAULT NULL,
  `STR_PROP_2` varchar(512) DEFAULT NULL,
  `STR_PROP_3` varchar(512) DEFAULT NULL,
  `INT_PROP_1` int(11) DEFAULT NULL,
  `INT_PROP_2` int(11) DEFAULT NULL,
  `LONG_PROP_1` bigint(20) DEFAULT NULL,
  `LONG_PROP_2` bigint(20) DEFAULT NULL,
  `DEC_PROP_1` decimal(13,4) DEFAULT NULL,
  `DEC_PROP_2` decimal(13,4) DEFAULT NULL,
  `BOOL_PROP_1` varchar(1) DEFAULT NULL,
  `BOOL_PROP_2` varchar(1) DEFAULT NULL,
  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
  CONSTRAINT `QRTZ_SIMPROP_TRIGGERS_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
-- ----------------------------
-- Table structure for QRTZ_TRIGGERS
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_TRIGGERS`;
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) DEFAULT NULL,
  `NEXT_FIRE_TIME` bigint(13) DEFAULT NULL,
  `PREV_FIRE_TIME` bigint(13) DEFAULT NULL,
  `PRIORITY` int(11) DEFAULT NULL,
  `TRIGGER_STATE` varchar(16) NOT NULL,
  `TRIGGER_TYPE` varchar(8) NOT NULL,
  `START_TIME` bigint(13) NOT NULL,
  `END_TIME` bigint(13) DEFAULT NULL,
  `CALENDAR_NAME` varchar(200) DEFAULT NULL,
  `MISFIRE_INSTR` smallint(2) DEFAULT NULL,
  `JOB_DATA` blob,
  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
  KEY `IDX_QRTZ_T_J` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),
  KEY `IDX_QRTZ_T_JG` (`SCHED_NAME`,`JOB_GROUP`),
  KEY `IDX_QRTZ_T_C` (`SCHED_NAME`,`CALENDAR_NAME`),
  KEY `IDX_QRTZ_T_G` (`SCHED_NAME`,`TRIGGER_GROUP`),
  KEY `IDX_QRTZ_T_STATE` (`SCHED_NAME`,`TRIGGER_STATE`),
  KEY `IDX_QRTZ_T_N_STATE` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),
  KEY `IDX_QRTZ_T_N_G_STATE` (`SCHED_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),
  KEY `IDX_QRTZ_T_NEXT_FIRE_TIME` (`SCHED_NAME`,`NEXT_FIRE_TIME`),
  KEY `IDX_QRTZ_T_NFT_ST` (`SCHED_NAME`,`TRIGGER_STATE`,`NEXT_FIRE_TIME`),
  KEY `IDX_QRTZ_T_NFT_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`),
  KEY `IDX_QRTZ_T_NFT_ST_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_STATE`),
  KEY `IDX_QRTZ_T_NFT_ST_MISFIRE_GRP` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),
  CONSTRAINT `QRTZ_TRIGGERS_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) REFERENCES `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

What I implemented is to persist the task to the database, so I need to override the quartz.properties file of the framework itself

#使用自己的配置文件
org.quartz.jobStore.useProperties:false
#默认或是自己改名字都行
org.quartz.scheduler.instanceName:DefaultQuartzScheduler
#如果使用集群,instanceId必须唯一,设置成AUTO
org.quartz.scheduler.instanceId=AUTO
org.quartz.threadPool.class:org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount:10
org.quartz.threadPool.threadPriority:5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread:true
#存储方式使用JobStoreTX,也就是数据库
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#是否使用集群(如果项目只部署到 一台服务器,就不用了)
org.quartz.jobStore.isClustered=false
org.quartz.jobStore.clusterCheckinInterval=20000
org.quartz.jobStore.tablePrefix=qrtz_
org.quartz.jobStore.dataSource=myDS

(Note: I put the configuration of the database loaded in the configuration class QuartzConfiguration) The
startup project can see from the information in the console whether the mode of persisting to data has been successfully enabled

10:44:39.576  INFO org.quartz.impl.jdbcjobstore.JobStoreTX  : JobStoreTX initialized.
10:44:39.578  INFO org.quartz.core.QuartzScheduler          : Scheduler meta-data: Quartz Scheduler (v2.3.0) 'schedulerFactoryBean' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  NOT STARTED.
  Currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
  Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is not clustered.

10:44:39.578  INFO org.quartz.impl.StdSchedulerFactory      : Quartz scheduler 'schedulerFactoryBean' initialized from an externally provided properties instance.
10:44:39.578  INFO org.quartz.impl.StdSchedulerFactory      : Quartz scheduler version: 2.3.0
10:44:39.579  INFO org.quartz.core.QuartzScheduler          : JobFactory set to: com.ywsoftware.oa.task.quartz.config.JobFactory@5486ee92
10:44:39.638  INFO c.m.v.c.i.AbstractPoolBackedDataSource   : Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1bqooz6aa1hbh2ue1dtk02|3f06ebe0, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.cj.jdbc.Driver, extensions -> {
    
    }, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1bqooz6aa1hbh2ue1dtk02|3f06ebe0, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://172.16.10.244:3306/rc_dev_test_data?characterEncoding=utf8&serverTimezone=GMT%2B8&allowMultiQueries=true&useSSL=false, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 5, maxStatements -> 0, maxStatementsPerConnection -> 120, minPoolSize -> 1, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {
    
    user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {
    
    }, usesTraditionalReflectiveProxies -> false ]

Several main configuration classes
mainly solve the problem:
1. We need to inject some business classes into the job class, but the job class is not a bean managed by spring, so we need to hand over the job class to spring for management through the following configuration
2. Distinguish Environment given timing task configuration different database
configuration 1:

package com.ywsoftware.oa.task.quartz.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceConfig {
    
    
    private String url;
    private String username;
    private String password;
}

(This is to read the database configuration from the configuration file we usually configure according to the environment)

configuration two

package com.ywsoftware.oa.task.quartz.config;

import org.quartz.Scheduler;
import org.quartz.spi.JobFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 描述 : quartz 配置信息
 */
@Configuration
public class QuartzConfiguration {
    
    
    @Autowired
    private JobFactory jobFactory;
    @Autowired
    private DataSourceConfig dataSourceConfig;

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
    
    
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(jobFactory);
        // 用于quartz集群,QuartzScheduler 启动时更新己存在的Job
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        schedulerFactoryBean.setStartupDelay(1);
        Map<String, Object> quartzMap = new HashMap<>();
        quartzMap.put("org.quartz.dataSource.myDS.URL", dataSourceConfig.getUrl());
        quartzMap.put("org.quartz.dataSource.myDS.user", dataSourceConfig.getUsername());
        quartzMap.put("org.quartz.dataSource.myDS.password", dataSourceConfig.getPassword());
        quartzMap.put("org.quartz.dataSource.myDS.driver", "com.mysql.cj.jdbc.Driver");
        quartzMap.put("org.quartz.dataSource.myDS.maxConnections", "5");
        Properties quartzProperties = new Properties();
        quartzProperties.putAll(quartzMap);
        schedulerFactoryBean.setConfigLocation(new ClassPathResource("quartz.properties"));
        schedulerFactoryBean.setQuartzProperties(quartzProperties);
        return schedulerFactoryBean;
    }

    @Bean
    public Scheduler scheduler() {
    
    
        return schedulerFactoryBean().getScheduler();
    }
}

Note: I believe that many people need to distinguish the development of the environment when they actually use it, such as prod, gray, dev, and different libraries in different environments, so they need to manually load the configuration. Use the following to distinguish databases with different environment configurations

		quartzMap.put("org.quartz.dataSource.myDS.URL", dataSourceConfig.getUrl());
        quartzMap.put("org.quartz.dataSource.myDS.user", dataSourceConfig.getUsername());
        quartzMap.put("org.quartz.dataSource.myDS.password", dataSourceConfig.getPassword());
        quartzMap.put("org.quartz.dataSource.myDS.driver", "com.mysql.cj.jdbc.Driver");
        quartzMap.put("org.quartz.dataSource.myDS.maxConnections", "5");
        Properties quartzProperties = new Properties();
        quartzProperties.putAll(quartzMap);
        schedulerFactoryBean.setConfigLocation(new ClassPathResource("quartz.properties"));
        schedulerFactoryBean.setQuartzProperties(quartzProperties);

Configuration three

package com.ywsoftware.oa.task.quartz.config;

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;

/**
 * 描述:
 */
@Component
public class JobFactory extends AdaptableJobFactory {
    
    

    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
    
    
        // 调用父类的方法
        Object jobInstance = super.createJobInstance(bundle);
        // 进行注入
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

Business Execution Method

package com.ywsoftware.oa.task.quartz.job;

import com.ywsoftware.oa.task.quartz.mapper.JobRuHistoryMapper;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
@DisallowConcurrentExecution
public class CustomDataJob extends QuartzJobBean {
    
      
    @Autowired
    private JobRuHistoryMapper jobRuHistoryMapper;

    @Override
    protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
    
    
      // 实现自己的业务方法
    }
}
(注:@DisallowConcurrentExecution 禁止并发执行多个相同定义的JobDetail,可以不加,看你得任务执行时间和周期)

Addition, deletion and modification of tasks

package com.ywsoftware.oa.task.quartz.service;

import com.ywsoftware.oa.data.core.schema.JobDesign;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.List;
import java.util.Map;

public interface QuartzService {
    
    

    /**
     * 增加一个job
     *
     * @param jobClass     任务实现类
     * @param jobName      任务名称
     * @param jobGroupName 任务组名
     * @param jobTime      时间表达式 (这是每隔多少秒为一次任务)
     * @param jobTimes     运行的次数 (<0:表示不限次数)
     * @param jobData      参数
     */
    void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int jobTime,
                int jobTimes, Map jobData);

    /**
     * 增加一个job
     *
     * @param jobClass     任务实现类
     */
    void addJob(Class<? extends QuartzJobBean> jobClass, JobDesign jobDesign);

    /**
     * 修改 一个job的 时间表达式
     * @param jobName 任务名称
     * @param jobGroupName 任务组名
     * @param jobTime 任务执行时间
     */
    void updateJob(String jobName, String jobGroupName, String jobTime);

    /**
     * 删除任务一个job
     *
     * @param jobName      任务名称
     * @param jobGroupName 任务组名
     */
    void deleteJob(String jobName, String jobGroupName);

    /**
     * 暂停一个job
     * @param jobName 任务名称
     * @param jobGroupName 任务组名
     */
    void pauseJob(String jobName, String jobGroupName);

    /**
     * 恢复一个job
     *
     * @param jobName 任务名称
     * @param jobGroupName 任务组名
     */
    void resumeJob(String jobName, String jobGroupName);

    /**
     * 立即执行一个job
     *
     * @param jobName 任务名称
     * @param jobGroupName 任务组名
     */
    void runAJobNow(String jobName, String jobGroupName);

    /**
     * 获取所有计划中的任务列表
     *
     * @return
     */
    List<Map<String, Object>> queryAllJob();

    /**
     * 获取所有正在运行的job
     *
     * @return
     */
    List<Map<String, Object>> queryRunJob();

    List<Map<String, Object>> queryJobByJobName(String jobName, String GroupName);

}

package com.ywsoftware.oa.task.quartz.service;

import com.ywsoftware.oa.data.core.schema.JobDesign;
import com.ywsoftware.oa.task.util.CronUtil;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Service
public class QuartzServiceImpl implements QuartzService {
    
    
    private static final String GROUP_NAME = "redcat";

    @Autowired
    private Scheduler scheduler;

    @PostConstruct
    public void startScheduler() {
    
    
        try {
    
    
            scheduler.start();
        } catch (SchedulerException scheduler) {
    
    
            scheduler.printStackTrace();
        }
    }

    /**
     * 增加一个job
     *
     * @param jobClass     任务实现类
     * @param jobName      任务名称
     * @param jobGroupName 任务组名
     * @param jobTime      时间表达式 (这是每隔多少秒为一次任务)
     * @param jobTimes     运行的次数 (<0:表示不限次数)
     * @param jobData      参数
     */
    @Override
    public void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int jobTime,
                       int jobTimes, Map jobData) {
    
    
        try {
    
    
            // 任务名称和组构成任务key
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)
                    .build();
            // 设置job参数
            if (jobData != null && jobData.size() > 0) {
    
    
                jobDetail.getJobDataMap().putAll(jobData);
            }
            // 使用simpleTrigger规则
            Trigger trigger = null;
            if (jobTimes < 0) {
    
    
                trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
                        .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(jobTime))
                        .startNow().build();
            } else {
    
    
                trigger = TriggerBuilder
                        .newTrigger().withIdentity(jobName, jobGroupName).withSchedule(SimpleScheduleBuilder
                                .repeatSecondlyForever(1).withIntervalInSeconds(jobTime).withRepeatCount(jobTimes))
                        .startNow().build();
            }
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException scheduler) {
    
    
            scheduler.printStackTrace();
        }
    }

    /**
     * 增加一个job
     *
     * @param jobClass     任务实现类
     */
    @Override
    public void addJob(Class<? extends QuartzJobBean> jobClass, JobDesign jobDesign) {
    
    
        try {
    
    
            String jobName = jobDesign.getJobId().replaceAll("-", "");
            String jobTimeCron = CronUtil.getCornByTime(jobDesign.getStartTime(), jobDesign.getCycle(),
                    Integer.valueOf(jobDesign.getTimeInterval() == null ? "1" : jobDesign.getTimeInterval()));
            // 创建jobDetail实例,绑定Job实现类
            // 指明job的名称,所在组的名称,以及绑定job类
            // 任务名称和组构成任务key
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, GROUP_NAME)
                    .build();
            // 设置job参数
            jobDetail.getJobDataMap().putAll(jobDesign.getJobDataMap());
            // 定义调度触发规则
            // 使用cornTrigger规则
            // 触发器key
            // 触发器key
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, GROUP_NAME)
                    .startAt(jobDesign.getStartTime())
                    .withSchedule(CronScheduleBuilder.cronSchedule(jobTimeCron).withMisfireHandlingInstructionDoNothing()).build();
            // 把作业和触发器注册到任务调度中
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException scheduler) {
    
    
            scheduler.printStackTrace();
        }
    }

    /**
     * 修改 一个job的 时间表达式
     *
     * @param jobName 任务名称
     * @param jobGroupName 任务组名
     * @param jobTime 任务执行时间
     */
    @Override
    public void updateJob(String jobName, String jobGroupName, String jobTime) {
    
    
        try {
    
    
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                    .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build();
            // 重启触发器
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException scheduler) {
    
    
            scheduler.printStackTrace();
        }
    }

    /**
     * 删除任务一个job
     *
     * @param jobName      任务名称
     * @param jobGroupName 任务组名
     */
    @Override
    public void deleteJob(String jobName, String jobGroupName) {
    
    
        try {
    
    
            scheduler.deleteJob(new JobKey(jobName, jobGroupName));
        } catch (SchedulerException scheduler) {
    
    
            scheduler.printStackTrace();
        }
    }

    /**
     * 暂停一个job
     *
     * @param jobName 任务名称
     * @param jobGroupName 任务组名
     */
    @Override
    public void pauseJob(String jobName, String jobGroupName) {
    
    
        try {
    
    
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException scheduler) {
    
    
            scheduler.printStackTrace();
        }
    }

    /**
     * 恢复一个job
     *
     * @param jobName 任务名称
     * @param jobGroupName 任务组名
     */
    @Override
    public void resumeJob(String jobName, String jobGroupName) {
    
    
        try {
    
    
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException scheduler) {
    
    
            scheduler.printStackTrace();
        }
    }

    /**
     * 立即执行一个job
     *
     * @param jobName 任务名称
     * @param jobGroupName 任务组名
     */
    @Override
    public void runAJobNow(String jobName, String jobGroupName) {
    
    
        try {
    
    
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException scheduler) {
    
    
            scheduler.printStackTrace();
        }
    }

    /**
     * 获取所有计划中的任务列表
     *
     * @return
     */
    @Override
    public List<Map<String, Object>> queryAllJob() {
    
    
        List<Map<String, Object>> jobList = null;
        try {
    
    
            GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
            Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
            jobList = new ArrayList<Map<String, Object>>();
            for (JobKey jobKey : jobKeys) {
    
    
                List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                for (Trigger trigger : triggers) {
    
    
                    Map<String, Object> map = new HashMap<>();
                    map.put("jobName", jobKey.getName());
                    map.put("jobGroupName", jobKey.getGroup());
                    map.put("description", "触发器:" + trigger.getKey());
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                    map.put("jobStatus", triggerState.name());
                    if (trigger instanceof CronTrigger) {
    
    
                        CronTrigger cronTrigger = (CronTrigger) trigger;
                        String cronExpression = cronTrigger.getCronExpression();
                        map.put("jobTime", cronExpression);
                    }
                    jobList.add(map);
                }
            }
        } catch (SchedulerException scheduler) {
    
    
            scheduler.printStackTrace();
        }
        return jobList;
    }

    /**
     * 获取所有正在运行的job
     *
     * @return
     */
    @Override
    public List<Map<String, Object>> queryRunJob() {
    
    
        List<Map<String, Object>> jobList = null;
        try {
    
    
            List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
            jobList = new ArrayList<Map<String, Object>>(executingJobs.size());
            for (JobExecutionContext executingJob : executingJobs) {
    
    
                Map<String, Object> map = new HashMap<String, Object>();
                JobDetail jobDetail = executingJob.getJobDetail();
                JobKey jobKey = jobDetail.getKey();
                Trigger trigger = executingJob.getTrigger();
                map.put("jobName", jobKey.getName());
                map.put("jobGroupName", jobKey.getGroup());
                map.put("description", "触发器:" + trigger.getKey());
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                map.put("jobStatus", triggerState.name());
                if (trigger instanceof CronTrigger) {
    
    
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    map.put("jobTime", cronExpression);
                }
                jobList.add(map);
            }
        } catch (SchedulerException scheduler) {
    
    
            scheduler.printStackTrace();
        }
        return jobList;
    }

    @Override
    public List<Map<String, Object>> queryJobByJobName(String jobName, String GroupName) {
    
    
        JobKey jobKey = new JobKey(jobName, GroupName);
        List<Map<String, Object>> jobList = new ArrayList<>();
        try {
    
    
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            for (Trigger trigger : triggers) {
    
    
                Map<String, Object> map = new HashMap<>();
                map.put("jobName", jobKey.getName());
                map.put("jobGroupName", jobKey.getGroup());
                map.put("description", "触发器:" + trigger.getKey());
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                map.put("jobStatus", triggerState.name());
                if (trigger instanceof CronTrigger) {
    
    
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    map.put("jobTime", cronExpression);
                }
                jobList.add(map);
            }
        } catch (SchedulerException scheduler) {
    
    
            scheduler.printStackTrace();
        }
        return jobList;
    }

}

I believe everyone will still have a headache how to generate corn expressions, haha, I am the same, let's give a util contributed by a colleague

package com.ywsoftware.oa.task.util;

import com.ywsoftware.oa.exceptions.InvalidParameterAppException;
import com.ywsoftware.oa.infrastructure.utils.DateHelper;

import java.util.Date;

// Cron生成工具
public class CronUtil {
    
    
    public static String getCornByTime(Date startAt, String timeType, Integer time) {
    
    
        String resultCorn;
        // 定时开始的分钟数
        String startAtMinute = DateHelper.ONLY_MINUTE_FORMAT.format(startAt);
        // 定时开始的小时数
        String startAtHour = DateHelper.ONLY_HOUR_FORMAT.format(startAt);
        // 定时开始的日数
        String startAtDay = DateHelper.ONLY_DAY_FORMAT.format(startAt);
        // 定时开始的月数
        String startAtMonth = DateHelper.ONLY_MONTH_FORMAT.format(startAt);
        // 定时开始的年数
        String startAtYear = DateHelper.ONLY_YEAR_FORMAT.format(startAt);
        // 年 月 日 周 时 分 秒
        switch (timeType) {
    
    
            // 当前时间类型位的/前的数字不能写死,应该用*,比如时间单位是每秒时,就秒位就是*/秒数,时间单位是每日时,就日位就是*/日数
            // 0/10 * * * * ? *  表示从0s开始,每10秒执行一次
            case "sec":
                if (time >= 60) {
    
    
                    throw new InvalidParameterAppException("秒数最大值59秒");
                }
                resultCorn = "*/" + String.valueOf(time) + " * * * * ? *";
                break;
            // 0 5/9 * * * ? *  表示从5分钟开始,每9分钟执行一次
            case "min":
                if (time >= 60) {
    
    
                    throw new InvalidParameterAppException("分钟数最大值59分钟");
                }
                resultCorn = "0 " + "*/" + String.valueOf(time) + " * * * ? *";
                break;
            // 0 0 */2 * * ? * 表示从现在开始,每2个小时执行一次
            case "hour":
                if (time >= 24) {
    
    
                    throw new InvalidParameterAppException("小时数最大值23小时");
                }
                resultCorn = "0 " + startAtMinute + " */" + String.valueOf(time) + " * * ? *";
                break;

            // 0/10 * * * * ? *  表示从0s开始,每10秒执行一次
            case "week":
                // TODO 每多少周执行一次定时任务  基本无用,而且开发难度偏大,目前支持的是每周的周几运行定时任务
                // 经理说可以不实现
                resultCorn = "";
                break;
            // 0 0 0 */1 * ? *  表示每1天执行一次
            case "day":
                if (time < 1 || time > 31) {
    
    
                    throw new InvalidParameterAppException("天数最小1天,最大31天");
                }
                resultCorn = "0 " + startAtMinute + " " + startAtHour + " */" + String.valueOf(time) + " * ? *";
                break;
            // 0 0 0 0 1/3 ?   表示从1月开始,每个3月执行一次
            case "month":
                if (time > 12) {
    
    
                    throw new InvalidParameterAppException("月数最大值12月");
                }
                resultCorn = "0 " + startAtMinute + " " + startAtHour + " " + startAtDay + " */" + String.valueOf(time) + " ?";
                break;
            case "year":
                resultCorn = "0 " + startAtMinute + " " + startAtHour + " " + startAtDay + " " + startAtMonth + " ?"
                        + " */" + String.valueOf(time);
                break;
            case "onetime":
                resultCorn = "0 " + startAtMinute + " " + startAtHour + " " + startAtDay + " " + startAtMonth + " ?"
                        + " " + startAtYear;
                break;
            default:
                resultCorn = "";
                break;
        }
        return resultCorn;
    }
}

Guess you like

Origin blog.csdn.net/weixin_42303757/article/details/106800733