一、新建一個springboot工程,並添加依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>name.ealenxie</groupId> <artifactId>SpringBoot-Quartz</artifactId> <version>1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client --> <dependency> <groupId>org.mariadb.jdbc</groupId> <artifactId>mariadb-java-client</artifactId> <version>2.5.2</version> </dependency> <dependency> <!--quartz依賴--> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.3</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.2.3</version> </dependency> <!--日志 start--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> <scope>test</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.25</version> <scope>test</scope> </dependency> <!--日志end--> </dependencies> </project>
二、配置文件application.yml
quartz:
enabled: true
server:
port: 9090
spring:
application:
name: Spring-Quartz-Scheduler #Quartz調度中心
datasource:
url: jdbc:mariadb://localhost:3306/model
username: root
password: 123
tomcat:
initialSize: 20
maxActive: 100
maxIdle: 100
minIdle: 20
maxWait: 10000
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
quartz.properties
#ID設置為自動獲取 每一個必須不同 (所有調度器實例中是唯一的)
org.quartz.scheduler.instanceId=AUTO
#指定調度程序的主線程是否應該是守護線程
org.quartz.scheduler.makeSchedulerThreadDaemon=true
#ThreadPool實現的類名
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
#ThreadPool配置線程守護進程
org.quartz.threadPool.makeThreadsDaemons=true
#線程數量
org.quartz.threadPool.threadCount:20
#線程優先級
org.quartz.threadPool.threadPriority:5
#數據保存方式為持久化
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
#StdJDBCDelegate說明支持集群
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#quartz內部表的前綴
org.quartz.jobStore.tablePrefix=QRTZ_
#是否加入集群
org.quartz.jobStore.isClustered=true
#容許的最大作業延長時間
org.quartz.jobStore.misfireThreshold=25000
三、Quartz核心配置
package com.ealen.config; import org.quartz.spi.JobFactory; import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; 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 org.springframework.scheduling.quartz.SpringBeanJobFactory; import javax.sql.DataSource; import java.io.IOException; import java.util.Properties; /** * Quartz的核心配置類 */ @Configuration public class ConfigureQuartz { //配置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 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; } } }
四、Entity類
package com.ealen.entity; import lombok.Data; import lombok.experimental.Accessors; import javax.persistence.*; import java.io.Serializable; /** * 這里個人示例,可自定義相關屬性 */ @Entity @Table(name = "JOB_ENTITY") @Data @Accessors(chain = true) public class JobEntity implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; //job名稱 private String jobGroup; //job組名 private String cron; //執行的cron private String parameter; //job的參數 private String description; //job描述信息 private String vmParam; //vm參數 private String jarPath; //job的jar路徑 private String status; //job的執行狀態,這里我設置為OPEN/CLOSE且只有該值為OPEN才會執行該Job public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getJobGroup() { return jobGroup; } public void setJobGroup(String jobGroup) { this.jobGroup = jobGroup; } public String getCron() { return cron; } public void setCron(String cron) { this.cron = cron; } public String getParameter() { return parameter; } public void setParameter(String parameter) { this.parameter = parameter; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getVmParam() { return vmParam; } public void setVmParam(String vmParam) { this.vmParam = vmParam; } public String getJarPath() { return jarPath; } public void setJarPath(String jarPath) { this.jarPath = jarPath; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } }
五、實現job接口
Job可以理解為就是一個工作任務,代碼中就是一個實現了org.quartz.Job或org.quartz.StatefulJob接口的java類。當Scheduler決定運行Job時,execute()方法就會被執行。
具體可以干啥:
1、每天定時發送系統郵件
2、在指定的時刻發送一條短信給用戶
3、執行完A任務后希望B任務在10秒后執行
總結就是任何java能做的任務都可以成為一個job。
package com.ealen.job; import com.ealen.util.StringUtil; import lombok.extern.slf4j.Slf4j; import org.quartz.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** * :@DisallowConcurrentExecution : 此標記用在實現Job的類上面,意思是不允許並發執行. * :注意org.quartz.threadPool.threadCount線程池中線程的數量至少要多個,否則@DisallowConcurrentExecution不生效 * :假如Job的設置時間間隔為3秒,但Job執行時間是5秒,設置@DisallowConcurrentExecution以后程序會等任務執行完畢以后再去執行,否則會在3秒時再啟用新的線程執行 */ @DisallowConcurrentExecution @Component @Slf4j public class DynamicJob implements Job { private static final Logger log = LoggerFactory.getLogger(DynamicJob.class); /** * 核心方法,Quartz Job真正的執行邏輯. * * @param executorContext executorContext JobExecutionContext中封裝有Quartz運行所需要的所有信息 * @throws JobExecutionException execute()方法只允許拋出JobExecutionException異常 */ @Override public void execute(JobExecutionContext executorContext) throws JobExecutionException { //JobDetail中的JobDataMap是共用的,從getMergedJobDataMap獲取的JobDataMap是全新的對象 JobDataMap map = executorContext.getMergedJobDataMap(); String jarPath = map.getString("jarPath"); String parameter = map.getString("parameter"); String vmParam = map.getString("vmParam"); log.info("Running Job name : {} ", map.getString("name")); log.info("Running Job description : {}", map.getString("jobDescription")); log.info("Running Job group: {} ", map.getString("jobGroup")); log.info(String.format("Running Job cron : %s", map.getString("cronExpression"))); log.info("Running Job jar path : {} ", jarPath); log.info("Running Job parameter : {} ", parameter); log.info("Running Job vmParam : {} ", vmParam); long startTime = System.currentTimeMillis(); if (!StringUtils.isEmpty(jarPath)) { File jar = new File(jarPath); if (jar.exists()) { ProcessBuilder processBuilder = new ProcessBuilder(); processBuilder.directory(jar.getParentFile()); List<String> commands = new ArrayList<>(); commands.add("java"); if (!StringUtils.isEmpty(vmParam)) commands.add(vmParam); commands.add("-jar"); commands.add(jarPath); if (!StringUtils.isEmpty(parameter)) commands.add(parameter); processBuilder.command(commands); log.info("Running Job details as follows >>>>>>>>>>>>>>>>>>>>: "); log.info("Running Job commands : {} ", StringUtil.getListString(commands)); try { Process process = processBuilder.start(); logProcess(process.getInputStream(), process.getErrorStream()); } catch (IOException e) { throw new JobExecutionException(e); } } else throw new JobExecutionException("Job Jar not found >> " + jarPath); } long endTime = System.currentTimeMillis(); log.info(">>>>>>>>>>>>> Running Job has been completed , cost time : {}ms\n ", (endTime - startTime)); } //記錄Job執行內容 private void logProcess(InputStream inputStream, InputStream errorStream) throws IOException { String inputLine; String errorLine; BufferedReader inputReader = new BufferedReader(new InputStreamReader(inputStream)); BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream)); while (Objects.nonNull(inputLine = inputReader.readLine())) log.info(inputLine); while (Objects.nonNull(errorLine = errorReader.readLine())) log.error(errorLine); } }
六、定義dao接口
package com.ealen.dao; import com.ealen.entity.JobEntity; import org.springframework.data.jpa.repository.JpaRepository; public interface JobEntityRepository extends JpaRepository<JobEntity, Long> { JobEntity getById(Integer id); }
七、service 服務層
package com.ealen.service; import com.ealen.dao.JobEntityRepository; import com.ealen.entity.JobEntity; import com.ealen.job.DynamicJob; import org.quartz.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class DynamicJobService { @Autowired private JobEntityRepository repository; //通過Id獲取Job public JobEntity getJobEntityById(Integer id) { return repository.getById(id); } //從數據庫中加載獲取到所有Job public List<JobEntity> loadJobs() { return repository.findAll(); } //獲取JobDataMap.(Job參數對象) public JobDataMap getJobDataMap(JobEntity job) { JobDataMap map = new JobDataMap(); map.put("name", job.getName()); map.put("jobGroup", job.getJobGroup()); map.put("cronExpression", job.getCron()); map.put("parameter", job.getParameter()); map.put("jobDescription", job.getDescription()); map.put("vmParam", job.getVmParam()); map.put("jarPath", job.getJarPath()); map.put("status", job.getStatus()); return map; } //獲取JobDetail,JobDetail是任務的定義,而Job是任務的執行邏輯,JobDetail里會引用一個Job Class來定義 public JobDetail getJobDetail(JobKey jobKey, String description, JobDataMap map) { return JobBuilder.newJob(DynamicJob.class) .withIdentity(jobKey) .withDescription(description) .setJobData(map) .storeDurably() .build(); } //獲取Trigger (Job的觸發器,執行規則) public Trigger getTrigger(JobEntity job) { return TriggerBuilder.newTrigger() .withIdentity(job.getName(), job.getJobGroup()) .withSchedule(CronScheduleBuilder.cronSchedule(job.getCron())) .build(); } //獲取JobKey,包含Name和Group public JobKey getJobKey(JobEntity job) { return JobKey.jobKey(job.getName(), job.getJobGroup()); } }
八、自定義工具類
package com.ealen.util; import java.util.List; import java.util.Map; /** * 自定義枚舉單例對象 StringUtil */ public enum StringUtil { getStringUtil; //是否為空 public boolean isEmpty(String str) { return (str == null) || (str.length() == 0) || (str.equals("")); } //去空格 public String trim(String str) { return str == null ? null : str.trim(); } //獲取Map參數值 public String getMapString(Map<String, String> map) { String result = ""; for (Map.Entry entry : map.entrySet()) { result += entry.getValue() + " "; } return result; } //獲取List參數值 public static String getListString(List<String> list) { StringBuilder result = new StringBuilder(); for (String s : list) { result.append(s).append(" "); } return result.toString(); } }
九、Dto
package com.ealen.web.dto; import lombok.Data; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; @Data public class ModifyCronDTO { @NotNull(message = "the job id cannot be null") private Integer id; @NotEmpty(message = "the cron cannot be empty") private String cron; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getCron() { return cron; } public void setCron(String cron) { this.cron = cron; } }
十、Controller控制器
package com.ealen.web; import com.ealen.dao.JobEntityRepository; import com.ealen.entity.JobEntity; import com.ealen.job.DynamicJob; import com.ealen.service.DynamicJobService; import com.ealen.web.dto.ModifyCronDTO; import lombok.extern.slf4j.Slf4j; import org.quartz.*; import org.quartz.impl.matchers.GroupMatcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.PostConstruct; import javax.validation.constraints.NotNull; import java.util.Objects; import java.util.Set; @RestController @Slf4j public class JobController { private static final Logger log = LoggerFactory.getLogger(JobController.class); @Autowired private SchedulerFactoryBean schedulerFactoryBean; @Autowired private DynamicJobService jobService; @Autowired private JobEntityRepository repository; //初始化啟動所有的Job @PostConstruct public void initialize() { try { reStartAllJobs(); log.info("init success"); } catch (SchedulerException e) { log.error("printStackTrace ", e); } } //根據ID重啟某個Job @RequestMapping("/refresh/{id}") public String refresh(@PathVariable @NotNull Integer id) throws SchedulerException { String result; JobEntity entity = jobService.getJobEntityById(id); if (Objects.isNull(entity)) return "error: id is not exist "; synchronized (log) { JobKey jobKey = jobService.getJobKey(entity); Scheduler scheduler = schedulerFactoryBean.getScheduler(); scheduler.pauseJob(jobKey); scheduler.unscheduleJob(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup())); scheduler.deleteJob(jobKey); JobDataMap map = jobService.getJobDataMap(entity); JobDetail jobDetail = jobService.getJobDetail(jobKey, entity.getDescription(), map); if (entity.getStatus().equals("OPEN")) { scheduler.scheduleJob(jobDetail, jobService.getTrigger(entity)); result = "Refresh Job : " + entity.getName() + "\t jarPath: " + entity.getJarPath() + " success !"; } else { result = "Refresh Job : " + entity.getName() + "\t jarPath: " + entity.getJarPath() + " failed ! , " + "Because the Job status is " + entity.getStatus(); } } return result; } //重啟數據庫中所有的Job @RequestMapping("/refresh/all") public String refreshAll() { String result; try { reStartAllJobs(); result = "success"; } catch (SchedulerException e) { result = "exception : " + e.getMessage(); } return "refresh all jobs : " + result; } /** * 重新啟動所有的job */ private void reStartAllJobs() throws SchedulerException { synchronized (log) { //只允許一個線程進入操作 Scheduler scheduler = schedulerFactoryBean.getScheduler(); Set<JobKey> set = scheduler.getJobKeys(GroupMatcher.anyGroup()); scheduler.pauseJobs(GroupMatcher.anyGroup()); //暫停所有JOB for (JobKey jobKey : set) { //刪除從數據庫中注冊的所有JOB scheduler.unscheduleJob(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup())); scheduler.deleteJob(jobKey); } for (JobEntity job : jobService.loadJobs()) { //從數據庫中注冊的所有JOB log.info("Job register name : {} , group : {} , cron : {}", job.getName(), job.getJobGroup(), job.getCron()); JobDataMap map = jobService.getJobDataMap(job); JobKey jobKey = jobService.getJobKey(job); JobDetail jobDetail = jobService.getJobDetail(jobKey, job.getDescription(), map); if (job.getStatus().equals("OPEN")) scheduler.scheduleJob(jobDetail, jobService.getTrigger(job)); else log.info("Job jump name : {} , Because {} status is {}", job.getName(), job.getName(), job.getStatus()); } } } //修改某個Job執行的Cron @PostMapping("/modifyJob") public String modifyJob(@RequestBody @Validated ModifyCronDTO dto) { if (!CronExpression.isValidExpression(dto.getCron())) return "cron is invalid !"; synchronized (log) { JobEntity job = jobService.getJobEntityById(dto.getId()); if (job.getStatus().equals("OPEN")) { try { JobKey jobKey = jobService.getJobKey(job); TriggerKey triggerKey = new TriggerKey(jobKey.getName(), jobKey.getGroup()); Scheduler scheduler = schedulerFactoryBean.getScheduler(); CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey); String oldCron = cronTrigger.getCronExpression(); if (!oldCron.equalsIgnoreCase(dto.getCron())) { job.setCron(dto.getCron()); CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(dto.getCron()); CronTrigger trigger = TriggerBuilder.newTrigger() .withIdentity(jobKey.getName(), jobKey.getGroup()) .withSchedule(cronScheduleBuilder) .usingJobData(jobService.getJobDataMap(job)) .build(); scheduler.rescheduleJob(triggerKey, trigger); repository.save(job); } } catch (Exception e) { log.error("printStackTrace", e); } } else { log.info("Job jump name : {} , Because {} status is {}", job.getName(), job.getName(), job.getStatus()); return "modify failure , because the job is closed"; } } return "modify success"; } }
十一、SQL
job_entity.sql 任務
SET FOREIGN_KEY_CHECKS=0; DROP TABLE IF EXISTS `job_entity`; CREATE TABLE `job_entity` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `job_group` varchar(255) DEFAULT NULL, `cron` varchar(255) DEFAULT NULL, `parameter` varchar(255) NOT NULL, `description` varchar(255) DEFAULT NULL, `vm_param` varchar(255) DEFAULT NULL, `jar_path` varchar(255) DEFAULT NULL, `status` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; INSERT INTO `job_entity` VALUES ('1', 'first', 'helloworld', '0/2 * * * * ? ', '1', '第一個', '', null, 'OPEN'); INSERT INTO `job_entity` VALUES ('2', 'second', 'helloworld', '0/5 * * * * ? ', '2', '第二個', null, null, 'OPEN'); INSERT INTO `job_entity` VALUES ('4', 'third', 'helloworld', '0/15 * * * * ? ', '3', '第三個', null, null, 'OPEN'); INSERT INTO `job_entity` VALUES ('5', 'four', 'helloworld', '0 0/1 * * * ? *', '4', '第四個', null, null, 'CLOSE'); INSERT INTO `job_entity` VALUES ('6', 'OLAY Job', 'Nomal', '0 0/2 * * * ?', '', '第五個', null, 'C:\\EalenXie\\Download\\JDE-Order-1.0-SNAPSHOT.jar', 'CLOSE');
quartz_innodb.sql
-- in your Quartz properties file, you'll need to set org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate -- 你需要在你的quartz.properties文件中設置org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate -- StdJDBCDelegate說明支持集群,所有的任務信息都會保存到數據庫中,可以控制事物,還有就是如果應用服務器關閉或者重啟,任務信息都不會丟失,並且可以恢復因服務器關閉或者重啟而導致執行失敗的任務 -- This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM -- 這是來自quartz的腳本,在MySQL數據庫中創建以下的表,修改為使用INNODB而不是MYISAM -- 你需要在數據庫中執行以下的sql腳本 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; -- 存儲每一個已配置的Job的詳細信息 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)) ENGINE=InnoDB; -- 存儲已配置的Trigger的信息 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)) ENGINE=InnoDB; -- 存儲已配置的Simple Trigger的信息 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)) ENGINE=InnoDB; -- 存儲Cron Trigger,包括Cron表達式和時區信息 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), 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)) ENGINE=InnoDB; 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)) ENGINE=InnoDB; -- Trigger作為Blob類型存儲(用於Quartz用戶用JDBC創建他們自己定制的Trigger類型,JobStore並不知道如何存儲實例的時候) 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), INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) ENGINE=InnoDB; -- 以Blob類型存儲Quartz的Calendar日歷信息,quartz可配置一個日歷來指定一個時間范圍 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; -- 存儲已暫停的Trigger組的信息 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; -- 存儲與已觸發的Trigger相關的狀態信息,以及相聯Job的執行信息 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)) ENGINE=InnoDB; -- 存儲少量的有關 Scheduler的狀態信息,和別的 Scheduler 實例(假如是用於一個集群中) 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; -- 存儲程序的非觀鎖的信息(假如使用了悲觀鎖) CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(120) NOT NULL, LOCK_NAME VARCHAR(40) NOT NULL, PRIMARY KEY (SCHED_NAME,LOCK_NAME)) ENGINE=InnoDB; CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY); CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME); CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME); CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY); CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP); CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); commit;
項目結構圖:
測試
數據庫 顯示:
0 0/3 * * * ? 解釋 :三分鍾執行
0/15 * * * * ? 解釋 :十五秒執行
表達式 生成地址: http://cron.qqe2.com/
GitHub地址:https://github.com/nongzihong/SpringBoot-Quartz