Sprinboot整合Quartz實現定時任務調度管理
版本說明:
springboot版本:2.0.0.RELEASE
quartz版本:2.3.0
Quartz官網:http://www.quartz-scheduler.org/
Quartz是一款開源的定時任務調度框架,本文主要記錄一下在工作中使用springboot整合quartz實現定時任務調度管理的用例。內容主要有:springboot整合quartz相關配置、實現基於simpleTrigger的定時任務、實現基於cronTrigger的定時任務。
1 springboot整合Quartz相關配置
在之前,先創建一個springboot項目。
1.1 引入依賴
springboot整合quartz需要依賴兩個包,quartz-jobs和spring-boot-starter-quartz下面我們在pom.xml文件里加入我們所需要的依賴包
<!--quartz-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
1.2未整合前使用Quartz
參考官網:http://www.quartz-scheduler.org/documentation/quartz-2.2.x/quick-start.html
在整合spring和quartz之前,我們來看一下,如何以普通的方式使用Quartz。
1.2.1 創建可調度Job
在項目中,創建一個job包用來存放我們使用Quartz調度的job。然後我們創建一個HelloJob.java類,來寫我們的Job裏的邏輯。HelloJob類需要繼承org.quartz.Job接口並實現接口裏的execute方法,這裏我們只是簡單的輸出了一句話。代碼如下:
package com.example.quartz.job;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* Created by shirukai on 2018/9/6
*/
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("這裏可以執行我們的業務邏輯");
}
}
1.2.2 使用Quartz調度HelloJob
如上我們已經創建了一個HelloJob類,現在我們要寫一個main方法,使用Quartz對HelloJob進行定時調度。
實現步驟如下:
第一步:使用StdSchedulerFactory工廠創建一個Scheduler實例
第二步:創建一個JobDetail並綁定HelloJob,設置jobName和group
第三步:創建一個Trigger,用以設置定時任務的時間、週期等屬性
第四步:將JobDetail和Trigger傳出Scheduler進行調度
代碼如下:
package com.example.quartz;
import com.example.quartz.job.HelloJob;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
/**
* Created by shirukai on 2018/9/6
*/
public class QuartzTest {
public static void main(String[] args) throws Exception {
//從工廠創建scheduler實例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//開啓scheduler
scheduler.start();
//定義一個job,並綁定我們的HelloJob
JobDetail jobDetail = newJob(HelloJob.class)
.withIdentity("job1", "group1")
.build();
//定義一個simple trigger,設置重複次數爲10次,週期爲2秒
Trigger trigger = newTrigger()
.withIdentity("job1", "group1")
.startNow()
.withSchedule(simpleSchedule().withIntervalInSeconds(1).withRepeatCount(10)).build();
//使用scheduler進行job調度
scheduler.scheduleJob(jobDetail, trigger);
}
}
執行上述main方法,效果如下;
1.2.3 在HelloJob中使用Spring IOC容器
下面我們改寫HelloJob,看看我們的定時任務能不能調用我們註冊到Spring中的業務。
首先創建一個service包,用於存放我們spring中業務邏輯,並創建一個QuartzService類。
package com.example.quartz.service;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.springframework.stereotype.Service;
/**
* Created by shirukai on 2018/9/6
*/
@Service
public class QuartzService {
public void printJobInfo(JobExecutionContext context) {
//從上下文中獲取JobDetail
JobDetail jobDetail = context.getJobDetail();
String jobName = jobDetail.getKey().getName();
String group = jobDetail.getKey().getGroup();
System.out.println("Schedule job name is:" + jobName);
System.out.println("Schedule job group is:" + group);
}
}
修改HelloJob類
package com.example.quartz.job;
import com.example.quartz.service.QuartzService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Created by shirukai on 2018/9/6
*/
public class HelloJob implements Job {
@Autowired
QuartzService quartzService;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("這裏可以執行我們的業務邏輯");
quartzService.printJobInfo(context);
}
}
然後執行main方法,發現報錯,這是因爲我們使用了spring的IOC容器,所以我們要啓動spring才能進行測試。否則我們獲取不到我們注入到spring裏的bean,會得到空指針異常。
創建上面main方法的junit單元測試類,並啓用spring如下所示:
package com.example.quartz;
import com.example.quartz.job.HelloJob;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
/**
* Created by shirukai on 2018/9/6
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class QuartzTestTest {
@Test
public void main() throws Exception {
//從工廠創建scheduler實例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//開啓scheduler
scheduler.start();
//定義一個job,並綁定我們的HelloJob
JobDetail jobDetail = newJob(HelloJob.class)
.withIdentity("job1", "group1")
.build();
//定義一個simple trigger,設置重複次數爲10次,週期爲2秒
Trigger trigger = newTrigger()
.withIdentity("job1", "group1")
.startNow()
.withSchedule(simpleSchedule().withIntervalInSeconds(1).withRepeatCount(10)).build();
//使用scheduler進行job調度
scheduler.scheduleJob(jobDetail, trigger);
}
}
執行單元測試之後,發現仍然報空指針異常,這是爲什麼呢,因爲我們沒有與spring整合,我們的job裏是沒法注入spring ioc管理的bean的,也就是說,沒法在job裏調用spring裏的業務邏輯。所以接下來我們來看一下spring如何整合Quartz。
1.3 Springboot整合Quartz
在項目目錄下創建一個conf包用來存放我們Quartz的相關配置。
然後創建一個JobFactory類,用於將JobFactory注入到spring裏。如下所示:
package com.example.quartz.conf;
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;
/**
* Created by shirukai on 2018/9/4
*/
@Component
public class JobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//調用父類的方法
Object jobInstance = super.createJobInstance(bundle);
//進行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
在創建一個QuartzConfig類,用於注入Scheduler相關的Bean
package com.example.quartz.conf;
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 javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;
/**
* Created by shirukai on 2018/9/4
*/
@Configuration
public class QuartzConfig {
@Autowired
private JobFactory jobFactory;
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setJobFactory(jobFactory);
return schedulerFactoryBean;
}
// 創建schedule
@Bean(name = "scheduler")
public Scheduler scheduler() throws IOException {
return schedulerFactoryBean().getScheduler();
}
}
這時我們已經將Quartz與Springboot簡單的整合到一起,下面我們再次修改一下單元測試類裏的方法,不再使用工廠類去創建Scheduler實例,而是通過註解從spring的ioc容器裏拿到對應的實例,代碼如下:
package com.example.quartz;
import com.example.quartz.job.HelloJob;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
/**
* Created by shirukai on 2018/9/6
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class QuartzTestTest {
@Autowired
Scheduler scheduler;
@Test
public void main() throws Exception {
//開啓scheduler
scheduler.start();
//定義一個job,並綁定我們的HelloJob
JobDetail jobDetail = newJob(HelloJob.class)
.withIdentity("job1", "group1")
.build();
//定義一個simple trigger,設置重複次數爲10次,週期爲2秒
Trigger trigger = newTrigger()
.withIdentity("job1", "group1")
.startNow()
.withSchedule(simpleSchedule().withIntervalInSeconds(1).withRepeatCount(10)).build();
//使用scheduler進行job調度
scheduler.scheduleJob(jobDetail, trigger);
}
}
運行測試類,成功執行。效果如下:
1.4 自定義配置文件與持久化
這一小節主要記錄一下Springboot與Quartz的深度整合,一個是自定義Quartz的配置文件、另一個是Quartz定時任務的持久化。
1.4.1 自定義Quartz的配置文件
在項目resources目錄下創建一個quartz.properties配置文件,內容如下:
#使用自己的配置文件
org.quartz.jobStore.useProperties:true
#默認或是自己改名字都行
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
在上面我們創建的QuartzConfig類中注入我們的配置文件
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setQuartzProperties(quartzProperties());
schedulerFactoryBean.setJobFactory(jobFactory);
return schedulerFactoryBean;
}
這樣我們就可以使用自定義的配置文件了。
1.4.2 Quartz定時任務的持久化
默認情況下,Quartz是將我們的定時任務的記錄保存到內存裏,等我們再次啓動項目的時候,我們之前設置的定時任務都會被清空,無法持久化。當然Quartz可以將記錄持久化到數據庫中,下面將從自定義DataSource持久化數據和使用Springboot的DataSource兩方面來持久化Quartz的數據。
1.4.2.1 自定義DataSource
首先在配置文件中添加如下內容,用以配置數據庫相關信息:
#存儲方式使用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
#配置數據源
#數據庫中quartz表的表名前綴
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/springboot?characterEncoding=utf-8
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = hollysys
org.quartz.dataSource.myDS.maxConnections = 5
這樣配置之後我們就可以將Quartz數據持久化到我們指定的數據庫了,但是僅僅是這樣操作是不行的,回報如下錯誤:
從錯誤信息可以看出,與Quartz相關的表不存在。我們需要創建相應的表,建表腳本在官網都可以download。
官網地址:http://www.quartz-scheduler.org/downloads/
到官網下載源碼,然後在源碼quartz-2.3.0/docs/dbTables目錄下可以找到所有數據庫的建表語句,
這裏提供一下2.3.0版mysql innoDB的建表腳本:
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))
ENGINE=InnoDB;
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;
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;
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;
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;
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;
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;
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;
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;
手動執行sql建表腳本,將所需要的表創建到數據庫裏,關於如何自動初始化建表腳本,後面將會補充到。
1.4.2.2 使用Springboot的DataSource
除了使用我們在配置文件中指定的數據源外,我們話可以使用springboot項目中配置的數據源。
首先需要註釋掉配置文件中與數據源相關的配置,如下所示:
#存儲方式使用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
#
##配置數據源
##數據庫中quartz表的表名前綴
#
#org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
#org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/springboot?characterEncoding=utf-8
#org.quartz.dataSource.myDS.user = root
#org.quartz.dataSource.myDS.password = hollysys
#org.quartz.dataSource.myDS.maxConnections = 5
然後再QuartzConfig類中設置我們的數據源,分爲兩步;
第一步 從spring的ioc容器中獲取datasource
@Autowired
DataSource dataSource;
第二步 將獲取到的datasource設置到SchedulerFactoryBean裏
schedulerFactoryBean.setDataSource(dataSource);
這樣我們就可以使用項目中的datasource了。
2 實現基於simpleTrigger的定時任務
先講一下我們將Quartz與Springboot整合實現定時任務管理的實現思路:
- 使用自己的表來保存定時任務相關信息
- 封裝Quartz相關操作提供基於simpleTrigger和cronTrigger的定時任務設置接口
- 對外提供相關操作的API
2.1 創建Schedule表
利用springboot創建Schedule表,用以來保存我們定時任務相關的信息。
2.1.1 創建ScheduleStatusEnum.java枚舉類
在項目entity包下創建定時任務狀態枚舉類,用來映射定時任務的狀態
package com.example.quartz.entity;
/**
* Created by shirukai on 2018/9/4
*/
public enum ScheduleStatusEnum {
ACTIVATED(1, "已激活"),
INACTIVATED(0, "未激活");
private int state;
private String stateInfo;
ScheduleStatusEnum(int state, String stateInfo) {
this.state = state;
this.stateInfo = stateInfo;
}
public int getState() {
return state;
}
public String getStateInfo() {
return stateInfo;
}
}
2.1.2 創建Schedule.java的實體類
在項目entity包下創建Sechedule.java實體類
package com.example.quartz.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
/**
* Created by shirukai on 2018/9/3
*/
@Entity
public class Schedule implements Serializable {
@Id
private String id;
private String triggerInfo;
@Enumerated(EnumType.STRING)
private ScheduleStatusEnum status;
private String groupName;
private String jobName;
private int record;//運行記錄
@Temporal(TemporalType.TIMESTAMP)
@Column(updatable = false)
@CreationTimestamp
private Date createdTimestamp;
@JsonIgnore
@Temporal(TemporalType.TIMESTAMP)
@Column(insertable = false)
@UpdateTimestamp
private Date updatedTimestamp;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTriggerInfo() {
return triggerInfo;
}
public void setTriggerInfo(String triggerInfo) {
this.triggerInfo = triggerInfo;
}
public ScheduleStatusEnum getStatus() {
return status;
}
public void setStatus(ScheduleStatusEnum status) {
this.status = status;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public int getRecord() {
return record;
}
public void setRecord(int record) {
this.record = record;
}
public Date getCreatedTimestamp() {
return createdTimestamp;
}
public void setCreatedTimestamp(Date createdTimestamp) {
this.createdTimestamp = createdTimestamp;
}
public Date getUpdatedTimestamp() {
return updatedTimestamp;
}
public void setUpdatedTimestamp(Date updatedTimestamp) {
this.updatedTimestamp = updatedTimestamp;
}
}
2.1.3 創建 ScheduleRepository.java
在項目repository包下創建ScheduleRepository.java,用以使用jpa對數據庫表進行相關的操作。
package com.example.quartz.repository;
import com.example.quartz.entity.Schedule;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* Created by shirukai on 2018/9/4
*/
public interface ScheduleRepository extends JpaRepository<Schedule, String> {
Schedule findScheduleByJobNameAndGroupName(String jobName, String groupName);
Schedule findScheduleById(String scheduleId);
}
至此我們Schedule表相關的操作就已經完成了,啓動項目後,我們的表會被自動創建。
2.2 基於SimpleTrigger封裝Quartz相關操作
2.2.1 創建SimpleScheduleDTO.java實體類
創建DTO類是爲了方便我們對象之間的數據傳輸,格式如下所示;
{
"startTime":0,
"repeatCount": 100,
"period": {
"time": "5",
"unit": "minutes"
},
"endTime":0
}
在項目dto包想創建ScheduleDTO.java實體類
package com.example.quartz.dto;
/**
* Created by shirukai on 2018/9/7
*/
public class ScheduleDTO {
private String jobName;
private String group;
private long startTime;
private long endTime;
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public long getEndTime() {
return endTime;
}
public void setEndTime(long endTime) {
this.endTime = endTime;
}
}
同目錄下創建Period類映射JSON中的period字段
package com.example.quartz.dto;
/**
* Created by shirukai on 2018/9/7
*/
public class Period {
private long time;
private String unit;
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
}
同目錄下創建SimpleScheduleDTO類繼承上面的ScheduleDTO類
package com.example.quartz.dto;
/**
* Created by shirukai on 2018/9/7
*/
public class SimpleScheduleDTO extends ScheduleDTO {
private int repeatCount;
private Period period;
public int getRepeatCount() {
return repeatCount;
}
public void setRepeatCount(int repeatCount) {
this.repeatCount = repeatCount;
}
public Period getPeriod() {
return period;
}
public void setPeriod(Period period) {
this.period = period;
}
}
2.2.2 創建ScheduleManager
創建定時任務管理器ScheduleManager用以封裝Quartz相關的操作。
2.2.2.1 在項目manager包下創建ScheduleManager.java類
創建ScheduleManager類,從spring ioc中注入Quartz的Scheduler,並將ScheduleManager類使用@Component註解註冊到Spring裏。
package com.example.quartz.manager;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Created by shirukai on 2018/9/7
* 定時任務管理器
*/
@Component
public class ScheduleManager {
@Autowired
Scheduler scheduler;
//todo
}
2.2.2.2 構建SimpleScheduleBuilder
根據週期參數
"period": {
"time": "5",
"unit": "minutes"
},
構建SimpleScheduleBuilder。如參數表示每5分鐘爲一個週期,所以我們編寫getSimpleScheduleBuilder方法,傳入如上的參數,構建相應的SimpleSchedulerBuilder,代碼如下
/**
* 構建 SimpleScheduleBuilder
*
* @param period 週期參數
* @return SimpleScheduleBuilder
*/
private SimpleScheduleBuilder getSimpeScheduleBuilder(Period period, int repeatCount) {
SimpleScheduleBuilder ssb = SimpleScheduleBuilder.simpleSchedule();
String unit = period.getUnit();
long time = period.getTime();
switch (unit) {
case "milliseconds":
ssb.withIntervalInMilliseconds(time);
break;
case "seconds":
ssb.withIntervalInSeconds((int) time);
break;
case "minutes":
ssb.withIntervalInMinutes((int) time);
break;
case "hours":
ssb.withIntervalInHours((int) time);
break;
case "days":
ssb.withIntervalInHours((int) time * 24);
break;
default:
break;
}
ssb.withRepeatCount(repeatCount);
return ssb;
}
2.2.2.3 構建SimpleTrigger
利用上述生成的SimpleScheduleBuilder和傳入的參數,這裏用SimpleScheduleDTO封裝,構建相應的SimpleTrigger。代碼如下:
/**
* 構建 SimpleTrigger
*
* @param ssd 參數
* @return Trigger
*/
private Trigger getSimpleTrigger(SimpleScheduleDTO ssd) {
String jobName = ssd.getJobName();
String group = ssd.getGroup();
int repeatCount = ssd.getRepeatCount();
TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger()
//設置jobName和group
.withIdentity(jobName, group)
//設置Schedule方式
.withSchedule(getSimpeScheduleBuilder(ssd.getPeriod(), repeatCount));
if (ssd.getStartTime() != 0) {
//設置起始時間
triggerBuilder.startAt(new Date(ssd.getStartTime()));
} else {
triggerBuilder.startNow();
}
if (ssd.getEndTime() != 0) {
//設置終止時間
triggerBuilder.endAt(new Date(ssd.getEndTime()));
}
return triggerBuilder.build();
}
2.2.2.4 創建ScheduleJob
Quartz創建定時任務,通過JobDetail 和Trigger就可以創建,我們編寫createJob方法,通過傳入相應參數來實現創建定時任務的功能,代碼如下:
/**
* 創建Job
* @param jobClass 要調度的類名
* @param sd 調度參數
* @param jobDataMap 數據
* @param trigger trigger
* @return Schedule
*/
private Schedule createJob(
Class<? extends Job> jobClass,
ScheduleDTO sd,
JobDataMap jobDataMap,
Trigger trigger
){
String jobName = sd.getJobName();
String group = sd.getGroup();
//判斷記錄在數據庫是否存在
Schedule schedule = scheduleRepository.findScheduleByJobNameAndGroupName(jobName, group);
if (schedule == null) {
schedule = new Schedule();
} else {
throw new RuntimeException("Schedule job already exists.");
}
String scheduleId = UUID.randomUUID().toString();
try {
if (jobDataMap == null) {
jobDataMap = new JobDataMap();
}
jobDataMap.put("id", scheduleId);
//創建JobDetail
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, group).usingJobData(jobDataMap).build();
schedule.setId(scheduleId);
schedule.setStatus(ScheduleStatusEnum.ACTIVATED);
schedule.setJobName(jobName);
schedule.setGroupName(group);
schedule.setTriggerInfo(JSON.toJSONString(sd));
schedule.setRecord(0);
//保存記錄信息
schedule = scheduleRepository.save(schedule);
//調度執行定時任務
scheduler.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
log.error("Create schedule job error:{}", e.getMessage());
throw new RuntimeException(e);
}
return schedule;
}
編寫一個createSimpleJob方法,用於創建SimpleTrigger類型的Job
/**
* 創建 simple schedule job
*
* @param jobClass job class
* @param ssd 參數
* @param jobDataMap 數據
* @return Schedule
*/
public Schedule createSimpleJob(Class<? extends Job> jobClass,
SimpleScheduleDTO ssd,
JobDataMap jobDataMap) {
Trigger trigger = getSimpleTrigger(ssd);
return createJob(jobClass, ssd, jobDataMap, trigger);
}
2.2.2.5 更新Simple Schedule Job
/**
* 更新simple job
*
* @param scheduleId scheduleId
* @param ssd ssv
* @return Schedule
*/
public Schedule updateSimpleJob(String scheduleId, SimpleScheduleDTO ssd) {
Schedule schedule = getSchedule(scheduleId);
return updateSimpleJob(schedule, ssd);
}
public Schedule updateSimpleJob(Schedule schedule, SimpleScheduleDTO ssd) {
try {
String jobName = schedule.getJobName();
String groupName = schedule.getGroupName();
JobKey jobKey = new JobKey(jobName, groupName);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
//先刪除
scheduler.deleteJob(jobKey);
//重新創建
Trigger trigger = getSimpleTrigger(ssd);
scheduler.scheduleJob(jobDetail, trigger);
//更新元數據
schedule.setRecord(0);
schedule.setTriggerInfo(JSON.toJSONString(ssd));
scheduleRepository.save(schedule);
} catch (SchedulerException e) {
log.error("Update simple schedule job error:{}", e.getMessage());
}
return schedule;
}
public Schedule getSchedule(String scheduleId) {
Schedule schedule = scheduleRepository.findScheduleById(scheduleId);
if (schedule == null) {
throw new RuntimeException("Schedule job does not exist");
}
return schedule;
}
2.2.2.6 暫停job
s/**
* 暫停某個job
*
* @param scheduleId id
*/
public Schedule pauseJob(String scheduleId) {
Schedule schedule = getSchedule(scheduleId);
return pauseJob(schedule);
}
public Schedule pauseJob(Schedule schedule) {
JobKey jobKey = new JobKey(schedule.getJobName(), schedule.getGroupName());
try {
scheduler.pauseJob(jobKey);
schedule.setStatus(ScheduleStatusEnum.INACTIVATED);
scheduleRepository.save(schedule);
} catch (SchedulerException e) {
log.error("Pause schedule job error:{}", e.getMessage());
}
return schedule;
}
2.2.2.7 恢復job
/**
* 恢復某個job
*
* @param scheduleId id
*/
public Schedule resumeJob(String scheduleId) {
Schedule schedule = getSchedule(scheduleId);
return resumeJob(schedule);
}
public Schedule resumeJob(Schedule schedule) {
JobKey jobKey = new JobKey(schedule.getJobName(), schedule.getGroupName());
try {
scheduler.resumeJob(jobKey);
schedule.setStatus(ScheduleStatusEnum.ACTIVATED);
scheduleRepository.save(schedule);
} catch (SchedulerException e) {
log.error("Resume schedule job error:{}", e.getMessage());
}
return schedule;
}
2.2.2.8 刪除job
/**
* 刪除 job
*
* @param scheduleId id
*/
public void deleteJob(String scheduleId) {
Schedule schedule = getSchedule(scheduleId);
deleteJob(schedule);
}
public void deleteJob(Schedule schedule) {
JobKey jobKey = new JobKey(schedule.getJobName(), schedule.getGroupName());
try {
scheduler.deleteJob(jobKey);
scheduleRepository.delete(schedule);
} catch (SchedulerException e) {
log.error("Delete schedule job error:{}", e.getMessage());
}
}
2.3 對外提供相關API
2.3.1 創建ScheduleService
創建ScheduleService,調用ScheduleManager分裝的接口。
package com.example.quartz.service;
import com.example.quartz.dto.SimpleScheduleDTO;
import com.example.quartz.entity.Schedule;
import com.example.quartz.job.HelloJob;
import com.example.quartz.manager.ScheduleManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Created by shirukai on 2018/9/7
*/
@Service
public class ScheduleService {
private final static String GROUP = "TEST_GROUP";
@Autowired
ScheduleManager scheduleManager;
private final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* 設置編排定時任務
*
* @param ssd 定時參數
* @return schedule
*/
public Schedule setSchedule(String joName, SimpleScheduleDTO ssd) {
ssd.setJobName(joName);
ssd.setGroup(GROUP);
return scheduleManager.createSimpleJob(HelloJob.class, ssd, null);
}
/**
* 更新編排定時任務
*
* @param ssd 參數
* @return schedule
*/
public Schedule modifySchedule(String jobName, SimpleScheduleDTO ssd) {
Schedule schedule = scheduleManager.getJobByNameAndGroup(jobName, GROUP);
ssd.setJobName(jobName);
ssd.setGroup(GROUP);
return scheduleManager.updateSimpleJob(schedule, ssd);
}
/**
* 獲取編排定時信息
*
* @param joName id
* @return schedule
*/
public Schedule getSchedule(String joName) {
return scheduleManager.getJobByNameAndGroup(joName, GROUP);
}
/**
* 暫停編排定時任務
*
* @param joName joName
* @return schedule
*/
public Schedule pauseSchedule(String joName) {
Schedule schedule = scheduleManager.getJobByNameAndGroup(joName, GROUP);
return scheduleManager.pauseJob(schedule);
}
/**
* 恢復編排定時任務
*
* @param joName joName
* @return schedule
*/
public Schedule resumeSchedule(String joName) {
Schedule schedule = scheduleManager.getJobByNameAndGroup(joName, GROUP);
return scheduleManager.resumeJob(schedule);
}
/**
* 刪除編排定時任務
*
* @param joName joName
* @return str
*/
public String removerSchedule(String joName) {
Schedule schedule = scheduleManager.getJobByNameAndGroup(joName, GROUP);
scheduleManager.deleteJob(schedule);
return "Delete schedule job succeed.";
}
}
2.3.2 創建SceduleController
創建SceduleCOntroller對外提供可訪問API
package com.example.quartz.controller;
import com.example.quartz.common.rest.RestMessage;
import com.example.quartz.common.util.RestMessageUtil;
import com.example.quartz.dto.SimpleScheduleDTO;
import com.example.quartz.service.ScheduleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* Created by shirukai on 2018/9/7
*/
@RestController
@RequestMapping(value = "/api/v1/schedule/")
public class ScheduleController {
@Autowired
ScheduleService scheduleService;
@PostMapping(value = "/{jobName}/simple")
public RestMessage schedule(
@PathVariable("jobName") String jobName,
@RequestBody SimpleScheduleDTO simpleScheduleDTO
) {
return RestMessageUtil.objectToRestMessage(scheduleService.setSchedule(jobName, simpleScheduleDTO));
}
@PutMapping(value = "/{jobName}/simple")
public RestMessage modifySchedule(
@PathVariable("jobName") String jobName,
@RequestBody SimpleScheduleDTO simpleScheduleDTO
) {
return RestMessageUtil.objectToRestMessage(scheduleService.modifySchedule(jobName, simpleScheduleDTO));
}
@DeleteMapping(value = "/{jobName}")
public RestMessage removeSchedule(
@PathVariable("jobName") String jobName
) {
return RestMessageUtil.objectToRestMessage(scheduleService.removerSchedule(jobName));
}
@PostMapping(value = "/{jobName}/pause")
public RestMessage pauseSchedule(
@PathVariable("jobName") String jobName
) {
return RestMessageUtil.objectToRestMessage(scheduleService.pauseSchedule(jobName));
}
@PostMapping(value = "/{jobName}/resume")
public RestMessage resumeSchedule(
@PathVariable("jobName") String jobName
) {
return RestMessageUtil.objectToRestMessage(scheduleService.resumeSchedule(jobName));
}
@GetMapping(value = "/{jobName}")
public RestMessage scheduleInfo(
@PathVariable("jobName") String jobName
) {
return RestMessageUtil.objectToRestMessage(scheduleService.getSchedule(jobName));
}
}
2.3.3 測試提供的API
在這之前先修改我們的HelloJob類,用以記錄我們執行的條數
package com.example.quartz.job;
import com.example.quartz.entity.Schedule;
import com.example.quartz.repository.ScheduleRepository;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Created by shirukai on 2018/9/7
*/
public class HelloJob implements Job {
@Autowired
ScheduleRepository scheduleRepository;
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
//獲取上下文數據
JobDataMap dataMap = context.getMergedJobDataMap();
String scheduleId = dataMap.getString("id");
Schedule schedule = scheduleRepository.findScheduleById(scheduleId);
log.info("定時任務執行了:{}", scheduleId);
//更新執行條數
schedule.setRecord(schedule.getRecord() + 1);
scheduleRepository.save(schedule);
}
}
2.3.3.1 設置定時任務
請求API:{{hostIP}}/api/v1/schedule/8aaabdfc659f74620165b2ad36b50030/simple
請求類型:POST
請求參數:
{
"repeatCount": 100,
"period": {
"time": "5",
"unit": "seconds"
}
}
響應:
{
"success": true,
"code": 0,
"msg": "操作成功",
"data": {
"id": "218a3db4-e2e6-480e-b520-bac138b6c403",
"triggerInfo": "{\"endTime\":0,\"group\":\"TEST_GROUP\",\"jobName\":\"8aaabdfc659f74620165b2ad36b50030\",\"period\":{\"time\":5,\"unit\":\"seconds\"},\"repeatCount\":100,\"startTime\":0}",
"status": "ACTIVATED",
"groupName": "TEST_GROUP",
"jobName": "8aaabdfc659f74620165b2ad36b50030",
"record": 0,
"createdTimestamp": "2018-09-07T06:50:15.521+0000"
}
}
2.3.3.2 查看定時任務
請求API:{{hostIP}}/api/v1/schedule/8aaabdfc659f74620165b2ad36b50030/
請求類型:GET
請求參數:無
響應:
{
"success": true,
"code": 0,
"msg": "操作成功",
"data": {
"id": "218a3db4-e2e6-480e-b520-bac138b6c403",
"triggerInfo": "{\"endTime\":0,\"group\":\"TEST_GROUP\",\"jobName\":\"8aaabdfc659f74620165b2ad36b50030\",\"period\":{\"time\":5,\"unit\":\"seconds\"},\"repeatCount\":100,\"startTime\":0}",
"status": "ACTIVATED",
"groupName": "TEST_GROUP",
"jobName": "8aaabdfc659f74620165b2ad36b50030",
"record": 9,
"createdTimestamp": "2018-09-07T06:50:16.000+0000"
}
}
2.3.3.3 暫停定時任務
請求API:{{hostIP}}/api/v1/schedule/8aaabdfc659f74620165b2ad36b50030/pause
請求類型:POST
請求參數:無
響應:
{
"success": true,
"code": 0,
"msg": "操作成功",
"data": {
"id": "218a3db4-e2e6-480e-b520-bac138b6c403",
"triggerInfo": "{\"endTime\":0,\"group\":\"TEST_GROUP\",\"jobName\":\"8aaabdfc659f74620165b2ad36b50030\",\"period\":{\"time\":5,\"unit\":\"seconds\"},\"repeatCount\":100,\"startTime\":0}",
"status": "INACTIVATED",
"groupName": "TEST_GROUP",
"jobName": "8aaabdfc659f74620165b2ad36b50030",
"record": 23,
"createdTimestamp": "2018-09-07T06:50:16.000+0000"
}
}
2.3.3.4 恢復定時任務
請求API:{{hostIP}}/api/v1/schedule/8aaabdfc659f74620165b2ad36b50030/resume
請求類型:POST
請求參數:無
響應:
{
"success": true,
"code": 0,
"msg": "操作成功",
"data": {
"id": "218a3db4-e2e6-480e-b520-bac138b6c403",
"triggerInfo": "{\"endTime\":0,\"group\":\"TEST_GROUP\",\"jobName\":\"8aaabdfc659f74620165b2ad36b50030\",\"period\":{\"time\":5,\"unit\":\"seconds\"},\"repeatCount\":100,\"startTime\":0}",
"status": "ACTIVATED",
"groupName": "TEST_GROUP",
"jobName": "8aaabdfc659f74620165b2ad36b50030",
"record": 23,
"createdTimestamp": "2018-09-07T06:50:16.000+0000"
}
}
2.3.3. 5 刪除定時任務
請求API:{{hostIP}}/api/v1/schedule/8aaabdfc659f74620165b2ad36b50030/
請求類型:DELETE
請求參數:無
響應:
{
"success": true,
"code": 0,
"msg": "操作成功",
"data": "Delete schedule job succeed."
}
3 實現基於CronTrigger的定時任務
上面我們已經實現了基於SimpleTrigger的定時任務管理,從Quartz封裝,到對外提供RESTful接口。實現了的定時任務的添加、暫停、恢復、查看、刪除等功能。接下來我們在此基礎上,繼續對Quartz進行分裝,實現基於CronTrigger的定時任務。
3.1 基於CronTrigger分裝Quartz相關操作
3.1.1 創建CronScheduleDTO.java實體類
與SimpleScheduleDTO一樣需要繼承ScheduleDTO類,代碼如下:
package com.example.quartz.dto;
/**
* Created by shirukai on 2018/9/7
*/
public class CronScheduleDTO extends ScheduleDTO {
private String cronExpression;
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
}
3.1.2 構建CronTrigger
在ScheduleManager類裏添加getCronTrigger方法,用於構建CronTrigger
private Trigger getCronTrigger(CronScheduleDTO csd) {
CronScheduleBuilder scb = CronScheduleBuilder.cronSchedule(csd.getCronExpression());
TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger()
.withIdentity(csd.getJobName(), csd.getGroup())
.withSchedule(scb);
if (csd.getStartTime() != 0) {
triggerBuilder.startAt(new Date(csd.getStartTime()));
} else {
triggerBuilder.startNow();
}
if (csd.getEndTime() != 0) {
triggerBuilder.endAt(new Date(csd.getEndTime()));
}
return triggerBuilder.build();
}
3.1.3 構建Cron Schedule Job
在ScheduleManager類裏添加createCronJob方法
/**
* 創建 cron schedule job
*
* @param jobClass 可執行job class
* @param csd 定時參數
* @param jobDataMap 數據
* @return Schedule
*/
public Schedule createCronJob(Class<? extends Job> jobClass, CronScheduleDTO csd, JobDataMap jobDataMap) {
Trigger trigger = getCronTrigger(csd);
return createJob(jobClass, csd, jobDataMap, trigger);
}
3.1.4 更新Job
在ScheduleManager類裏添加updateCronJob方法
public Schedule updateCronJob(String scheduleId, CronScheduleDTO csd) {
Schedule schedule = getSchedule(scheduleId);
return updateCronJob(schedule, csd);
}
public Schedule updateCronJob(Schedule schedule, CronScheduleDTO csd) {
try {
String jobName = schedule.getJobName();
String groupName = schedule.getGroupName();
JobKey jobKey = new JobKey(jobName, groupName);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
//先刪除
scheduler.deleteJob(jobKey);
//重新創建
Trigger trigger = getCronTrigger(csd);
scheduler.scheduleJob(jobDetail, trigger);
//更新元數據
schedule.setRecord(0);
schedule.setTriggerInfo(JSON.toJSONString(csd));
scheduleRepository.save(schedule);
} catch (SchedulerException e) {
log.error("Update cron schedule job error:{}", e.getMessage());
}
return schedule;
}
3.2 對外提供相關API
3.3.1 修改ScheduleService
在ScheduleService類裏添加cron相關的操作,主要是添加Cron定時任務和更新Cron定時任務,代碼如下:
public Schedule setSchedule(String jobName, CronScheduleDTO csd) {
csd.setJobName(jobName);
csd.setGroup(GROUP);
return scheduleManager.createCronJob(HelloJob.class, csd, null);
}
public Schedule modifySchedule(String jobName, CronScheduleDTO csd) {
Schedule schedule = scheduleManager.getJobByNameAndGroup(jobName, GROUP);
csd.setJobName(jobName);
csd.setGroup(GROUP);
return scheduleManager.updateCronJob(schedule, csd);
}
3.3.2 修改ScheduleController
同樣在ScheduleController下添加cron相應的接口
@PostMapping(value = "/{jobName}/cron")
public RestMessage schedule(
@PathVariable("jobName") String jobName,
@RequestBody CronScheduleDTO cronScheduleDTO
) {
return RestMessageUtil.objectToRestMessage(scheduleService.setSchedule(jobName, cronScheduleDTO));
}
@PutMapping(value = "/{jobName}/cron")
public RestMessage modifySchedule(
@PathVariable("jobName") String jobName,
@RequestBody CronScheduleDTO cronScheduleDTO
) {
return RestMessageUtil.objectToRestMessage(scheduleService.modifySchedule(jobName, cronScheduleDTO));
}
3.3.3 測試提供的API
3.3.3.1 設置cron格式的定時任務
請求API:{{hostIP}}/api/v1/schedule/8aaabdfc659f74620165b2ad36b50030/cron
請求類型:POST
請求參數:
{
"cronExpression":"0 0/5 * * * ? *"
}
響應:
{
"success": true,
"code": 0,
"msg": "操作成功",
"data": {
"id": "2d876e3e-f1e1-4ad7-bc64-5a107387cd3f",
"triggerInfo": "{\"cronExpression\":\"0 0/5 * * * ? *\",\"endTime\":0,\"group\":\"TEST_GROUP\",\"jobName\":\"8aaabdfc659f74620165b2ad36b50030\",\"startTime\":0}",
"status": "ACTIVATED",
"groupName": "TEST_GROUP",
"jobName": "8aaabdfc659f74620165b2ad36b50030",
"record": 0,
"createdTimestamp": "2018-09-07T07:32:40.238+0000"
}
}
3.3.3.2 更新cron格式的定時任務
請求API:{{hostIP}}/api/v1/schedule/8aaabdfc659f74620165b2ad36b50030/cron
請求類型:PUT
請求參數:
{
"cronExpression":"0/1 * * * * ? "
}
響應:
{
"success": true,
"code": 0,
"msg": "操作成功",
"data": {
"id": "2d876e3e-f1e1-4ad7-bc64-5a107387cd3f",
"triggerInfo": "{\"cronExpression\":\"0/1 * * * * ? \",\"endTime\":0,\"group\":\"TEST_GROUP\",\"jobName\":\"8aaabdfc659f74620165b2ad36b50030\",\"startTime\":0}",
"status": "ACTIVATED",
"groupName": "TEST_GROUP",
"jobName": "8aaabdfc659f74620165b2ad36b50030",
"record": 0,
"createdTimestamp": "2018-09-07T07:32:40.000+0000"
}
}
4 Springboot整合Quartz進階
4.1 自動初始化Quartz建表SQL
4.1.1 使用Springboot的DataSource時,初始化Quartz建表SQL
使用springboot的DataSource初始化SQL我這裏提供了兩種方式,一種是基於配置的SQL初始化、另一種是基於編程的SQL初始化。下面將分別記錄一下這兩種初始化SQL的方式。
4.1.1.1 基於配置的SQL初始化
基於配置的SQL初始化很簡單,只需要在springboot中添加幾個配置項即可。
首先列一下我們的初始化腳本:
CREATE TABLE IF NOT EXISTS 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;
CREATE TABLE IF NOT EXISTS 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;
CREATE TABLE IF NOT EXISTS 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;
CREATE TABLE IF NOT EXISTS 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 IF NOT EXISTS 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;
CREATE TABLE IF NOT EXISTS 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;
CREATE TABLE IF NOT EXISTS 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;
CREATE TABLE IF NOT EXISTS QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS 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;
CREATE TABLE IF NOT EXISTS 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 IF NOT EXISTS QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;
可以看出,我們的初始化腳本,是基於官網提供的建表腳本進行了改造。改造有兩點:第一點是去掉原有的如果表存在則刪除的腳本,改爲如果表不存在則創建。第二點是去掉索引腳本,因爲索引重複創建會報錯。這個可以利用存儲過程的方式去解決,後面會提到。下面將我們的初始化SQL的腳本配置到springboot的配置文件中。在application.yml配置文件中,添加如下內容:
spring:
#配置數據庫
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot?useSSL=false&characterEncoding=utf-8
username: root
password: hollysys
schema-username: root
schema-password: hollysys
schema: classpath:quartz_tables.sql
initialization-mode: always
這樣的話,系統就會默認初始化我們的sql了。
注意:在springboot2.0之前不需要指定schema-username、schema-password、initialization-mode這三個屬性就可以初始化sql,但是在2.0之後必須要設置這三個屬性,否則spring.datasource.schema屬性無法正常執行。
補充配置帶存儲過程的SQL腳本:
帶添加索引的SQL腳本:
CREATE TABLE IF NOT EXISTS 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;
CREATE TABLE IF NOT EXISTS 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;
CREATE TABLE IF NOT EXISTS 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;
CREATE TABLE IF NOT EXISTS 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 IF NOT EXISTS 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;
CREATE TABLE IF NOT EXISTS 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;
CREATE TABLE IF NOT EXISTS 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;
CREATE TABLE IF NOT EXISTS QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS 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;
CREATE TABLE IF NOT EXISTS 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 IF NOT EXISTS QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;
DROP PROCEDURE IF EXISTS schema_change;
CREATE PROCEDURE schema_change()
DELIMITER $$
BEGIN
DECLARE CurrentDatabase VARCHAR(100);
SELECT DATABASE() INTO CurrentDatabase;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_JOB_DETAILS' AND index_name = 'IDX_QRTZ_J_REQ_RECOVERY') THEN
CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_JOB_DETAILS' AND index_name = 'IDX_QRTZ_J_GRP') THEN
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_J') THEN
CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_JG') THEN
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_C') THEN
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_G') THEN
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_STATE') THEN
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_N_STATE') THEN
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_N_G_STATE') THEN
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_NEXT_FIRE_TIME') THEN
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_NFT_ST') THEN
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_NFT_MISFIRE') THEN
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_NFT_ST_MISFIRE') THEN
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_NFT_ST_MISFIRE_GRP') THEN
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_FIRED_TRIGGERS' AND index_name = 'IDX_QRTZ_FT_TRIG_INST_NAME') THEN
CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_FIRED_TRIGGERS' AND index_name = 'IDX_QRTZ_FT_INST_JOB_REQ_RCVRY') THEN
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_FIRED_TRIGGERS' AND index_name = 'IDX_QRTZ_FT_J_G') THEN
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_FIRED_TRIGGERS' AND index_name = 'IDX_QRTZ_FT_JG') THEN
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_FIRED_TRIGGERS' AND index_name = 'IDX_QRTZ_FT_T_G') THEN
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_FIRED_TRIGGERS' AND index_name = 'IDX_QRTZ_FT_TG') THEN
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
END IF;
END $$
DELIMITER;
CALL schema_change();
剛纔說到基於配置的初始化SQL腳本不能去執行存儲過程,原因是,springboot默認的SQL分隔符爲;,也就是說,當它讀到腳本中的;時就會默認爲這是一條可執行的語句,所以我們的存儲過程就沒有辦法執行,就會報如下錯誤:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the right syntax to use near
'$$ BEGIN DECLARE CurrentDatabase VARCHAR(100)' at line 1
顯然就是說我們的SQL語法有問題,實際上就是上面我所提到的問題導致的,那麼如何解決呢,這裏參考了網上一種解決方法就是修改springboot默認的SQL分隔符。這裏我們修改分隔符爲$$。在配置文件中添加:
spirng.datasource.separator: $$
然後就是修改我們的SQL腳本,將原來的;改爲;$$,存儲過程中的;不變。
腳本如下所示:
CREATE TABLE IF NOT EXISTS 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;$$
CREATE TABLE IF NOT EXISTS 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;$$
CREATE TABLE IF NOT EXISTS 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;$$
CREATE TABLE IF NOT EXISTS 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 IF NOT EXISTS 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;$$
CREATE TABLE IF NOT EXISTS 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;$$
CREATE TABLE IF NOT EXISTS 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;$$
CREATE TABLE IF NOT EXISTS QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;$$
CREATE TABLE IF NOT EXISTS 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;$$
CREATE TABLE IF NOT EXISTS 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 IF NOT EXISTS QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;$$
DROP PROCEDURE IF EXISTS schema_change;$$
CREATE PROCEDURE schema_change()
BEGIN
DECLARE CurrentDatabase VARCHAR(100);
SELECT DATABASE() INTO CurrentDatabase;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_JOB_DETAILS' AND index_name = 'IDX_QRTZ_J_REQ_RECOVERY') THEN
CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_JOB_DETAILS' AND index_name = 'IDX_QRTZ_J_GRP') THEN
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_J') THEN
CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_JG') THEN
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_C') THEN
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_G') THEN
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_STATE') THEN
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_N_STATE') THEN
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_N_G_STATE') THEN
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_NEXT_FIRE_TIME') THEN
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_NFT_ST') THEN
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_NFT_MISFIRE') THEN
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_NFT_ST_MISFIRE') THEN
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_TRIGGERS' AND index_name = 'IDX_QRTZ_T_NFT_ST_MISFIRE_GRP') THEN
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_FIRED_TRIGGERS' AND index_name = 'IDX_QRTZ_FT_TRIG_INST_NAME') THEN
CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_FIRED_TRIGGERS' AND index_name = 'IDX_QRTZ_FT_INST_JOB_REQ_RCVRY') THEN
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_FIRED_TRIGGERS' AND index_name = 'IDX_QRTZ_FT_J_G') THEN
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_FIRED_TRIGGERS' AND index_name = 'IDX_QRTZ_FT_JG') THEN
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_FIRED_TRIGGERS' AND index_name = 'IDX_QRTZ_FT_T_G') THEN
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
END IF;
IF NOT EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema=CurrentDatabase AND table_name = 'QRTZ_FIRED_TRIGGERS' AND index_name = 'IDX_QRTZ_FT_TG') THEN
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
END IF;
END $$
CALL schema_change();$$
如上修改完我們的SQL腳本之後,就可以在啓動項目的時候自動初始化項目了 。
4.1.1.2 基於編程的SQL初始化
基於編程的SQL初始化就是手動加載DataSource和SQL腳本,然後去執行腳本。因爲我們之前將Quartz與Spring整合,需要將Scheduler以Bean的形式注入到IOC容器中,所以我們的初始化腳本要在這之前執行。這裏我們將在之前的QuartzConfig類裏添加相應修改。
註釋掉之前的基於配置的SQL初始化配置
# schema-username: root
# schema-password: hollysys
# schema: classpath:quartz_tables.sql
# initialization-mode: always
# separator: $$
在QuartzConfig類裏注入Springboot的DataSource
@Autowired
DataSource dataSource;
在QuartzConfig類創建初始化數據庫方法initDataBase()
public void initDataBase(DataSource dataSource) {
log.info("============== init quartz database started ==============");
try {
//加載SQL
ClassPathResource recordsSys = new ClassPathResource("quartz_tables.sql");
//使用DataSourceInitializer初始化
DataSourceInitializer dsi = new DataSourceInitializer();
dsi.setDataSource(dataSource);
dsi.setDatabasePopulator(new ResourceDatabasePopulator(true, true, "utf-8", recordsSys));
dsi.setEnabled(true);
dsi.afterPropertiesSet();
log.info("============== init quartz database succeed ==============");
} catch (Exception e) {
log.error("init quartz database failed:{}", e.getMessage());
}
}
在創建SchedulerFactoryBean的時候,去初始化數據庫
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
//初始化數據庫
initDataBase(dataSource);
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setQuartzProperties(quartzProperties());
schedulerFactoryBean.setJobFactory(jobFactory);
return schedulerFactoryBean;
}
這樣我們就完成基於編程的SQL初始化。
4.1.2 使用Quartz自定義DataSource時,初始化Quartz建表SQL
上面我們介紹了兩種方式實現使用Springboot的DataSource去初始化Quartz的建表SQL。這裏我們來講一下,使用自定義的DataSource該如何初始化呢?其實原理與上面的基於編程的方式一樣,只不過不能通過註解的方式注入DataSource,而是需要我們手動創建DataSource,然後傳入上面編寫的initDataBase()方法。
首先在quartz.properties配置文件中開啓我們的數據庫相關配置
#存儲方式使用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
#配置數據源
#數據庫中quartz表的表名前綴
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/springboot?characterEncoding=utf-8&useSSL=false
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = hollysys
org.quartz.dataSource.myDS.maxConnections = 5
在QuartzConfig類裏添加創建DataSource的方法 quartzSource()如下所示:
@Bean
public DataSource quartzSource() throws IOException {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
Properties properties = quartzProperties();
dataSource.setDriverClassName(properties.getProperty("org.quartz.dataSource.myDS.driver"));
dataSource.setUrl(properties.getProperty("org.quartz.dataSource.myDS.URL"));
dataSource.setUsername(properties.getProperty("org.quartz.dataSource.myDS.user"));
dataSource.setPassword(properties.getProperty("org.quartz.dataSource.myDS.password"));
return dataSource;
}
然後同樣在創建SchedulerFactoryBean的時候,去初始化數據庫
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
//初始化數據庫,這時候DataSource就要使用上面的quartzSource()方法創建了
initDataBase(quartzSource());
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setQuartzProperties(quartzProperties());
schedulerFactoryBean.setJobFactory(jobFactory);
return schedulerFactoryBean;
}
這樣我們就可以實現基於自定義DataSource的SQL初始化。
4.2 關於實現多租戶的幾點思路
4.2.1 租戶獨享服務模式的實現
這裏所說的租戶獨享服務包含兩種情景:一種是一個租戶對應一個服務,另一種是一個租戶對應多個服務(服務集羣)即一個租戶對應一個集羣。這兩種場景的實現方式都可以用一種方案解決。我的思路是使用Scheduler容器來區分租戶,即一個租戶對應一個Scheduler容器或一個Scheduler容器集羣。該租戶的Scheduler容器,只會管理在該Scheduler實例中創建的定時任務。生產中,我們的租戶以服務作爲隔離級別,租戶各自有自己的服務。Quartz集羣以Scheduler容器作爲隔離級別,租戶各自的定時任務在自己的Scheduler容器和Scheduler集羣裏執行。
那麼如何通過Scheduler去做租戶隔離呢?其實很簡單,只需要在創建SchedulerFactoryBean的時候,指定一下SchedulerName即可,這個地方我們用租戶ID來做SchedulerName,從而實現基於Scheduler容器的多租戶。
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
//初始化數據庫
initDataBase(dataSource);
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setQuartzProperties(quartzProperties());
schedulerFactoryBean.setJobFactory(jobFactory);
//通過租戶ID來設置SchedulerName從而實現多租戶
schedulerFactoryBean.setSchedulerName(tenant_id);
return schedulerFactoryBean;
}
這樣我們租戶下的服務只會執行自己租戶ID對應的Scheduler容器中的定時任務。
4.2.1 租戶共享服務模式的實現
租戶共享服務模式是指一個服務對應多個租戶,或者一個服務集羣對應多個租戶。這樣的服務模式原本隔離性就很差。這樣的多租戶實現,可以使用JobGroup來進行隔離。使用同一個Scheduler容器或者一個Scheduler集羣來共同調度所有的定時任務,只不過是通過JobGroup來區分,哪個定時任務對應哪個租戶。這時候我們就可以使用租戶ID來作爲group名就能實現。具體實現,根據自己的業務場景定製,這裏不做演示。
5 總結
至此,已經完成了所有Springboot整合Quartz實現定時任務調度管理的內容。項目的實現我也是從0到1,從接觸Quartz調度框架到整合到自己的項目中,其中一些思路來自於強大的網絡,另一些是自己不成熟的見解。希望記錄整合過程,對以後開發有所幫助。