精华内容
下载资源
问答
  • springboot整合quartz
    2022-02-11 23:25:05

    因为一些需求需要一个定时任务,但Java的简单的定时任务对于多节点的情况执行会有消息不共享的问题,所以使用quartzlai实现定时任务,再此记录一下开发过程,以下是一个demo,可以直接运行。
    本次开发使用的依赖是原生包,因为网络环境的原因没有使用springboot整合包。
    项目结构
    在这里插入图片描述
    代码:
    pom.xml
    其中配置数据源的时候mysql-connector-java和spring-boot-starter-jdbc需要同时配置,不然会报数据源为空的错

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.4.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.quartzs</groupId>
        <artifactId>demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>demo</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
                <version>2.2.1</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context-support</artifactId>
            </dependency>
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz-jobs</artifactId>
                <version>2.2.1</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-test</artifactId>
                <version>2.6.3</version>
                <scope>compile</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
    
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.12</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>compile</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>5.3.15</version>
                <scope>compile</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>2.3.3.RELEASE</version>
    
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>5.2.9.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
                <scope>runtime</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    

    myQuartz.properties
    配置quartz的一些参数,其中文件名不建议写quartz.properties,因为和quartz框架中默认的配置文件同名,启动的时候会将默认的配置文件覆盖,导致项目报错,报错信息为:Active Scheduler of name ‘JobScheduler’ already registered in Quartz SchedulerRepository
    如果使用spring的数据源database要和spring配置相同,org.quartz.jobStore.dataSource

    #可以为任意字符串,对于scheduler来说此值没有意义,但是可以区分同一系统中多个不同的实例,
    #如果使用了集群的功能,就必须对每一个实例使用相同的名称,这样使这些实例“逻辑上”是同一个scheduler。
    org.quartz.scheduler.instanceName = JobScheduler
    #可以是任意字符串,但如果是集群,scheduler实例的值必须唯一,可以使用AUTO自动生成。
    org.quartz.scheduler.instanceId = AUTO
    org.quartz.scheduler.rmi.export = false
    org.quartz.scheduler.rmi.proxy = false
    
    # 默认false,若是在执行Job之前Quartz开启UserTransaction,此属性应该为true。
    #Job执行完毕,JobDataMap更新完(如果是StatefulJob)事务就会提交。默认值是false,可以在job类上使用@ExecuteInJTATransaction 注解,以便在各自的job上决定是否开启JTA事务。
    org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
    #一个scheduler节点允许接收的trigger的最大数,默认是1,这个值越大,定时任务执行的越多,但代价是集群节点之间的不均衡。
    org.quartz.scheduler.batchTriggerAcquisitionMaxCount=1
    
    #线程池的实例类,(一般使用SimpleThreadPool即可满足几乎所有用户的需求)
    org.quartz.threadPool.class= org.quartz.simpl.SimpleThreadPool
    #线程数量,不会动态增加
    org.quartz.threadPool.threadCount= 10
    #线程优先级
    org.quartz.threadPool.threadPriority= 5
    #加载任务代码的ClassLoader是否从外部继承
    org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread= true
    #是否设置调度器线程为守护线程
    org.quartz.scheduler.makeSchedulerThreadDaemon: true
    
    
    #选择JDBC的存储方式
    org.quartz.jobStore.class= org.quartz.impl.jdbcjobstore.JobStoreTX
    #存储相关信息表的前缀
    org.quartz.jobStore.tablePrefix = QRTZ_
    #JobDataMaps是否都为String类型
    #(若是true的话,便可不用让更复杂的对象以序列化的形式保存到BLOB列中。以防序列化可能导致的版本号问题)
    org.quartz.jobStore.useProperties = false
    #最大能忍受的触发超时时间,如果超时则认为“失误”
    org.quartz.jobStore.misfireThreshold = 60000
    #是否是应用在集群中,当应用在集群中时必须设置为TRUE,否则会出错。
    #如果有多个Quartz实例在用同一套数据库时,必须设置为true。
    org.quartz.jobStore.isClustered=true
    #只用于设置了isClustered为true的时候,设置一个频度(毫秒),用于实例报告给集群中的其他实例。
    #这会影响到侦测失败实例的敏捷度。
    org.quartz.jobStore.clusterCheckinInterval =15000
    #这是JobStore能处理的错过触发的Trigger的最大数量。处理太多(2打)很快就会导致数据库表被锁定够长的时间,
    #这样会妨碍别的(还未错过触发)trigger执行的性能。
    org.quartz.jobStore.maxMisfiresToHandleAtATime=20
    #设置这个参数为true会告诉Quartz从数据源获取连接后不要调用它的setAutoCommit(false)方法。
    #在少数情况下是有用的,比如有一个驱动本来是关闭的,但是又调用这个关闭的方法。但是大部分情况下驱动都要求调用setAutoCommit(false)
    org.quartz.jobStore.dontSetAutoCommitFalse=false
    #这必须是一个从LOCKS表查询一行并对这行记录加锁的SQL。假设没有设置,默认值如下。
    #{0}会在运行期间被前面配置的TABLE_PREFIX所代替
    org.quartz.jobStore.selectWithLockSQL=SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE
    #值为true时告知Quartz(当使用JobStoreTX或CMT)调用JDBC连接的setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE) 方法。这有助于某些数据库在高负载和长时间事务时锁的超时。
    org.quartz.jobStore.txIsolationLevelSerializable=false
    
    quartz.job-store-type=jdbc
    org.quartz.jobStore.dataSource = com.mysql.cj.jdbc.Driver
    

    application.properties
    配置数据库连接的时候要配置时区不然会报错,报错为:The server time zone value ‘�й���׼ʱ��’ is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the ‘serverTimezone’ configuration property) to use a more specifc time zone value if you want to utiliz

    server.port=8080
    
    spring.application.name=quartz1
    spring.profiles.active=quartz
    
    # 连接四大参数
    spring.datasource.url=jdbc:mysql://localhost:3306/quartz1?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    spring.datasource.username=root
    spring.datasource.password=root
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    

    QuartzConfig.java
    初始化Scheduler,注入scheduler的时候要加上@Primay注解,不然会扫描多个注入报错:Field scheduler in com.quartzs.demo.util.QuartzJobManager required a single bean, but 2 were found:
    - schedulerFactoryBean: defined by method ‘schedulerFactoryBean’ in class path resource [com/quartzs/demo/config/QuartzConfig.class]
    - Scheduler: defined by method ‘scheduler’ in class path resource [com/quartzs/demo/config/QuartzConfig.class]

    package com.quartzs.demo.config;
    
    import org.quartz.Scheduler;
    import org.quartz.ee.servlet.QuartzInitializerListener;
    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.context.annotation.Primary;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.scheduling.annotation.EnableScheduling;
    import org.springframework.scheduling.quartz.SchedulerFactoryBean;
    
    import javax.sql.DataSource;
    import java.io.IOException;
    import java.util.Properties;
    
    @Configuration
    @EnableScheduling
    public class QuartzConfig {
    
        @Autowired
        private DataSource dataSource;
    
        @Bean
        public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
            SchedulerFactoryBean factory = new SchedulerFactoryBean();
            factory.setQuartzProperties(quartzProperties());
            factory.setDataSource(dataSource);
            return factory;
        }
    
        @Bean
        public Properties quartzProperties() throws IOException {
            PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
            propertiesFactoryBean.setLocation(new ClassPathResource("/myQuartz.properties"));
            //在quartz.properties中的属性被读取并注入后再初始化对象
            propertiesFactoryBean.afterPropertiesSet();
            return propertiesFactoryBean.getObject();
        }
    
        /**
         * quartz初始化监听器
         */
        @Bean
        public QuartzInitializerListener executorListener() {
            return new QuartzInitializerListener();
        }
    
        /**
         * 通过SchedulerFactoryBean获取Scheduler的实例
         */
        @Primary
        @Bean(name = "Scheduler")
        public Scheduler scheduler() throws IOException {
            return schedulerFactoryBean().getScheduler();
        }
    }
    
    
    

    TaskJobFactory.java
    2.x版本一下无法注入job,此类解决quartz无法注入bean的问题

    package com.quartzs.demo.config;
    
    import org.quartz.spi.TriggerFiredBundle;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
    import org.springframework.scheduling.quartz.AdaptableJobFactory;
    
    /**
     * 解决quartz无法注入bean的问题
     * @author author
     * 2022-02-11 15:53
     */
    public class TaskJobFactory extends AdaptableJobFactory {
    
        @Autowired
        private AutowireCapableBeanFactory capableBeanFactory;
    
        @Override
        protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
            //调用父类方法
            Object jobInstance = super.createJobInstance(bundle);
            //进行注入
            capableBeanFactory.autowireBean(jobInstance);
            return jobInstance;
        }
    
    }
    
    

    HelloJob.java
    定时任务的逻辑代码类,业务逻辑都是写在这个里面,需要实现job类
    如果需要传参数可以放在JobDataMap里,这是一个string,object的map集合,可以根据jobExecutionContext.getJobDetail().getJobDataMap().get(key);取到参数值,可以放多个

    package com.quartzs.demo.service;
    
    import org.quartz.Job;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    
    public class HelloJob implements Job {
        @Override
        public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        // 业务逻辑
            System.out.println("定时任务开始执行");
            Object id = jobExecutionContext.getJobDetail().getJobDataMap().get("id");
            System.out.println("定时任务的id为{}"+id);
    
    
        }
    }
    
    

    QuartzJobManager.java
    主要是封装定时任务的启动修改删除的一些工具,其中定时任务修改可以多加一个Class类型传入要执行定时任务的逻辑类,根据class类型可以执行不同的方法,因为涉及我的业务只有一个方法所以没有加

    package com.quartzs.demo.util;
    
    import com.quartzs.demo.service.HelloJob;
    import lombok.extern.log4j.Log4j2;
    import org.quartz.*;
    import org.quartz.impl.matchers.GroupMatcher;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.*;
    
    /**
     * task任务创建个工具类
     *
     * @author author
     * 2020-02-11 16:04
     */
    @Component
    @Log4j2
    public class QuartzJobManager {
        private static QuartzJobManager jobUtil;
    
        @Autowired
        private Scheduler scheduler;
    
        public QuartzJobManager() {
            log.info("QuartzJobManager init");
            jobUtil = this;
        }
    
        public static QuartzJobManager getInstance() {
            log.info("return JobUtil");
            return QuartzJobManager.jobUtil;
        }
    
        /**
         * 创建任务
         *
         * @param jobName        任务名称
         * @param jobGroupName   任务所在组名称
         * @param cronExpression cron表达式
         * @throws Exception
         */
        public void addJob(String jobName, String jobGroupName, String cronExpression) throws Exception {
            //启动调度器
            scheduler.start();
            //构建job信息 HelloJob:执行定时任务的具体代码
            JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity(jobName, jobGroupName).build();
            //表达式调度构造起,任务执行时间
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            //根据任务执行时间的cron表达式构建trigger触发器
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName).withSchedule(scheduleBuilder).build();
            scheduler.scheduleJob(jobDetail, trigger);
        }
    
        /**
         * 创建job,可传参,可以新加一个定时任务实现类的参数,本例写死
         *
         * @param jobName        任务名称
         * @param jobGroupName   任务所在组名称
         * @param cronExpression cron表达式
         * @param argMap         map形式参数
         * @throws Exception
         */
        public void addJob(String jobName, String jobGroupName, String cronExpression, Map<String, Object> argMap) throws Exception {
            // 启动调度器
            scheduler.start();
            //构建job信息
            JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity(jobName, jobGroupName).build();
            //传入参数
            jobDetail.getJobDataMap().putAll(argMap);
            //表达式调度构建器(即任务执行的时间)
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
                    .withSchedule(scheduleBuilder).build();
            //获得JobDataMap,写入数据
            //trigger.getJobDataMap().putAll(argMap);
            scheduler.scheduleJob(jobDetail, trigger);
        }
    
        /**
         * 暂停job
         *
         * @param jobName      任务名称
         * @param jobGroupName 任务所在组名称
         * @throws SchedulerException
         */
        public void pauseJob(String jobName, String jobGroupName) throws SchedulerException {
            scheduler.pauseJob(JobKey.jobKey(jobName, jobGroupName));
        }
    
        /**
         * 恢复job
         *
         * @param jobName      任务名称
         * @param jobGroupName 任务所在组名称
         * @throws SchedulerException
         */
        public void resumeJob(String jobName, String jobGroupName) throws SchedulerException {
            scheduler.resumeJob(JobKey.jobKey(jobName, jobGroupName));
        }
    
    
        /**
         * job 更新,只更新频率
         *
         * @param jobName        任务名称
         * @param jobGroupName   任务所在组名称
         * @param cronExpression cron表达式
         * @throws Exception
         */
        public void updateJob(String jobName, String jobGroupName, String cronExpression) throws Exception {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        }
    
    
        /**
         * job 更新,更新频率和参数
         *
         * @param jobName        任务名称
         * @param jobGroupName   任务所在组名称
         * @param cronExpression cron表达式
         * @param argMap         参数
         * @throws Exception
         */
        public void updateJob(String jobName, String jobGroupName, String cronExpression, Map<String, Object> argMap) throws Exception {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            //修改map
            trigger.getJobDataMap().putAll(argMap);
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        }
    
        /**
         * job 更新,只更新更新参数
         *
         * @param jobName      任务名称
         * @param jobGroupName 任务所在组名称
         * @param argMap       参数
         * @throws Exception
         */
        public void updateJob(String jobName, String jobGroupName, Map<String, Object> argMap) throws Exception {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            //修改map
            trigger.getJobDataMap().putAll(argMap);
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        }
    
    
        /**
         * job 删除
         *
         * @param jobName      任务名称
         * @param jobGroupName 任务所在组名称
         * @throws Exception
         */
        public void deleteJob(String jobName, String jobGroupName) throws Exception {
            scheduler.pauseTrigger(TriggerKey.triggerKey(jobName, jobGroupName));
            scheduler.unscheduleJob(TriggerKey.triggerKey(jobName, jobGroupName));
            scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));
        }
    
    
        /**
         * 启动所有定时任务
         */
        public void startAllJobs() {
            try {
                scheduler.start();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        /**
         * 关闭所有定时任务
         */
        public void shutdownAllJobs() {
            try {
                if (!scheduler.isShutdown()) {
                    scheduler.shutdown();
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
    
    
        }
    
        /**
         * 获取所有任务列表
         *
         * @return
         * @throws SchedulerException
         */
        public List<Map<String, Object>> getAllJob() throws SchedulerException {
            GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
            Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
            List<Map<String, Object>> jobList = new ArrayList<>();
            for (JobKey jobKey : jobKeys) {
                List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                for (Trigger trigger : triggers) {
                    Map<String, Object> job = new HashMap<>();
                    job.put("jobName", jobKey.getName());
                    job.put("jobGroupName", jobKey.getGroup());
                    job.put("trigger", trigger.getKey());
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                    job.put("jobStatus", triggerState.name());
                    if (trigger instanceof CronTrigger) {
                        CronTrigger cronTrigger = (CronTrigger) trigger;
                        String cronExpression = cronTrigger.getCronExpression();
                        job.put("cronExpression", cronExpression);
                    }
                    jobList.add(job);
                }
            }
            return jobList;
        }
    }
    
    
    

    TestController.java
    测试类,类上记得要加@RestController或者方法上加@ResponseBody注解,不然解析参数时会报错:Circular view path [addTasks]: would dispatch back to the current handler URL [/addTasks]]
    代码中的map集合存放所需的业务参数

    package com.quartzs.demo.controller;
    
    import com.quartzs.demo.util.DateCronUtil;
    import com.quartzs.demo.util.QuartzJobManager;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    @RestController
    public class TestController {
    
        @Autowired
        private QuartzJobManager manager;
    
    
        @RequestMapping("/addTasks")
        public void addTask(String timeStr) throws Exception {
            Date date = DateCronUtil.dateTo(timeStr);
            String s = DateCronUtil.dateToCron(date);
            Map<String,Object> map = new HashMap<>();
            map.put("id","23984901");
    
            manager.addJob("23984901","quartzGroup",s,map);
            System.out.println("定时任务完成");
    
        }
    
        @RequestMapping("/updTasks")
        public void updTask(String timeStr) throws Exception {
            Date date = DateCronUtil.dateTo(timeStr);
            String s = DateCronUtil.dateToCron(date);
            String id = "23984901";
            Map<String,Object> map = new HashMap<>();
            map.put("id","xnsdiwnrk");
    
            manager.updateJob(id,"quartzGroup",s,map);
            System.out.println("定时任务修改完成");
    
        }
    }
    
    

    定时任务我需要的是指定时间执行,且只执行一次,我将时间转为cron表达式传入参数中,时间转换工具类在下面
    DateCronUtil.java

    package com.quartzs.demo.util;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class DateCronUtil {
        /**
         * Date-Cron表达式转化工具类
         * Quartz模块使用
         *
         * @author author
         * 2020-02-11 17:19
         */
        public static String dateToCron(Date date) {
            SimpleDateFormat format = new SimpleDateFormat("ss mm HH dd MM ? yyyy");
            return format.format(date);
        }
    
        /**
         * yyyyMMddHHmmss类型转换时间测试用
         * @param date
         * @return
         */
        public static Date dateTo(String date) {
            SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
            Date parse =null;
            try {
                parse = format.parse(date);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return parse;
        }
    }
    
    

    最后启动类上面加上事务开启的注解,deam完成
    DemoApplication.java

    package com.quartzs.demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    @SpringBootApplication
    @EnableTransactionManagement
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
    }
    
    

    最后附加quartz建表语句(mysql),需要其他数据库建表语句可以从quartz jar包的org.quartz.impl.jdbcjobstore位置下面下载

    -- mysql ---
    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);
    
    更多相关内容
  • springboot整合quartz,实现数据库方式执行定时任务。把定时任务信息存进数据库,项目启动后自动执行定时任务。
  • springboot整合quartz完整项目,采用cron类型表达式,可动态添加修改删除定时任务,保证真实可用!可自己扩展
  • 主要介绍了Springboot整合quartz产生错误及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • Springboot整合Quartz实现定时任务数据库动态配置,新增任务、修改cron表达式、暂停任务、恢复任务、删除任务等操作
  • 本篇文章主要介绍了springboot整合Quartz实现动态配置定时任务的方法,非常具有实用价值,需要的朋友可以参考下
  • springboot整合quartz的demo,实现了simpleTrigger和cronTrigger,具体功能如下:项目启动后,每隔两秒输出一串星号,每隔三秒输出一串#号,访问controller后,添加定时任务,十秒后输出访问者的ip
  • springboot整合Quartz实现动态配置定时任务源码
  • spring-cloud-alibaba+dubbo+nacos+quartz以yml配置方式实现
  • 1、若是部署多台机器,到了时间点,只有一台会执行,其他不会执行。 2、若多个节点其中一个scheduler执行job失败,将会被另外一个scheduler执行
  • SpringBoot整合Quartz

    千次阅读 多人点赞 2021-11-02 23:17:07
    目录`Quartz` 简介 Quartz 简介 Quartz 是一款功能强大的开源任务调度框架,几乎可以集成到任何 Java 应用程序中(小到单机应用,大到分布式应用)。Quartz 可用于创建简单或复杂的任务调度,用以执行数以万计的任务...

    Quartz

    Quartz 简介

    QuartzOpenSymphony 开源组织在 Job Scheduling 领域又一个开源项目,是完全由 Java 开发的一个开源任务日程管理系统,“任务进度管理器”就是一个在预先确定(被纳入日程)的时间到达时,负责执行(或者通知)其他软件组件的系统。 Quartz 是一个开源的作业调度框架,它完全由 Java 写成,并设计用于 J2SEJ2EE 应用中,它提供了巨大的灵活性而不牺牲简单性

    当定时任务愈加复杂时,使用 Spring 注解 @Schedule 已经不能满足业务需要

    在项目开发中,经常需要定时任务来帮助我们来做一些内容,如定时派息、跑批对账、将任务纳入日程或者从日程中取消,开始,停止,暂停日程进度等。SpringBoot 中现在有两种方案可以选择,第一种是 SpringBoot 内置的方式简单注解就可以使用,当然如果需要更复杂的应用场景还是得 Quartz 上场,Quartz 目前是 Java 体系中最完善的定时方案

    官方网站:http://quartz-scheduler.org/

    Quartz 优点

    • 丰富的 Job 操作 API
    • 支持多种配置
    • SpringBoot 无缝集成
    • 支持持久化
    • 支持集群
    • Quartz 还支持开源,是一个功能丰富的开源作业调度库,可以集成到几乎任何 Java 应用程序中

    核心概念

    • SchedulerQuartz 中的任务调度器,通过 TriggerJobDetail 可以用来调度、暂停和删除任务。调度器就相当于一个容器,装载着任务和触发器,该类是一个接口,代表一个 Quartz 的独立运行容器,TriggerJobDetail 可以注册到 Scheduler 中,两者在 Scheduler 中拥有各自的组及名称,组及名称是 Scheduler 查找定位容器中某一对象的依据,Trigger 的组及名称必须唯一,JobDetail 的组和名称也必须唯一(但可以和 Trigger 的组和名称相同,因为它们是不同类型的)
    • TriggerQuartz 中的触发器,是一个类,描述触发 Job 执行的时间触发规则,主要有 SimpleTriggerCronTrigger 这两个子类。当且仅当需调度一次或者以固定时间间隔周期执行调度,SimpleTrigger 是最适合的选择;而 CronTrigger 则可以通过 Cron 表达式定义出各种复杂时间规则的调度方案:如工作日周一到周五的 15:00 ~ 16:00 执行调度等
    • JobDetailQuartz 中需要执行的任务详情,包括了任务的唯一标识和具体要执行的任务,可以通过 JobDataMap 往任务中传递数据
    • JobQuartz 中具体的任务,包含了执行任务的具体方法。是一个接口,只定义一个方法 execute() 方法,在实现接口的 execute() 方法中编写所需要定时执行的 Job

    当然可以这样快速理解:

    • job:任务 - 你要做什么事
    • Trigger:触发器 - 你什么时候去做
    • Scheduler:任务调度 - 你什么时候需要做什么事

    四者其关系如下图所示

    在这里插入图片描述
    Job 为作业的接口,为任务调度的对象;JobDetail 用来描述 Job 的实现类及其他相关的静态信息;Trigger 做为作业的定时管理工具,一个 Trigger 只能对应一个作业实例,而一个作业实例可对应多个触发器;Scheduler 做为定时任务容器,是 Quartz 最上层的东西,它提携了所有触发器和作业,使它们协调工作,每个 Scheduler 都存有 JobDetailTrigger 的注册,一个 Scheduler 中可以注册多个 JobDetail 和多个 Trigger

    Quartz 的作业存储类型

    • RAMJobStoreRAM 也就是内存,默认情况下 Quartz 会将任务调度存储在内存中,这种方式性能是最好的,因为内存的速度是最快的。不好的地方就是数据缺乏持久性,但程序崩溃或者重新发布的时候,所有运行信息都会丢失
    • JDBC 作业存储:存到数据库之后,可以做单点也可以做集群,当任务多了之后,可以统一进行管理,随时停止、暂停、修改任务。关闭或者重启服务器,运行的信息都不会丢失。缺点就是运行速度快慢取决于连接数据库的快慢

    Cron 表达式

    Cron 表达式是一个字符串,包括 6~7 个时间元素,在 Quartz 中可以用于指定任务的执行时间

    Cron 语法

    Seconds Minutes Hours DayofMonth Month DayofWeek
      秒	  分钟	 小时   日期天/日  日期月份  星期
    

    Cron 语法中每个时间元素的说明

    时间元素可出现的字符有效数值范围
    Seconds, - * /0-59
    Minutes, - * /0-59
    Hours, - * /0-23
    DayofMonth, - * / ? L W0-31
    Month, - * /1-12
    DayofWeek, - * / ? L #1-7或SUN-SAT

    Cron 语法中特殊字符说明

    字符作用举例
    ,列出枚举值在Minutes域使用5,10,表示在5分和10分各触发一次
    -表示触发范围在Minutes域使用5-10,表示从5分到10分钟每分钟触发一次
    *匹配任意值在Minutes域使用*, 表示每分钟都会触发一次
    /起始时间开始触发,每隔固定时间触发一次在Minutes域使用5/10,表示5分时触发一次,每10分钟再触发一次
    ?在DayofMonth和DayofWeek中,用于匹配任意值在DayofMonth域使用?,表示每天都触发一次
    #在DayofMonth中,确定第几个星期几1#3表示第三个星期日
    L表示最后在DayofWeek中使用5L,表示在最后一个星期四触发
    W表示有效工作日(周一到周五)在DayofMonth使用5W,如果5日是星期六,则将在最近的工作日4日触发一次

    在线 Cron 表达式生成器

    其实 Cron 表达式无需多记,需要使用的时候直接使用在线生成器就可以了,地址:https://cron.qqe2.com/

    Springboot 整合 Quartz

    • SpringBoot 版本:2.0.9.RELEASE
    • MySQL 版本:5.7.35

    数据库表准备

    Quartz 存储任务信息有两种方式,使用内存或者使用数据库来存储,这里我们采用 MySQL 数据库存储的方式,首先需要新建 Quartz 的相关表,sql 脚本下载地址:http://www.quartz-scheduler.org/downloads/,名称为 tables_mysql.sql,创建成功后数据库中多出 11 张表

    Maven 主要依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>
    <!--mysql 驱动-->
    <dependency>
    	<groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    	<version>5.1.38</version>
    </dependency>
    <dependency>
    	<groupId>com.alibaba</groupId>
    	<artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.10</version>
    </dependency>
    <!--mybatis-->
    <dependency>
    	<groupId>org.mybatis.spring.boot</groupId>
    	<artifactId>mybatis-spring-boot-starter</artifactId>
    	<version>1.3.2</version>
    </dependency>
    <!--pagehelper分页-->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
    	<version>1.3.0</version>
    </dependency>
    

    这里使用 druid 作为数据库连接池,Quartz 默认使用 c3p0

    配置文件

    quartz.properties

    默认情况下,Quartz 会加载 classpath 下的 quartz.properties 作为配置文件。如果找不到,则会使用 quartz 框架自己 jar 包下 org/quartz 底下的 quartz.properties 文件

    #主要分为scheduler、threadPool、jobStore、dataSource等部分
    
    
    org.quartz.scheduler.instanceId=AUTO
    org.quartz.scheduler.instanceName=DefaultQuartzScheduler
    #如果您希望Quartz Scheduler通过RMI作为服务器导出本身,则将“rmi.export”标志设置为true
    #在同一个配置文件中为'org.quartz.scheduler.rmi.export'和'org.quartz.scheduler.rmi.proxy'指定一个'true'值是没有意义的,如果你这样做'export'选项将被忽略
    org.quartz.scheduler.rmi.export=false
    #如果要连接(使用)远程服务的调度程序,则将“org.quartz.scheduler.rmi.proxy”标志设置为true。您还必须指定RMI注册表进程的主机和端口 - 通常是“localhost”端口1099
    org.quartz.scheduler.rmi.proxy=false
    org.quartz.scheduler.wrapJobExecutionInUserTransaction=false
    
    
    #实例化ThreadPool时,使用的线程类为SimpleThreadPool
    org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
    #threadCount和threadPriority将以setter的形式注入ThreadPool实例
    #并发个数  如果你只有几个工作每天触发几次 那么1个线程就可以,如果你有成千上万的工作,每分钟都有很多工作 那么久需要50-100之间.
    #只有1到100之间的数字是非常实用的
    org.quartz.threadPool.threadCount=5
    #优先级 默认值为5
    org.quartz.threadPool.threadPriority=5
    #可以是“true”或“false”,默认为false
    org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
    
    
    #在被认为“misfired”(失火)之前,调度程序将“tolerate(容忍)”一个Triggers(触发器)将其下一个启动时间通过的毫秒数。默认值(如果您在配置中未输入此属性)为60000(60秒)
    org.quartz.jobStore.misfireThreshold=5000
    # 默认存储在内存中,RAMJobStore快速轻便,但是当进程终止时,所有调度信息都会丢失
    #org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore
    
    #持久化方式,默认存储在内存中,此处使用数据库方式
    org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
    #您需要为JobStore选择一个DriverDelegate才能使用。DriverDelegate负责执行特定数据库可能需要的任何JDBC工作
    # StdJDBCDelegate是一个使用“vanilla”JDBC代码(和SQL语句)来执行其工作的委托,用于完全符合JDBC的驱动程序
    org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
    #可以将“org.quartz.jobStore.useProperties”配置参数设置为“true”(默认为false),以指示JDBCJobStore将JobDataMaps中的所有值都作为字符串,
    #因此可以作为名称 - 值对存储而不是在BLOB列中以其序列化形式存储更多复杂的对象。从长远来看,这是更安全的,因为您避免了将非String类序列化为BLOB的类版本问题
    org.quartz.jobStore.useProperties=true
    #表前缀
    org.quartz.jobStore.tablePrefix=QRTZ_
    #数据源别名,自定义
    org.quartz.jobStore.dataSource=qzDS
    
    
    #使用阿里的druid作为数据库连接池
    org.quartz.dataSource.qzDS.connectionProvider.class=org.example.config.DruidPoolingconnectionProvider
    org.quartz.dataSource.qzDS.URL=jdbc:mysql://127.0.0.1:3306/test_quartz?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTC
    org.quartz.dataSource.qzDS.user=root
    org.quartz.dataSource.qzDS.password=123456
    org.quartz.dataSource.qzDS.driver=com.mysql.jdbc.Driver
    org.quartz.dataSource.qzDS.maxConnections=10
    #设置为“true”以打开群集功能。如果您有多个Quartz实例使用同一组数据库表,则此属性必须设置为“true”,否则您将遇到破坏
    #org.quartz.jobStore.isClustered=false
    

    关于配置详细解释:https://blog.csdn.net/zixiao217/article/details/53091812

    也可以查看官网:http://www.quartz-scheduler.org/documentation/2.3.1-SNAPSHOT/

    application.properties

    server.port=8080
    
    #JDBC 配置
    spring.datasource.druid.url=jdbc:mysql://127.0.0.1:3306/test_quartz?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTC
    spring.datasource.druid.username=root
    spring.datasource.druid.password=123456
    spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    
    #druid 连接池配置
    spring.datasource.druid.initial-size=3
    spring.datasource.druid.min-idle=3
    spring.datasource.druid.max-active=10
    spring.datasource.druid.max-wait=60000
    
    #指定 mapper 文件路径
    mybatis.mapper-locations=classpath:org/example/mapper/*.xml
    mybatis.configuration.cache-enabled=true
    #开启驼峰命名
    mybatis.configuration.map-underscore-to-camel-case=true
    #打印 SQL 语句
    mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    

    quartz 配置类 QuartzConfig

    @Configuration
    public class QuartzConfig implements SchedulerFactoryBeanCustomizer {
    
        @Bean
        public Properties properties() throws IOException {
            PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
            // 对quartz.properties文件进行读取
            propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
            // 在quartz.properties中的属性被读取并注入后再初始化对象
            propertiesFactoryBean.afterPropertiesSet();
            return propertiesFactoryBean.getObject();
        }
    
        @Bean
        public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
            SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
            schedulerFactoryBean.setQuartzProperties(properties());
            return schedulerFactoryBean;
        }
    
        /*
         * quartz初始化监听器
         */
        @Bean
        public QuartzInitializerListener executorListener() {
            return new QuartzInitializerListener();
        }
    
        /*
         * 通过SchedulerFactoryBean获取Scheduler的实例
         */
        @Bean
        public Scheduler scheduler() throws IOException {
            return schedulerFactoryBean().getScheduler();
        }
    
        /**
         * 使用阿里的druid作为数据库连接池
         */
        @Override
        public void customize(@NotNull SchedulerFactoryBean schedulerFactoryBean) {
            schedulerFactoryBean.setStartupDelay(2);
            schedulerFactoryBean.setAutoStartup(true);
            schedulerFactoryBean.setOverwriteExistingJobs(true);
        }
    }
    

    创建任务类 HelloJob

    @Slf4j
    public class HelloJob implements Job {
    
        @Override
        public void execute(JobExecutionContext jobExecutionContext) {
            QuartzService quartzService = (QuartzService) SpringUtil.getBean("quartzServiceImpl");
            PageInfo<JobAndTriggerDto> jobAndTriggerDetails = quartzService.getJobAndTriggerDetails(1, 10);
            log.info("任务列表总数为:" + jobAndTriggerDetails.getTotal());
            log.info("Hello Job执行时间: " + DateUtil.now());
        }
    }
    

    业务 Service

    具体的 QuartzService 接口这里不在赘述,可以查看后面的源码

    @Slf4j
    @Service
    public class QuartzServiceImpl implements QuartzService {
    
        @Autowired
        private JobDetailMapper jobDetailMapper;
    
        @Autowired
        private Scheduler scheduler;
    
        @Override
        public PageInfo<JobAndTriggerDto> getJobAndTriggerDetails(Integer pageNum, Integer pageSize) {
            PageHelper.startPage(pageNum, pageSize);
            List<JobAndTriggerDto> list = jobDetailMapper.getJobAndTriggerDetails();
            PageInfo<JobAndTriggerDto> pageInfo = new PageInfo<>(list);
            return pageInfo;
        }
    
        /**
         * 新增定时任务
         *
         * @param jName 任务名称
         * @param jGroup 任务组
         * @param tName 触发器名称
         * @param tGroup 触发器组
         * @param cron cron表达式
         */
        @Override
        public void addjob(String jName, String jGroup, String tName, String tGroup, String cron) {
            try {
                // 构建JobDetail
                JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                        .withIdentity(jName, jGroup)
                        .build();
                // 按新的cronExpression表达式构建一个新的trigger
                CronTrigger trigger = TriggerBuilder.newTrigger()
                        .withIdentity(tName, tGroup)
                        .startNow()
                        .withSchedule(CronScheduleBuilder.cronSchedule(cron))
                        .build();
                // 启动调度器
                scheduler.start();
                scheduler.scheduleJob(jobDetail, trigger);
            } catch (Exception e) {
                log.info("创建定时任务失败" + e);
            }
        }
    
        @Override
        public void pausejob(String jName, String jGroup) throws SchedulerException {
            scheduler.pauseJob(JobKey.jobKey(jName, jGroup));
        }
    
        @Override
        public void resumejob(String jName, String jGroup) throws SchedulerException {
            scheduler.resumeJob(JobKey.jobKey(jName, jGroup));
        }
    
        @Override
        public void rescheduleJob(String jName, String jGroup, String cron) throws SchedulerException {
            TriggerKey triggerKey = TriggerKey.triggerKey(jName, jGroup);
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            // 按新的trigger重新设置job执行,重启触发器
            scheduler.rescheduleJob(triggerKey, trigger);
        }
    
        @Override
        public void deletejob(String jName, String jGroup) throws SchedulerException {
            scheduler.pauseTrigger(TriggerKey.triggerKey(jName, jGroup));
            scheduler.unscheduleJob(TriggerKey.triggerKey(jName, jGroup));
            scheduler.deleteJob(JobKey.jobKey(jName, jGroup));
        }
    }
    

    Controller

    @Slf4j
    @Controller
    @RequestMapping(path = "/quartz")
    public class QuartzController {
    
        @Autowired
        private QuartzService quartzService;
    
        /**
         * 新增定时任务
         *
         * @param jName 任务名称
         * @param jGroup 任务组
         * @param tName 触发器名称
         * @param tGroup 触发器组
         * @param cron cron表达式
         * @return ResultMap
         */
        @PostMapping(path = "/addjob")
        @ResponseBody
        public ResultMap addjob(String jName, String jGroup, String tName, String tGroup, String cron) {
            try {
                quartzService.addjob(jName, jGroup, tName, tGroup, cron);
                return new ResultMap().success().message("添加任务成功");
            } catch (Exception e) {
                e.printStackTrace();
                return new ResultMap().error().message("添加任务失败");
            }
        }
    
        /**
         * 暂停任务
         *
         * @param jName 任务名称
         * @param jGroup 任务组
         * @return ResultMap
         */
        @PostMapping(path = "/pausejob")
        @ResponseBody
        public ResultMap pausejob(String jName, String jGroup) {
            try {
                quartzService.pausejob(jName, jGroup);
                return new ResultMap().success().message("暂停任务成功");
            } catch (SchedulerException e) {
                e.printStackTrace();
                return new ResultMap().error().message("暂停任务失败");
            }
        }
    
        /**
         * 恢复任务
         *
         * @param jName 任务名称
         * @param jGroup 任务组
         * @return ResultMap
         */
        @PostMapping(path = "/resumejob")
        @ResponseBody
        public ResultMap resumejob(String jName, String jGroup) {
            try {
                quartzService.resumejob(jName, jGroup);
                return new ResultMap().success().message("恢复任务成功");
            } catch (SchedulerException e) {
                e.printStackTrace();
                return new ResultMap().error().message("恢复任务失败");
            }
        }
    
        /**
         * 重启任务
         *
         * @param jName 任务名称
         * @param jGroup 任务组
         * @param cron cron表达式
         * @return ResultMap
         */
        @PostMapping(path = "/reschedulejob")
        @ResponseBody
        public ResultMap rescheduleJob(String jName, String jGroup, String cron) {
            try {
                quartzService.rescheduleJob(jName, jGroup, cron);
                return new ResultMap().success().message("重启任务成功");
            } catch (SchedulerException e) {
                e.printStackTrace();
                return new ResultMap().error().message("重启任务失败");
            }
        }
    
        /**
         * 删除任务
         *
         * @param jName 任务名称
         * @param jGroup 任务组
         * @return ResultMap
         */
        @PostMapping(path = "/deletejob")
        @ResponseBody
        public ResultMap deletejob(String jName, String jGroup) {
            try {
                quartzService.deletejob(jName, jGroup);
                return new ResultMap().success().message("删除任务成功");
            } catch (SchedulerException e) {
                e.printStackTrace();
                return new ResultMap().error().message("删除任务失败");
            }
        }
    
        /**
         * 查询任务
         *
         * @param pageNum 页码
         * @param pageSize 每页显示多少条数据
         * @return Map
         */
        @GetMapping(path = "/queryjob")
        @ResponseBody
        public ResultMap queryjob(Integer pageNum, Integer pageSize) {
            PageInfo<JobAndTriggerDto> pageInfo = quartzService.getJobAndTriggerDetails(pageNum, pageSize);
            Map<String, Object> map = new HashMap<>();
            if (!StringUtils.isEmpty(pageInfo.getTotal())) {
                map.put("JobAndTrigger", pageInfo);
                map.put("number", pageInfo.getTotal());
                return new ResultMap().success().data(map).message("查询任务成功");
            }
            return new ResultMap().fail().message("查询任务成功失败,没有数据");
        }
    }
    

    接口测试

    新增定时任务

    postman 测试如下

    在这里插入图片描述
    数据库数据展示如下

    在这里插入图片描述
    在这里插入图片描述
    同样,我们的任务类 HelloJob 也开始执行了,控制台日志如下

    在这里插入图片描述

    停止项目,再启动运行

    可以看到项目中 HelloJob 的任务依然在运行,这就是 quartz 数据库持久化的好处

    在这里插入图片描述
    源码:https://gitee.com/chaojiangcj/springboot-quartz.git

    展开全文
  • springboot 整合 quartz

    2022-04-21 11:40:35
    quartz-project: quartz 2. 环境信息 springboot 2.5.4 3. 搭建流程 3.1 创建一个springboot项目 (略) 3.2 pom.xml <dependencies> <!-- springboot启动文件 --> <dependency> <...

    1. 示例代码 :

    quartz-project: quartz

    2. 环境信息

    springboot 2.5.4

    3. 搭建流程

    3.1 创建一个springboot项目 (略)

    3.2 pom.xml

    <dependencies>
    
            <!-- springboot启动文件 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-quartz</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <!--druid数据库连接池-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.20</version>
            </dependency>
    
            <!--mybatisplus-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.4.2</version>
            </dependency>
            <!--mybatisplus 代码生成器-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-generator</artifactId>
                <version>3.4.1</version>
            </dependency>
    
            <!--hutool工具类-->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.3.4</version>
            </dependency>
    
            <!--hutool 表情类扩展包-->
            <dependency>
                <groupId>com.vdurmont</groupId>
                <artifactId>emoji-java</artifactId>
                <version>4.0.0</version>
            </dependency>
    
        </dependencies>

    application.yml

    server:
      port: 9999
    
    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        password: 123456
        url: jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowMutiQueries=true
        username: root
        #   配置数据库连接池
        type: com.alibaba.druid.pool.DruidDataSource
        druid:
          initial-size: 1
          min-idle: 1
          max-active: 20
          max-wait: 60000
          test-while-idle: true
          time-between-connect-error-millis: 60000
          min-evictable-idle-time-millis: 30000
          validation-query: select 'x'
          test-on-borrow: false
          test-on-return: false
          pool-prepared-statements: true
          max-pool-prepared-statement-per-connection-size: 20
          use-global-data-source-stat: false
          filters: stat,wall,slf4j
          connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
          time-between-log-stats-millis: 300000
          username: ${spring.datasource.username}
          password: ${spring.datasource.password}
          url : ${spring.datasource.url}
          driver-class-name: ${spring.datasource.driver-class-name}
    

    quartz.properties

    #主要分为scheduler、threadPool、jobStore、dataSource等部分
    
    
    org.quartz.scheduler.instanceId=AUTO
    #如果您希望Quartz Scheduler通过RMI作为服务器导出本身,则将“rmi.export”标志设置为true
    #在同一个配置文件中为'org.quartz.scheduler.rmi.export'和'org.quartz.scheduler.rmi.proxy'指定一个'true'值是没有意义的,如果你这样做'export'选项将被忽略
    org.quartz.scheduler.rmi.export=false
    #如果要连接(使用)远程服务的调度程序,则将“org.quartz.scheduler.rmi.proxy”标志设置为true。您还必须指定RMI注册表进程的主机和端口 - 通常是“localhost”端口1099
    org.quartz.scheduler.rmi.proxy=false
    org.quartz.scheduler.wrapJobExecutionInUserTransaction=false
    
    
    #实例化ThreadPool时,使用的线程类为SimpleThreadPool
    org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
    #threadCount和threadPriority将以setter的形式注入ThreadPool实例
    #并发个数  如果你只有几个工作每天触发几次 那么1个线程就可以,如果你有成千上万的工作,每分钟都有很多工作 那么久需要50-100之间.
    #只有1到100之间的数字是非常实用的
    org.quartz.threadPool.threadCount=5
    #优先级 默认值为5
    org.quartz.threadPool.threadPriority=5
    #可以是“true”或“false”,默认为false
    org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
    
    
    #在被认为“misfired”(失火)之前,调度程序将“tolerate(容忍)”一个Triggers(触发器)将其下一个启动时间通过的毫秒数。默认值(如果您在配置中未输入此属性)为60000(60秒)
    org.quartz.jobStore.misfireThreshold=5000
    # 默认存储在内存中,RAMJobStore快速轻便,但是当进程终止时,所有调度信息都会丢失
    #org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore
    
    #持久化方式,默认存储在内存中,此处使用数据库方式
    org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
    #您需要为JobStore选择一个DriverDelegate才能使用。DriverDelegate负责执行特定数据库可能需要的任何JDBC工作
    # StdJDBCDelegate是一个使用“vanilla”JDBC代码(和SQL语句)来执行其工作的委托,用于完全符合JDBC的驱动程序
    org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
    #可以将“org.quartz.jobStore.useProperties”配置参数设置为“true”(默认为false),以指示JDBCJobStore将JobDataMaps中的所有值都作为字符串,
    #因此可以作为名称 - 值对存储而不是在BLOB列中以其序列化形式存储更多复杂的对象。从长远来看,这是更安全的,因为您避免了将非String类序列化为BLOB的类版本问题
    org.quartz.jobStore.useProperties=true
    #表前缀
    org.quartz.jobStore.tablePrefix=QRTZ_
    #数据源别名,自定义
    org.quartz.jobStore.dataSource=qzDS
    
    
    #使用阿里的druid作为数据库连接池
    org.quartz.dataSource.qzDS.connectionProvider.class=com.hctrl.quartz.config.DruidConnectionProvider
    org.quartz.dataSource.qzDS.URL=jdbc:mysql://127.0.0.1:3306/quartz?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTC
    org.quartz.dataSource.qzDS.user=root
    org.quartz.dataSource.qzDS.password=123456
    org.quartz.dataSource.qzDS.driver=com.mysql.cj.jdbc.Driver
    org.quartz.dataSource.qzDS.maxConnection=10
    #设置为“true”以打开群集功能。如果您有多个Quartz实例使用同一组数据库表,则此属性必须设置为“true”,否则您将遇到破坏
    #org.quartz.jobStore.isClustered=false
    

    quartz.sql (项目启动不会自动创建表, 需要在数据库中手动创建表)

    /*
     Navicat Premium Data Transfer
    
     Source Server         : localhost
     Source Server Type    : MySQL
     Source Server Version : 80025
     Source Host           : localhost:3306
     Source Schema         : quartz
    
     Target Server Type    : MySQL
     Target Server Version : 80025
     File Encoding         : 65001
    
     Date: 21/04/2022 11:17:50
    */
    
    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    -- Table structure for qrtz_blob_triggers
    -- ----------------------------
    DROP TABLE IF EXISTS `qrtz_blob_triggers`;
    CREATE TABLE `qrtz_blob_triggers`  (
      `sched_name` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '计划名',
      `trigger_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器名称',
      `trigger_group` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器组',
      `blob_data` blob,
      PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
      INDEX `sched_name`(`sched_name`) USING BTREE,
      CONSTRAINT `qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '以blob 类型存储的触发器' ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Table structure for qrtz_calendars
    -- ----------------------------
    DROP TABLE IF EXISTS `qrtz_calendars`;
    CREATE TABLE `qrtz_calendars`  (
      `sched_name` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '计划名称',
      `calendar_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
      `calendar` blob NOT NULL,
      PRIMARY KEY (`sched_name`, `calendar_name`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '日历信息表' ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Table structure for qrtz_cron_triggers
    -- ----------------------------
    DROP TABLE IF EXISTS `qrtz_cron_triggers`;
    CREATE TABLE `qrtz_cron_triggers`  (
      `sched_name` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '计划名称',
      `trigger_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器名称',
      `trigger_group` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器组',
      `cron_expression` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '时间表达式',
      `time_zone_id` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '时区id',
      PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
      CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '定时触发器表' ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Table structure for qrtz_fired_triggers
    -- ----------------------------
    DROP TABLE IF EXISTS `qrtz_fired_triggers`;
    CREATE TABLE `qrtz_fired_triggers`  (
      `sched_name` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '计划名称',
      `entry_id` varchar(95) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '组标识',
      `trigger_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器名称',
      `trigger_group` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器组',
      `instance_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '当前实例的名称',
      `fired_time` bigint(0) NOT NULL COMMENT '当前执行时间',
      `sched_time` bigint(0) NOT NULL COMMENT '计划时间',
      `priority` int(0) NOT NULL COMMENT '权重',
      `state` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '状态',
      `job_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '作业名称',
      `job_group` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '作业组',
      `is_nonconcurrent` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '是否并行',
      `requests_recovery` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '是否要求唤醒',
      PRIMARY KEY (`sched_name`, `entry_id`) USING BTREE,
      INDEX `idx_qrtz_ft_trig_inst_name`(`sched_name`, `instance_name`) USING BTREE,
      INDEX `idx_qrtz_ft_inst_job_req_rcvry`(`sched_name`, `instance_name`, `requests_recovery`) USING BTREE,
      INDEX `idx_qrtz_ft_j_g`(`sched_name`, `job_name`, `job_group`) USING BTREE,
      INDEX `idx_qrtz_ft_jg`(`sched_name`, `job_group`) USING BTREE,
      INDEX `idx_qrtz_ft_t_g`(`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
      INDEX `idx_qrtz_ft_tg`(`sched_name`, `trigger_group`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '保存已经触发的触发器状态信息' ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Table structure for qrtz_job_details
    -- ----------------------------
    DROP TABLE IF EXISTS `qrtz_job_details`;
    CREATE TABLE `qrtz_job_details`  (
      `sched_name` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '计划名称',
      `job_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '作业名称',
      `job_group` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '作业组',
      `description` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '描述',
      `job_class_name` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '作业程序类名',
      `is_durable` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '是否持久',
      `is_nonconcurrent` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '是否并行',
      `is_update_data` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '是否更新',
      `requests_recovery` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '是否要求唤醒',
      `job_data` blob COMMENT '作业名称',
      PRIMARY KEY (`sched_name`, `job_name`, `job_group`) USING BTREE,
      INDEX `idx_qrtz_j_req_recovery`(`sched_name`, `requests_recovery`) USING BTREE,
      INDEX `idx_qrtz_j_grp`(`sched_name`, `job_group`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'job 详细信息表' ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Table structure for qrtz_locks
    -- ----------------------------
    DROP TABLE IF EXISTS `qrtz_locks`;
    CREATE TABLE `qrtz_locks`  (
      `sched_name` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '计划名称',
      `lock_name` varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '锁名称',
      PRIMARY KEY (`sched_name`, `lock_name`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储程序的悲观锁的信息(假如使用了悲观锁) ' ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Table structure for qrtz_paused_trigger_grps
    -- ----------------------------
    DROP TABLE IF EXISTS `qrtz_paused_trigger_grps`;
    CREATE TABLE `qrtz_paused_trigger_grps`  (
      `sched_name` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '计划名称',
      `trigger_group` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器组',
      PRIMARY KEY (`sched_name`, `trigger_group`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存放暂停掉的触发器表' ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Table structure for qrtz_scheduler_state
    -- ----------------------------
    DROP TABLE IF EXISTS `qrtz_scheduler_state`;
    CREATE TABLE `qrtz_scheduler_state`  (
      `sched_name` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '计划名称',
      `instance_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '实例名称',
      `last_checkin_time` bigint(0) NOT NULL COMMENT '最后的检查时间',
      `checkin_interval` bigint(0) NOT NULL COMMENT '检查间隔',
      PRIMARY KEY (`sched_name`, `instance_name`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '调度器状态表' ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Table structure for qrtz_simple_triggers
    -- ----------------------------
    DROP TABLE IF EXISTS `qrtz_simple_triggers`;
    CREATE TABLE `qrtz_simple_triggers`  (
      `sched_name` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '计划名称',
      `trigger_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器名称',
      `trigger_group` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器组',
      `repeat_count` bigint(0) NOT NULL COMMENT '重复次数',
      `repeat_interval` bigint(0) NOT NULL COMMENT '重复间隔',
      `times_triggered` bigint(0) NOT NULL COMMENT '触发次数',
      PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
      CONSTRAINT `qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '简单的触发器表' ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Table structure for qrtz_simprop_triggers
    -- ----------------------------
    DROP TABLE IF EXISTS `qrtz_simprop_triggers`;
    CREATE TABLE `qrtz_simprop_triggers`  (
      `sched_name` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '计划名称',
      `trigger_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器名称',
      `trigger_group` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器组',
      `str_prop_1` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '计划名称',
      `str_prop_2` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '计划名称',
      `str_prop_3` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '计划名称',
      `int_prop_1` int(0) DEFAULT NULL,
      `int_prop_2` int(0) DEFAULT NULL,
      `long_prop_1` bigint(0) DEFAULT NULL,
      `long_prop_2` bigint(0) DEFAULT NULL,
      `dec_prop_1` decimal(13, 4) DEFAULT NULL,
      `dec_prop_2` decimal(13, 4) DEFAULT NULL,
      `bool_prop_1` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
      `bool_prop_2` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
      PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
      CONSTRAINT `qrtz_simprop_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储两种类型的触发器表' ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Table structure for qrtz_triggers
    -- ----------------------------
    DROP TABLE IF EXISTS `qrtz_triggers`;
    CREATE TABLE `qrtz_triggers`  (
      `sched_name` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '计划名称',
      `trigger_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器名称',
      `trigger_group` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器组',
      `job_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '作业名称',
      `job_group` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '作业组',
      `description` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '描述',
      `next_fire_time` bigint(0) DEFAULT NULL COMMENT '下次执行时间',
      `prev_fire_time` bigint(0) DEFAULT NULL COMMENT '前一次',
      `priority` int(0) DEFAULT NULL COMMENT '优先权',
      `trigger_state` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器状态',
      `trigger_type` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器类型',
      `start_time` bigint(0) NOT NULL COMMENT '开始时间',
      `end_time` bigint(0) DEFAULT NULL COMMENT '结束时间',
      `calendar_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '日历名称',
      `misfire_instr` smallint(0) DEFAULT NULL COMMENT '失败次数',
      `job_data` blob COMMENT '作业数据',
      PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
      INDEX `idx_qrtz_t_j`(`sched_name`, `job_name`, `job_group`) USING BTREE,
      INDEX `idx_qrtz_t_jg`(`sched_name`, `job_group`) USING BTREE,
      INDEX `idx_qrtz_t_c`(`sched_name`, `calendar_name`) USING BTREE,
      INDEX `idx_qrtz_t_g`(`sched_name`, `trigger_group`) USING BTREE,
      INDEX `idx_qrtz_t_state`(`sched_name`, `trigger_state`) USING BTREE,
      INDEX `idx_qrtz_t_n_state`(`sched_name`, `trigger_name`, `trigger_group`, `trigger_state`) USING BTREE,
      INDEX `idx_qrtz_t_n_g_state`(`sched_name`, `trigger_group`, `trigger_state`) USING BTREE,
      INDEX `idx_qrtz_t_next_fire_time`(`sched_name`, `next_fire_time`) USING BTREE,
      INDEX `idx_qrtz_t_nft_st`(`sched_name`, `trigger_state`, `next_fire_time`) USING BTREE,
      INDEX `idx_qrtz_t_nft_misfire`(`sched_name`, `misfire_instr`, `next_fire_time`) USING BTREE,
      INDEX `idx_qrtz_t_nft_st_misfire`(`sched_name`, `misfire_instr`, `next_fire_time`, `trigger_state`) USING BTREE,
      INDEX `idx_qrtz_t_nft_st_misfire_grp`(`sched_name`, `misfire_instr`, `next_fire_time`, `trigger_group`, `trigger_state`) USING BTREE,
      CONSTRAINT `qrtz_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `job_name`, `job_group`) REFERENCES `qrtz_job_details` (`sched_name`, `job_name`, `job_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '触发器表' ROW_FORMAT = Dynamic;
    
    SET FOREIGN_KEY_CHECKS = 1;
    

    3.3 代码

    DruidConfig :  druid数据源配置

    package com.hctrl.quartz.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.hctrl.quartz.properties.DruidDataSourceProperties;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.sql.DataSource;
    import java.sql.SQLException;
    
    /**
     * @author hanchao
     * @version 1.0
     * @date 2022/4/21 9:54
     */
    @Configuration
    @EnableConfigurationProperties({ DruidDataSourceProperties.class })
    public class DruidConfig {
    
        /**
         * 此处bean name设置为druidDataSource1 ,  防止冲突
         * @param properties
         * @return
         */
        @Bean
        @ConditionalOnMissingBean
        public DataSource druidDataSource1(DruidDataSourceProperties properties) {
    
            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.setDriverClassName(properties.getDriverClassName());
            druidDataSource.setUrl(properties.getUrl());
            druidDataSource.setUsername(properties.getUsername());
            druidDataSource.setPassword(properties.getPassword());
            druidDataSource.setInitialSize(properties.getInitialSize());
            druidDataSource.setMinIdle(properties.getMinIdle());
            druidDataSource.setMaxActive(properties.getMaxActive());
            druidDataSource.setMaxWait(properties.getMaxWait());
            druidDataSource.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRunsMillis());
            druidDataSource.setMinEvictableIdleTimeMillis(properties.getMinEvictableIdleTimeMillis());
            druidDataSource.setValidationQuery(properties.getValidationQuery());
            druidDataSource.setTestWhileIdle(properties.isTestWhileIdle());
            druidDataSource.setTestOnBorrow(properties.isTestOnBorrow());
            druidDataSource.setTestOnReturn(properties.isTestOnReturn());
            druidDataSource.setPoolPreparedStatements(properties.isPoolPreparedStatements());
            druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(
                    properties.getMaxPoolPreparedStatementPerConnectionSize());
    
            try {
                druidDataSource.init();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return druidDataSource;
        }
    
    
    }
    

    DruidConnectionProvider : 将quartz数据源修改为druid, 类中的属性会自动读取配置文件, 写好get, set方法即可 

    package com.hctrl.quartz.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import org.quartz.SchedulerException;
    import org.quartz.utils.ConnectionProvider;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    
    /**
     * @author hanchao
     * @version 1.0
     * @date 2022/4/21 9:37
     */
    public class DruidConnectionProvider implements ConnectionProvider {
    
        //JDBC驱动
        public String driver;
        //JDBC连接串
        public String URL;
        //数据库用户名
        public String user;
        //数据库用户密码
        public String password;
        //数据库最大连接数
        public int maxConnection;
        //数据库SQL查询每次连接返回执行到连接池,以确保它仍然是有效的。
        public String validationQuery;
        private boolean validateOnCheckout;
        private int idleConnectionValidationSeconds;
        public String maxCachedStatementsPerConnection;
        private String discardIdleConnectionsSeconds;
        public static final int DEFAULT_DB_MAX_CONNECTIONS = 10;
        public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120;
        private DruidDataSource datasource;
    
    
        public Connection getConnection() throws SQLException {
            return datasource.getConnection();
        }
    
        public void shutdown() throws SQLException {
            datasource.close();
        }
        public void initialize() throws SQLException{
            if (this.URL == null) {
                throw new SQLException("DBPool could not be created: DB URL cannot be null");
            }
    
            if (this.driver == null) {
                throw new SQLException("DBPool driver could not be created: DB driver class name cannot be null!");
            }
    
            if (this.maxConnection < 0) {
                throw new SQLException("DBPool maxConnectins could not be created: Max connections must be greater than zero!");
            }
    
            datasource = new DruidDataSource();
            try{
                datasource.setDriverClassName(this.driver);
            } catch (Exception e) {
                try {
                    throw new SchedulerException("Problem setting driver class name on datasource: " + e.getMessage(), e);
                } catch (SchedulerException e1) {
                }
            }
            datasource.setUrl(this.URL);
            datasource.setUsername(this.user);
            datasource.setPassword(this.password);
            datasource.setMaxActive(this.maxConnection);
            datasource.setMinIdle(1);
            datasource.setMaxWait(0);
            datasource.setMaxPoolPreparedStatementPerConnectionSize(this.DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION);
    
            if (this.validationQuery != null) {
                datasource.setValidationQuery(this.validationQuery);
                if(!this.validateOnCheckout)
                    datasource.setTestOnReturn(true);
                else
                    datasource.setTestOnBorrow(true);
                datasource.setValidationQueryTimeout(this.idleConnectionValidationSeconds);
            }
        }
    
        /*
         * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         *
         * 提供get set方法
         *
         * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         */
        public String getDriver() {
            return driver;
        }
    
        public void setDriver(String driver) {
            this.driver = driver;
        }
    
        public String getURL() {
            return URL;
        }
    
        public void setURL(String URL) {
            this.URL = URL;
        }
    
        public String getUser() {
            return user;
        }
    
        public void setUser(String user) {
            this.user = user;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public int getMaxConnection() {
            return maxConnection;
        }
    
        public void setMaxConnection(int maxConnection) {
            this.maxConnection = maxConnection;
        }
    
        public String getValidationQuery() {
            return validationQuery;
        }
    
        public void setValidationQuery(String validationQuery) {
            this.validationQuery = validationQuery;
        }
    
        public boolean isValidateOnCheckout() {
            return validateOnCheckout;
        }
    
        public void setValidateOnCheckout(boolean validateOnCheckout) {
            this.validateOnCheckout = validateOnCheckout;
        }
    
        public int getIdleConnectionValidationSeconds() {
            return idleConnectionValidationSeconds;
        }
    
        public void setIdleConnectionValidationSeconds(int idleConnectionValidationSeconds) {
            this.idleConnectionValidationSeconds = idleConnectionValidationSeconds;
        }
    
        public DruidDataSource getDatasource() {
            return datasource;
        }
    
        public void setDatasource(DruidDataSource datasource) {
            this.datasource = datasource;
        }
    
    
    
    }
    
    QuartzConfig : quartz配置类
    package com.hctrl.quartz.config;
    
    import org.quartz.Scheduler;
    import org.quartz.ee.servlet.QuartzInitializerListener;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.config.PropertiesFactoryBean;
    import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
    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.annotation.Resource;
    import javax.sql.DataSource;
    import java.io.IOException;
    import java.util.Properties;
    
    /**
     * @author hanchao
     * @version 1.0
     * @date 2022/4/21 9:34
     */
    @Configuration
    public class QuartzConfig implements SchedulerFactoryBeanCustomizer {
    
        @Resource
        private DataSource druidDataSource1;
    
        @Bean
        public Properties properties() throws IOException {
            PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
            // 对quartz.properties文件进行读取
            propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
            // 在quartz.properties中的属性被读取并注入后再初始化对象
            propertiesFactoryBean.afterPropertiesSet();
            return propertiesFactoryBean.getObject();
        }
    
        @Bean
        public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
            SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
    //        schedulerFactoryBean.setQuartzProperties(properties());
            //此处一定要配置数据源, 否则创建的任务和触发器不用存入到数据库中
            schedulerFactoryBean.setDataSource(druidDataSource1);
            return schedulerFactoryBean;
        }
    
        /*
         * quartz初始化监听器
         */
        @Bean
        public QuartzInitializerListener executorListener() {
            return new QuartzInitializerListener();
        }
    
        /*
         * 通过SchedulerFactoryBean获取Scheduler的实例
         */
        @Bean
        public Scheduler scheduler() throws IOException {
            return schedulerFactoryBean().getScheduler();
        }
    
        /**
         * 使用阿里的druid作为数据库连接池
         */
        @Override
        public void customize(SchedulerFactoryBean schedulerFactoryBean) {
            schedulerFactoryBean.setStartupDelay(2);
            schedulerFactoryBean.setAutoStartup(true);
            schedulerFactoryBean.setOverwriteExistingJobs(true);
        }
    }
    
    
    DruidDataSourceProperties : 读取配置文件的配置信息, 创建druid数据源使用
    package com.hctrl.quartz.properties;
    
    import lombok.Data;
    import lombok.extern.apachecommons.CommonsLog;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    /**
     * @author hanchao
     * @version 1.0
     * @date 2022/4/21 9:52
     */
    @ConfigurationProperties(prefix="spring.datasource.druid")
    @Data
    public class DruidDataSourceProperties {
    
        private String driverClassName;
        private String url;
        private String username;
        private String password;
        // jdbc connection pool
        private int initialSize;
        private int minIdle;
        private int maxActive = 100;
        private long maxWait;
        private long timeBetweenEvictionRunsMillis;
        private long minEvictableIdleTimeMillis;
        private String validationQuery;
        private boolean testWhileIdle;
        private boolean testOnBorrow;
        private boolean testOnReturn;
        private boolean poolPreparedStatements;
        private int maxPoolPreparedStatementPerConnectionSize;
        // filter
        private String filters;
    
    
    
    }
    

    controller : 

    package com.hctrl.quartz.controller;
    
    import cn.hutool.cron.CronUtil;
    import cn.hutool.cron.pattern.CronPattern;
    import cn.hutool.cron.pattern.CronPatternUtil;
    import com.hctrl.quartz.service.QuartzService;
    import org.quartz.SchedulerException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import result.BaseResult;
    import result.CommonResult;
    
    /**
     *
     *
     *  每个任务JobDetail可以绑定多个Trigger,但一个Trigger只能绑定一个任务
     *
     * @author hanchao
     * @version 1.0
     * @date 2022/4/21 9:29
     */
    @RestController
    @RequestMapping("/v1/quartz")
    public class QuartzController {
    
        @Autowired
        private QuartzService quartzService;
    
        /**
         * 新增定时任务
         *
         * @param jName 任务名称
         * @param jGroup 任务组
         * @param tName 触发器名称
         * @param tGroup 触发器组
         * @param cron cron表达式
         * @return ResultMap
         */
        @PostMapping(path = "/addJob")
        public BaseResult addJob(String jName, String jGroup, String tName, String tGroup, String cron) {
            try {
                if (cron == null){
                    cron = "*/5 * * * * ?";
                }
                quartzService.addJob(jName, jGroup, tName, tGroup, cron);
                return CommonResult.buildSuccess("添加任务成功");
            } catch (Exception e) {
                e.printStackTrace();
                return CommonResult.buildFailure(500,"添加任务失败");
            }
        }
    
        /**
         * 暂停任务
         *
         * @param jName 任务名称
         * @param jGroup 任务组
         * @return ResultMap
         */
        @PostMapping(path = "/pauseJob")
        public BaseResult pauseJob(String jName, String jGroup) {
            try {
                quartzService.pauseJob(jName, jGroup);
                return CommonResult.buildSuccess("暂停任务成功");
            } catch (SchedulerException e) {
                e.printStackTrace();
                return CommonResult.buildFailure(500,"暂停任务失败");
            }
        }
    
        /**
         * 恢复任务
         *
         * @param jName 任务名称
         * @param jGroup 任务组
         * @return ResultMap
         */
        @PostMapping(path = "/resumeJob")
        public BaseResult resumeJob(String jName, String jGroup) {
            try {
                quartzService.resumeJob(jName, jGroup);
                return CommonResult.buildSuccess("恢复任务成功");
            } catch (SchedulerException e) {
                e.printStackTrace();
                return CommonResult.buildFailure(500,"恢复任务失败");
            }
        }
    
        /**
         * 重启任务
         *
         * @param jName 任务名称
         * @param jGroup 任务组
         * @param cron cron表达式
         * @return ResultMap
         */
        @PostMapping(path = "/reScheduleJob")
        public BaseResult rescheduleJob(String jName, String jGroup, String cron) {
            try {
                quartzService.reScheduleJob(jName, jGroup, cron);
                return CommonResult.buildSuccess("重启任务成功");
            } catch (SchedulerException e) {
                e.printStackTrace();
                return CommonResult.buildFailure(500,"重启任务失败");
            }
        }
    
        /**
         * 删除任务
         *
         * @param jName 任务名称
         * @param jGroup 任务组
         * @return ResultMap
         */
        @PostMapping(path = "/deleteJob")
        public BaseResult deleteJob(String jName, String jGroup) {
            try {
                quartzService.deleteJob(jName, jGroup);
                return CommonResult.buildSuccess("删除任务成功");
            } catch (SchedulerException e) {
                e.printStackTrace();
                return CommonResult.buildFailure(500,"删除任务失败");
            }
        }
    
    }
    

    service : 

    package com.hctrl.quartz.service;
    
    import com.hctrl.quartz.Job.HelloJob;
    import lombok.extern.slf4j.Slf4j;
    import org.quartz.*;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    /**
     * @author hanchao
     * @version 1.0
     * @date 2022/4/21 9:29
     */
    @Service
    @Slf4j
    public class QuartzService {
    
        @Autowired
        private Scheduler scheduler;
    
        /**
         * 新增定时任务
         *
         * @param jName 任务名称
         * @param jGroup 任务组
         * @param tName 触发器名称
         * @param tGroup 触发器组
         * @param cron cron表达式
         */
        public void addJob(String jName, String jGroup, String tName, String tGroup, String cron) {
            try {
                // 构建JobDetail,  可以设置dataMap,  用于传递参数
                JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                        .withIdentity(jName, jGroup)
                        .build();
                // 按新的cronExpression表达式构建一个新的trigger
                CronTrigger trigger = TriggerBuilder.newTrigger()
                        .withIdentity(tName, tGroup)
                        .startNow()
                        .withSchedule(CronScheduleBuilder.cronSchedule(cron))
                        .build();
                // 启动调度器
                scheduler.start();
                scheduler.scheduleJob(jobDetail, trigger);
            } catch (Exception e) {
                log.info("创建定时任务失败" + e);
            }
        }
    
        public void pauseJob(String jName, String jGroup) throws SchedulerException {
            scheduler.pauseJob(JobKey.jobKey(jName, jGroup));
        }
    
        public void resumeJob(String jName, String jGroup) throws SchedulerException {
            scheduler.resumeJob(JobKey.jobKey(jName, jGroup));
        }
    
        public void reScheduleJob(String jName, String jGroup, String cron) throws SchedulerException {
            TriggerKey triggerKey = TriggerKey.triggerKey(jName, jGroup);
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            // 按新的trigger重新设置job执行,重启触发器
            scheduler.rescheduleJob(triggerKey, trigger);
        }
    
        public void deleteJob(String jName, String jGroup) throws SchedulerException {
            scheduler.pauseTrigger(TriggerKey.triggerKey(jName, jGroup));
            scheduler.unscheduleJob(TriggerKey.triggerKey(jName, jGroup));
            scheduler.deleteJob(JobKey.jobKey(jName, jGroup));
        }
    }
    

    HelloJob : 执行任务类, 主要逻辑

    package com.hctrl.quartz.Job;
    
    import cn.hutool.core.date.DatePattern;
    import cn.hutool.core.date.DateUtil;
    import org.quartz.DisallowConcurrentExecution;
    import org.quartz.Job;
    import org.quartz.JobExecutionContext;
    import org.quartz.PersistJobDataAfterExecution;
    
    import java.time.LocalDateTime;
    
    /**
     *
     * @DisallowConcurrentExecution : 防止并发
     *
     * @PersistJobDataAfterExecution : 默认每次执行都会创建一个新的HelloJob对象, 添加这个注解, 则变成单例
     *
     * org.quartz.jobStore.misfireThreshold : 重要配置, 当一个任务到了时间执行, 但是上一个任务还没执行完成,
     *                                        并且添加了@DisallowConcurrentExecution注解, 则本次任务会延期执行,
     *                                        延期时间由这个配置决定, 如果配置了5s, 并且延期了5秒还是没有执行, 则本次任务不会执行
     *
     * @author hanchao
     * @version 1.0
     * @date 2022/4/21 10:22
     */
    @DisallowConcurrentExecution
    @PersistJobDataAfterExecution
    public class HelloJob implements Job {
    
        @Override
        public void execute(JobExecutionContext context) {
    
            String name = context.getJobDetail().getKey().getName();
    
            String format = DateUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN);
            System.out.println(name + "   Hello Job执行时间: " + format);
        }
    }
    

    结果图 : 

     

    展开全文
  • Springboot整合Quartz

    2020-04-21 15:33:48
    1、Quartz核心概念 首先我们需要明白 Quartz 的几个核心概念,这样理解起 Quartz 的原理就会变得简单了。 Job表示一个工作,要执行的具体内容。此接口中只有一个方法,如下:void execute(JobExecutionContext ...

    1、Quartz核心概念

    首先我们需要明白 Quartz 的几个核心概念,这样理解起 Quartz 的原理就会变得简单了。

    1. Job 表示一个工作,要执行的具体内容。此接口中只有一个方法,如下:void execute(JobExecutionContext context) 
    2. JobDetail 表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。
    3. Trigger 代表一个调度参数的配置,什么时候去调。
    4. Scheduler 代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了

    2、具体分析

    2.1 Job其实由三部分组成:

    •       JobDetail: 用于描述这个Job是做什么的
    •       一个实现Job接口的类:告诉Quartz具体干什么活的
    •      JobDataMap: 给 Job 提供参数用的
    JobDetail jobDetail = newJob(TestJob.class)
                    .withIdentity("job1","group2")
                    .usingJobData("test","sparrow")
                    .build();

    可以看到我们在创建JobDetail的时候,将要执行的job的类名传给了JobDetail,所以scheduler就知道了要执行何种类型的job。每次当scheduler执行job时,在调用其execute(…)方法之前会创建该类的一个新的实例;执行完毕,对该实例的引用就被丢弃了,实例会被垃圾回收;

    这种执行策略带来的一个后果是,job必须有一个无参的构造函数(当使用默认的JobFactory时);另一个后果是,在job类中,不应该定义有状态的数据属性,因为在job的多次执行中,这些属性的值不会保留,那这个时候JobDataMap就可以干活了,JobDataMap中可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用其中的数据。

    JobDataMap 除了像上面使用usingJobData 方式之外,还可以

     jobDetail.getJobDataMap().put("test","testOne");

    2.2 常用的两种Trigger

    Trigger 就是触发器的意思,用来指定什么时间开始触发,触发多少次,每隔多久触发一次。最常用SimpleTriggerCronTrigger这两种。

    2.2.1、SimpleTrigger

    Trigger trigger = newTrigger().withIdentity("myTrigger","group1")
                    .startNow()
                    .withSchedule(simpleSchedule()
                            .withIntervalInSeconds(5)
                            .withRepeatCount(10))
                    .build();

    SimpleTrigger的属性包括:开始时间、结束时间、重复次数以及重复的间隔;上面这段代码的意思就是从现在开始,每隔5s执行一次,一共执行10次。

    重复次数,可以是0、正整数,以及常量SimpleTrigger.REPEAT_INDEFINITELY。重复的间隔,必须是0,或者long型的正数,表示毫秒。注意,如果重复间隔为0,trigger将会以重复次数并发执行(或者以scheduler可以处理的近似并发数)。

    定义开始时间,比如下方代码:5s后开始     对应的结束使用endAt

     Date startTime = DateBuilder.futureDate(5, DateBuilder.IntervalUnit.SECOND);
            Trigger trigger1 = newTrigger().withIdentity("myTrigger","group1")
                    .startAt(startTime)
                    .build();

    2.2.2、CronTrigger

    使用CronTrigger最主要的就是Cron表达式,关于表达式后面会专门写一篇博文,这里直接放个例子了,每天上午10点42触发

    trigger = newTrigger()
        .withIdentity("trigger3", "group1")
        .withSchedule(dailyAtHourAndMinute(10, 42))
        .forJob("myJob", "group1")
        .build();

    或者

    trigger = newTrigger()
        .withIdentity("trigger3", "group1")
        .withSchedule(cronSchedule("0 42 10 * * ?"))
        .forJob("myJob", "group1")
        .build();

    3、Springboot整合Quartz

    以上只是简单地介绍了Quartz,好在整合过程中有个过度,更多内容可以查看Quartz相关资料

    pom.xml

        <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
                <version>2.3.0</version>
            </dependency>

     业务代码

    public class TestJob implements Job {
        @Override
        public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            JobDetail jobDetail = jobExecutionContext.getJobDetail();
            String test = jobDetail.getJobDataMap().getString("test");
            System.out.println(test+new Date());
        }
    }

    测试类:

     @Test
        public void testThree() throws Exception{
            //创建调度器
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            //定义一个触发器
            Trigger trigger = newTrigger().withIdentity("myTrigger","group1")
                    .startNow()
                    .withSchedule(simpleSchedule()
                            .withIntervalInSeconds(5)
                            .withRepeatCount(5))
                    .build();
            //定义一个JobDetail
            JobDetail jobDetail = newJob(TestJob.class)
                    .withIdentity("job1","group2")
                    .usingJobData("test","sparrow")
                    .build();
            //调度加入这个job
            scheduler.scheduleJob(jobDetail,trigger);
            //启动
            scheduler.start();
            //等待任务执行完再关闭
            Thread.sleep(20000);
            scheduler.shutdown(true);
        }

    执行结果:

     扩展:实际应用

    以上只是测试了一下是否可用,下面来个实际应用的例子:

    先创建一个任务类,具体的业务逻辑根据实际情况来,我这里是系统给我发送一个邮件

    public class DailyMailJob implements Job {
    
        Logger logger = LoggerFactory.getLogger(DailyMailJob.class);
    
        @Autowired
        IMailService mailService;
    
        @Override
        public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            String content = "花有重开之日,人无再少年,走好脚下的路,笔直看眼前";
            mailService.sendSimpleMail("XXXXXXXX@qq.com", Constant.DAILY_SUBJECT,content);
            logger.info("----------------邮件已发送----------------");
        }
    }

    再写个任务调度配置类    这里只是简单举个例子

    @Configuration
    public class SchedulerConfig {
    
        @Autowired
        Scheduler scheduler;
    
        @Bean
        public void startJob() throws SchedulerException {
            customJobOne(scheduler);
            //启动
            scheduler.start();
        }
    
       private void customJobOne(Scheduler scheduler) throws SchedulerException {
           //创建调度器
           //Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
           //定义一个触发器
           Trigger trigger = newTrigger().withIdentity("mailTriggerOne","mailGroup")
                   .startNow()
                   .withSchedule(cronSchedule("0 36 16 * * ?"))
                   .build();
           //定义一个JobDetail
           JobDetail jobDetail = newJob(DailyMailJob.class)
                   .withIdentity("mailJobOne","JobGroup")
                   .build();
           //调度加入这个job
           scheduler.scheduleJob(jobDetail,trigger);
       }
    }

     结果:

     

     

    展开全文
  • Springboot 整合 quartz

    2021-12-22 15:36:05
    1.下载quartz的sql文件 quartz下载链接 直接download,后解压找到 table_mysql.sql 数据库使用的是mysql数据库 2.将sql文件导入到需要链接的数据库,得到以下文件 3. 引入quartz的maven依赖 lombok我在代码...
  • 个人搭建的一个利用springbootquartz框架整合的例子,可以进行一些任务的调度。
  • springboot整合quartz实现分布式定时任务集群_sqlgao22的博客-CSDN博客_springboot 定时任务 字段描述 quartz中表及其表字段的意义_sqlgao22的博客-CSDN博客_qrtz_triggers表说明 相关表可以从官网下载 下载后解压 ...
  • springboot + quartz + postgres实现持久化分布式调度 集群环境任务调度测试 数据源使用spring默认数据源
  • 我们来介绍下springboot整合quartz定时器 1、导入依赖pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> &...
  • springboot整合quartz,实现: 1.引入maven依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </...
  • SpringBoot整合quartz2.2.3使用自定义数据源,包括mysql和oracle,使用quartz配合websocket服务器向客户端主动通讯发起通讯。
  • 文章目录一、SpringBoot整合Quartz1.1导入依赖1.2创建任务类1.3在启动类添加@EnableScheduling配置,然后直接启动测试二、SpringBoot日志2.1在resources目录下创建logback-spring.xml 一、SpringBoot整合Quartz 1.1...
  • springboot2.0整合quartz

    2018-12-27 17:14:04
    springboot2.0整合quartz,采用 spring-boot-starter-quartz方式,更为简单方式
  • springboot整合quartz定时任务(任务持久化到数据库)

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,643
精华内容 2,257
关键字:

springboot整合quartz

spring 订阅