Quartz has different memory and database modes
In memory mode, task information is saved in memory, and it will be lost during shutdown. It needs to be re executed manually. In database mode, task information is saved in database, and the focus is to support cluster
RAMJobStore in memory mode and database mode JobStoreTX, RAMJobStore is suitable for single machine, does not support cluster, JobStoreTX supports cluster
The following is the JobStoreTX database mode
1. Add the main related dependencies. Other dependencies will not be mentioned here
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency>
2. There are 11 database tables in quartz, which can Download official package In the official package, you can find the table creation statement, which corresponds to different databases
Configuration file quartz.properties
org.quartz.scheduler.instanceName=DefaultQuartzScheduler org.quartz.scheduler.instanceid=AUTO org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount=20 org.quartz.threadPool.threadPriority=5 org.quartz.jobStore.misfireThreshold=2000 org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.useProperties=true org.quartz.jobStore.dataSource=myDS org.quartz.jobStore.tablePrefix=QRTZ_ org.quartz.jobStore.isClustered=true org.quartz.jobStore.clusterCheckinInterval=20000 org.quartz.dataSource.myDS.driver=org.mariadb.jdbc.Driver org.quartz.dataSource.myDS.URL=jdbc:mariadb://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true #org.quartz.dataSource.myDS.driver=com.mysql.jdbc.Driver #org.quartz.dataSource.myDS.URL=jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true org.quartz.dataSource.myDS.user=root org.quartz.dataSource.myDS.password=root org.quartz.dataSource.myDS.maxConnections=5 org.quartz.dataSource.myDS.validationQuery=select 0 from dual
3. JobFactory
import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.SpringBeanJobFactory; @Configuration public class JobFactory extends SpringBeanJobFactory { @Autowired private AutowireCapableBeanFactory autowireCapableBeanFactory; @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { Object createJobInstance = super.createJobInstance(bundle); autowireCapableBeanFactory.autowireBean(createJobInstance); return createJobInstance; } }
4. QuartzConfig
import java.io.IOException; import java.util.Properties; 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; @Configuration public class QuartzConfig { @Autowired private JobFactory jobFactory; // @Autowired // private DataSource dataSource; / / if you use the datasource in application.properties, you can use this method @Bean public SchedulerFactoryBean myScheduler() throws IOException { SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); // schedulerFactoryBean.setDataSource(dataSource); / / use the datasource in application.properties schedulerFactoryBean.setOverwriteExistingJobs(true); schedulerFactoryBean.setJobFactory(jobFactory); schedulerFactoryBean.setQuartzProperties(quartzProperties()); // Using the data source in quartz.properties schedulerFactoryBean.setSchedulerName("myScheduler"); schedulerFactoryBean.setStartupDelay(2);// Two second delay start schedulerFactoryBean.setAutoStartup(true); return schedulerFactoryBean; } @Bean public SchedulerFactoryBean myScheduler2() throws IOException { SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); // schedulerFactoryBean.setDataSource(dataSource); / / use the datasource in application.properties schedulerFactoryBean.setOverwriteExistingJobs(true); schedulerFactoryBean.setJobFactory(jobFactory); schedulerFactoryBean.setQuartzProperties(quartzProperties()); // Using the data source in quartz.properties schedulerFactoryBean.setSchedulerName("myScheduler2"); schedulerFactoryBean.setStartupDelay(2);// Two second delay start schedulerFactoryBean.setAutoStartup(true); return schedulerFactoryBean; } private Properties quartzProperties() throws IOException { PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties")); propertiesFactoryBean.afterPropertiesSet(); Properties properties = propertiesFactoryBean.getObject(); return properties; } }
5. JobController
import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.Job; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.JobKey; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.TriggerBuilder; import org.quartz.TriggerKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import com.codingos.springboot.model.JobInfo; @RestController public class JobController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private Scheduler myScheduler; // This injection should have the same method name as @ Bean in the config class @Autowired private Scheduler myScheduler2; // This injection should have the same method name as @ Bean in the config class /** * Create Job */ @PostMapping("/createJob") public void createJob(@RequestBody JobInfo jobInfo) { Scheduler scheduler = getScheduler(jobInfo.getSchedulerName()); Class<? extends Job> jobClass = null; try { jobClass = (Class<? extends Job>) Class.forName(jobInfo.getJobClassName()); } catch (ClassNotFoundException e) { logger.error("create Job " + jobInfo.getJobName() + " error: " + e.getMessage(), e); } JobKey jobKey = new JobKey(jobInfo.getJobName(), jobInfo.getJobGroup()); JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobKey).storeDurably().build(); CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobInfo.getCron()); TriggerKey triggerKey = new TriggerKey(jobInfo.getTriggerName(), jobInfo.getTriggerGroup()); CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).forJob(jobDetail).withSchedule(scheduleBuilder).build(); try { scheduler.scheduleJob(jobDetail, cronTrigger); scheduler.pauseJob(jobKey); // If you do not pause here, you will run the job directly } catch (SchedulerException e) { logger.error("create Job " + jobInfo.getJobName() + " error: " + e.getMessage(), e); } } @PostMapping("/jobStrat") public void jobStrat(@RequestBody JobInfo jobInfo) { Scheduler scheduler = getScheduler(jobInfo.getSchedulerName()); JobKey jobKey = new JobKey(jobInfo.getJobName(), jobInfo.getJobGroup()); try { scheduler.resumeJob(jobKey); // Resume job execution } catch (SchedulerException e) { logger.error("Job " + jobInfo.getJobName() + " start error: " + e.getMessage(), e); } } @PostMapping("/jobStop") public void jobStop(@RequestBody JobInfo jobInfo) { Scheduler scheduler = getScheduler(jobInfo.getSchedulerName()); JobKey jobKey = new JobKey(jobInfo.getJobName(), jobInfo.getJobGroup()); try { scheduler.pauseJob(jobKey); // Suspend job } catch (SchedulerException e) { logger.error("Job " + jobInfo.getJobName() + " stop error: " + e.getMessage(), e); } } @PostMapping("/jobEdit") public void jobEdit(@RequestBody JobInfo jobInfo) { Scheduler scheduler = getScheduler(jobInfo.getSchedulerName()); JobKey jobKey = new JobKey(jobInfo.getJobName(), jobInfo.getJobGroup()); try { TriggerKey triggerKey = new TriggerKey(jobInfo.getTriggerName(), jobInfo.getTriggerGroup()); CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobInfo.getCron()); CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).forJob(jobKey).withSchedule(scheduleBuilder).build(); scheduler.rescheduleJob(triggerKey, cronTrigger); // Update corresponding trigger } catch (SchedulerException e) { logger.error("Job " + jobInfo.getJobName() + " edit error: " + e.getMessage(), e); } } @PostMapping("/jobDelete") public void jobDelete(@RequestBody JobInfo jobInfo) { Scheduler scheduler = getScheduler(jobInfo.getSchedulerName()); JobKey jobKey = new JobKey(jobInfo.getJobName(), jobInfo.getJobGroup()); try { scheduler.deleteJob(jobKey); // Delete job and corresponding trigger } catch (SchedulerException e) { logger.error("Job " + jobInfo.getJobName() + " stop error: " + e.getMessage(), e); } } private Scheduler getScheduler(String schedulerName) { Scheduler scheduler = null; switch (schedulerName) { case "myScheduler": scheduler = myScheduler; break; case "myScheduler2": scheduler = myScheduler2; break; default: break; } return scheduler; } }
6. Job
import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.quartz.QuartzJobBean; import com.codingos.springboot.service.Job1Service; public class Job1 extends QuartzJobBean { @Autowired private Job1Service job1Service; @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { job1Service.service1(); } }
7. JobInfo class
public class JobInfo { private String jobName; private String jobGroup; private String jobClassName; private String triggerName; private String triggerGroup; private String schedulerName; private String prevExecuteTime; private String nextExecuteTime; private String cron; private String triggerState; public JobInfo() { } public String getJobName() { return jobName; } public void setJobName(String jobName) { this.jobName = jobName; } public String getCron() { return cron; } public void setCron(String cron) { this.cron = cron; } public String getTriggerState() { return triggerState; } public void setTriggerState(String triggerState) { this.triggerState = triggerState; } public String getJobGroup() { return jobGroup; } public void setJobGroup(String jobGroup) { this.jobGroup = jobGroup; } public String getTriggerName() { return triggerName; } public void setTriggerName(String triggerName) { this.triggerName = triggerName; } public String getTriggerGroup() { return triggerGroup; } public void setTriggerGroup(String triggerGroup) { this.triggerGroup = triggerGroup; } public String getPrevExecuteTime() { return prevExecuteTime; } public void setPrevExecuteTime(String prevExecuteTime) { this.prevExecuteTime = prevExecuteTime; } public String getNextExecuteTime() { return nextExecuteTime; } public void setNextExecuteTime(String nextExecuteTime) { this.nextExecuteTime = nextExecuteTime; } public String getJobClassName() { return jobClassName; } public void setJobClassName(String jobClassName) { this.jobClassName = jobClassName; } public String getSchedulerName() { return schedulerName; } public void setSchedulerName(String schedulerName) { this.schedulerName = schedulerName; } }