SpringBoot integrates Quartz to implement scheduled tasks

Article directory

  • Introduction to Quartz
  • pom dependency
  • Memory storage
  • Introduction to Quartz
  • pom dependency
  • Memory storage
  • Database storage
  • Distributed task support

Introduction to Quartz

For a quick start with Quartz see:
Quartz official documentation_w3cschool

Quartz has two storage methods: MEMORY and JDBC. The default is to maintain task information in memory mode, and the task will restart when the service is restarted; the JDBC mode can persist the task information to the database. Although the service is restarted, the task can continue to be executed according to the records in the database.

pom dependencies

 <!--springboot project integrates quartz dependencies-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>

Memory storage

  1. Create a new project and add quartz dependency to pom

  2. Create a new Job class, inherit QuartzJobBean, implement the methods in the abstract class, and implement the logic that needs to be executed by the scheduled task in the method.

For example, if I create a new PrintTipJob, hello world! will be output to the console every time it is executed.

package com.example.demo.job;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

public class PrintTipJob extends QuartzJobBean {<!-- -->
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {<!-- -->
        LocalTime time = LocalTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        System.out.println("time:" + time.format(formatter) + " hello world!");
    }
}
  1. Encapsulate a service layer for adding, updating, and deleting jobs

    • Job represents a job and the specific content to be executed. There is only one method in this interface: void execute(JobExecutionContext context)
    • JobDetail represents a specific executable scheduler. Job is the content to be executed by the executable scheduler. In addition, JobDetail also contains the task scheduling plan and strategy.
    • Trigger represents the configuration of a scheduling parameter and when to adjust it.
    • Scheduler represents a scheduling container, and multiple JobDetails and Triggers can be registered in a scheduling container. When Trigger is combined with JobDetail, it can be scheduled by the Scheduler container.

service interface

package com.example.demo.service;

import org.springframework.scheduling.quartz.QuartzJobBean;

public interface QuartzJobService {<!-- -->

   /**
    * Add a task
    * @param job
    * @param jobName
    * @param jobGroupName
    * @param jobCronTime
    */
   void addJob(Class<? extends QuartzJobBean> job, String jobName, String jobGroupName, String jobCronTime);

   /**
    * Update the execution cron of the task
    * @param job
    * @param jobGroupName
    * @param jobCronTime
    */
   void updateJob(String job, String jobGroupName, String jobCronTime);

   /**
    * Whether a job exists
    * @param job
    * @param jobGroupName
    * @return
    */
   boolean existJob(String job, String jobGroupName);

   /**
    * Delete a job
    *
    * @param jobName task name
    * @param jobGroupName task group name
    */
   void deleteJob(String jobName, String jobGroupName);
}

service implementation class

package com.example.demo.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.example.demo.service.QuartzJobService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;

import java.util.Set;

@Slf4j
@Service
public class QuartzJobServiceImpl implements QuartzJobService {<!-- -->

    @Autowired
    private Scheduler scheduler;

    @Override
    public void addJob(Class<? extends QuartzJobBean> job, String jobName, String jobGroupName, String jobCronTime) {<!-- -->
        JobDetail jobDetail = JobBuilder.newJob(job)
                .withIdentity(jobName, jobGroupName)
                .withDescription(jobCronTime)
                .build();

        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
                .startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
                .withSchedule(CronScheduleBuilder.cronSchedule(jobCronTime)).startNow().build();

        try {<!-- -->
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (Exception e) {<!-- -->
            log.error("e:{}", e.getMessage());
        }
    }

    @Override
    public void updateJob(String job, String jobGroupName, String jobCronTime) {<!-- -->
        try {<!-- -->
            TriggerKey triggerKey = TriggerKey.triggerKey(job, jobGroupName);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            if (ObjectUtil.isNotNull(trigger)) {<!-- -->
                trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                        .withSchedule(CronScheduleBuilder.cronSchedule(jobCronTime)).build();
            } else {<!-- -->
                trigger = TriggerBuilder.newTrigger().withIdentity(job, jobGroupName)
                        .startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
                        .withSchedule(CronScheduleBuilder.cronSchedule(jobCronTime)).startNow().build();
            }

            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException e) {<!-- -->
            log.error("e:{}", e.getMessage());
        }
    }

    @Override
    public boolean existJob(String job, String jobGroupName) {<!-- -->
        try {<!-- -->
            if (StringUtils.isAnyBlank(job, jobGroupName)) {<!-- -->
                return false;
            }
            GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
            Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
            if (CollectionUtils.isNotEmpty(jobKeys)) {<!-- -->
                for (JobKey jobKey : jobKeys) {<!-- -->
                    if (jobKey.getName().equals(job) & amp; & amp; jobKey.getGroup().equals(jobGroupName)) {<!-- -->
                        return true;
                    }
                }
            }
        } catch (Exception e) {<!-- -->
            log.error("e:{}", e.getMessage());
        }
        return false;
    }

    @Override
    public void deleteJob(String jobName, String jobGroupName) {<!-- -->
        try {<!-- -->
            scheduler.deleteJob(new JobKey(jobName, jobGroupName));
        } catch (Exception e) {<!-- -->
            log.error("e:{}", e.getMessage());
        }
    }
}

  1. Create a scheduled task listening class and implement the ApplicationListener interface. Once the service is started, the job will be added or updated.

Add PrintTipJob class, cron expression 0/5 * * * * ?, executed every 5 seconds

Give a link to a cron expression online generator gadget: Cron online expression generator

package com.example.demo.listener;

import com.example.demo.job.PrintTipJob;
import com.example.demo.service.QuartzJobService;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
public class ApplicationStartQuartzJobListener implements ApplicationListener<ContextRefreshedEvent> {<!-- -->

    @Resource
    private QuartzJobService quartzJobService;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {<!-- -->
        if (quartzJobService.existJob("PrintTipJob", "test")) {<!-- -->
            quartzJobService.updateJob("PrintTipJob", "test", "0/5 * * * * ? ");
        } else {<!-- -->
            quartzJobService.addJob(PrintTipJob.class, "PrintTipJob", "test", "0/5 * * * * ? ");
        }
    }
}

  1. Start the project, you can see that the console will print every 5 seconds

Figure 1 SpringBoot integrates Quartz to implement scheduled tasks

Introduction to Quartz

For a quick start with Quartz see:
Quartz official documentation_w3cschool

Quartz has two storage methods: MEMORY and JDBC. The default is to maintain task information in memory mode, and the task will restart when the service is restarted; the JDBC mode can persist the task information to the database. Although the service is restarted, the task can continue to be executed according to the records in the database.

pom dependencies

 <!--springboot project integrates quartz dependencies-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>

Memory storage

  1. Create a new project and add quartz dependency to pom

  2. Create a new Job class, inherit QuartzJobBean, implement the methods in the abstract class, and implement the logic that needs to be executed by the scheduled task in the method.

For example, if I create a new PrintTipJob, hello world! will be output to the console every time it is executed.

package com.example.demo.job;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

public class PrintTipJob extends QuartzJobBean {<!-- -->
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {<!-- -->
        LocalTime time = LocalTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        System.out.println("time:" + time.format(formatter) + " hello world!");
    }
}
  1. Encapsulate a service layer for adding, updating, and deleting jobs

    • Job represents a job and the specific content to be executed. There is only one method in this interface: void execute(JobExecutionContext context)
    • JobDetail represents a specific executable scheduler. Job is the content to be executed by the executable scheduler. In addition, JobDetail also contains the task scheduling plan and strategy.
    • Trigger represents the configuration of a scheduling parameter and when to adjust it.
    • Scheduler represents a scheduling container, and multiple JobDetails and Triggers can be registered in a scheduling container. When Trigger is combined with JobDetail, it can be scheduled by the Scheduler container.

service interface

package com.example.demo.service;

import org.springframework.scheduling.quartz.QuartzJobBean;

public interface QuartzJobService {<!-- -->

   /**
    * Add a task
    * @param job
    * @param jobName
    * @param jobGroupName
    * @param jobCronTime
    */
   void addJob(Class<? extends QuartzJobBean> job, String jobName, String jobGroupName, String jobCronTime);

   /**
    * Update the execution cron of the task
    * @param job
    * @param jobGroupName
    * @param jobCronTime
    */
   void updateJob(String job, String jobGroupName, String jobCronTime);

   /**
    * Whether a job exists
    * @param job
    * @param jobGroupName
    * @return
    */
   boolean existJob(String job, String jobGroupName);

   /**
    * Delete a job
    *
    * @param jobName task name
    * @param jobGroupName task group name
    */
   void deleteJob(String jobName, String jobGroupName);
}

service implementation class

package com.example.demo.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.example.demo.service.QuartzJobService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;

import java.util.Set;

@Slf4j
@Service
public class QuartzJobServiceImpl implements QuartzJobService {<!-- -->

    @Autowired
    private Scheduler scheduler;

    @Override
    public void addJob(Class<? extends QuartzJobBean> job, String jobName, String jobGroupName, String jobCronTime) {<!-- -->
        JobDetail jobDetail = JobBuilder.newJob(job)
                .withIdentity(jobName, jobGroupName)
                .withDescription(jobCronTime)
                .build();

        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
                .startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
                .withSchedule(CronScheduleBuilder.cronSchedule(jobCronTime)).startNow().build();

        try {<!-- -->
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (Exception e) {<!-- -->
            log.error("e:{}", e.getMessage());
        }
    }

    @Override
    public void updateJob(String job, String jobGroupName, String jobCronTime) {<!-- -->
        try {<!-- -->
            TriggerKey triggerKey = TriggerKey.triggerKey(job, jobGroupName);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            if (ObjectUtil.isNotNull(trigger)) {<!-- -->
                trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                        .withSchedule(CronScheduleBuilder.cronSchedule(jobCronTime)).build();
            } else {<!-- -->
                trigger = TriggerBuilder.newTrigger().withIdentity(job, jobGroupName)
                        .startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
                        .withSchedule(CronScheduleBuilder.cronSchedule(jobCronTime)).startNow().build();
            }

            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException e) {<!-- -->
            log.error("e:{}", e.getMessage());
        }
    }

    @Override
    public boolean existJob(String job, String jobGroupName) {<!-- -->
        try {<!-- -->
            if (StringUtils.isAnyBlank(job, jobGroupName)) {<!-- -->
                return false;
            }
            GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
            Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
            if (CollectionUtils.isNotEmpty(jobKeys)) {<!-- -->
                for (JobKey jobKey : jobKeys) {<!-- -->
                    if (jobKey.getName().equals(job) & amp; & amp; jobKey.getGroup().equals(jobGroupName)) {<!-- -->
                        return true;
                    }
                }
            }
        } catch (Exception e) {<!-- -->
            log.error("e:{}", e.getMessage());
        }
        return false;
    }

    @Override
    public void deleteJob(String jobName, String jobGroupName) {<!-- -->
        try {<!-- -->
            scheduler.deleteJob(new JobKey(jobName, jobGroupName));
        } catch (Exception e) {<!-- -->
            log.error("e:{}", e.getMessage());
        }
    }
}

  1. Create a scheduled task listening class and implement the ApplicationListener interface. Once the service is started, the job will be added or updated.

Add PrintTipJob class, cron expression 0/5 * * * * ?, executed every 5 seconds

Give a link to a cron expression online generator gadget: Cron online expression generator

package com.example.demo.listener;

import com.example.demo.job.PrintTipJob;
import com.example.demo.service.QuartzJobService;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
public class ApplicationStartQuartzJobListener implements ApplicationListener<ContextRefreshedEvent> {<!-- -->

    @Resource
    private QuartzJobService quartzJobService;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {<!-- -->
        if (quartzJobService.existJob("PrintTipJob", "test")) {<!-- -->
            quartzJobService.updateJob("PrintTipJob", "test", "0/5 * * * * ? ");
        } else {<!-- -->
            quartzJobService.addJob(PrintTipJob.class, "PrintTipJob", "test", "0/5 * * * * ? ");
        }
    }
}

  1. Start the project, you can see that the console will print every 5 seconds

Database storage

  1. pom needs to introduce quartz dependencies, and also introduce database dependencies.
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.4</version>
</dependency>
<!-- MySQL driver -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
  1. Add Quartz configuration information

Add relevant configurations to the application.properties file.

# Modify Quartz persistence mode to jdbc
spring.quartz.job-store-type=jdbc
#Instance name (default is quartzScheduler)
spring.quartz.properties.org.quartz.scheduler.instanceName=test_scheduler
# Instance node ID is automatically generated
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO
# Modify the class used to store content
spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
#Data source information
spring.quartz.properties.org.quartz.jobStore.dataSource=quartz_jobs
spring.quartz.properties.org.quartz.dataSource.quartz_jobs.driver=com.mysql.cj.jdbc.Driver
spring.quartz.properties.org.quartz.dataSource.quartz_jobs.URL=jdbc:mysql://localhost:3306/quartz_jobs?useUnicode=true &characterEncoding=utf8
spring.quartz.properties.org.quartz.dataSource.quartz_jobs.user=root
spring.quartz.properties.org.quartz.dataSource.quartz_jobs.password=
  1. Add the library table used by quartz

Download the Quartz release package. After the download is complete, unzip it and enter the quartz-2.2.3/docs/dbTables directory to find the SQL file matching the database.

download link:
https://www.quartz-scheduler.org/downloads/files/quartz-2.2.3-distribution.tar.gz

Use the mysql script: tables_mysql.sql, or use the following script

#
# Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
#
# PLEASE consider using mysql with innodb tables to avoid locking issues
#
# In your Quartz properties file, you'll need to set
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#

DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;


CREATE TABLE QRTZ_JOB_DETAILS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    JOB_NAME VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    JOB_CLASS_NAME VARCHAR(250) NOT NULL,
    IS_DURABLE VARCHAR(1) NOT NULL,
    IS_NONCONCURRENT VARCHAR(1) NOT NULL,
    IS_UPDATE_DATA VARCHAR(1) NOT NULL,
    REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    JOB_NAME VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    NEXT_FIRE_TIME BIGINT(13) NULL,
    PREV_FIRE_TIME BIGINT(13) NULL,
    PRIORITY INTEGER NULL,
    TRIGGER_STATE VARCHAR(16) NOT NULL,
    TRIGGER_TYPE VARCHAR(8) NOT NULL,
    START_TIME BIGINT(13) NOT NULL,
    END_TIME BIGINT(13) NULL,
    CALENDAR_NAME VARCHAR(200) NULL,
    MISFIRE_INSTR SMALLINT(2) NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
        REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_SIMPLE_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    REPEAT_COUNT BIGINT(7) NOT NULL,
    REPEAT_INTERVAL BIGINT(12) NOT NULL,
    TIMES_TRIGGERED BIGINT(10) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CRON_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    CRON_EXPRESSION VARCHAR(200) NOT NULL,
    TIME_ZONE_ID VARCHAR(80),
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_BLOB_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    BLOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CALENDARS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    CALENDAR_NAME VARCHAR(200) NOT NULL,
    CALENDAR BLOB NOT NULL,
    PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_FIRED_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    ENTRY_ID VARCHAR(95) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    FIRED_TIME BIGINT(13) NOT NULL,
    SCHED_TIME BIGINT(13) NOT NULL,
    PRIORITY INTEGER NOT NULL,
    STATE VARCHAR(16) NOT NULL,
    JOB_NAME VARCHAR(200) NULL,
    JOB_GROUP VARCHAR(200) NULL,
    IS_NONCONCURRENT VARCHAR(1) NULL,
    REQUESTS_RECOVERY VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);

CREATE TABLE QRTZ_SCHEDULER_STATE
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,
    PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);

CREATE TABLE QRTZ_LOCKS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    LOCK_NAME VARCHAR(40) NOT NULL,
    PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);


commit;

Another problem was encountered when executing sql here. Some tables kept reporting errors when creating: Index column size too large. The maximum column size is 767 bytes. Solution reference link: MySQL encountered Specified key was too long when building an index; max key length is 767 bytes

  1. Start service
    The code does not need to be modified. Start the service and you can see that the scheduled task is executed normally and is printed every 5 seconds.
    There is also data in the tables related to the database.

Distributed task support

You can enable cluster configuration to allow scheduled tasks to support distributed tasks. I have not tried it. I have seen supplements from other articles here.

In the application.properties file, add the configuration information of the Quartz cluster:

# Start the cluster, multiple Quartz instances use the same set of database tables
spring.quartz.properties.org.quartz.jobStore.isClustered=true

Note that when Quartz uses the same set of database tables for clustering, you only need to configure the same instanceName instance name. For example, test_scheduler is used this time.

 spring.quartz.properties.org.quartz.scheduler.instanceName=test_scheduler

Reference article:
Play with Spring Boot integration (scheduled task framework Quartz)