所有分类
  • 所有分类
  • 未分类

SpringBoot整合Quartz–实例

简介

说明

本文用实例介绍quartz的用法。

quartz与spring自带的定时最大的区别之处:quartz可以用Calendar来控制哪些时间不执行此定时任务。

Job

  • 是一个接口,只定义一个方法 execute(JobExecutionContext context),在实现接口的execute 方法中编写所需要定时执行的 Job(任务);
  • Job接口是真正需要执行的任务,Quartz 每次调度 Job 时,都重新创建一个 Job 实例,因此它不接受多个 Job 的实例。
  • JobExecutionContext 类提供了调度应用的一些信息;
  • Job 运行时的信息保存在 JobDataMap 实例中。

JobDetail

JobDetail接口相当于将Job接口包装了一下,Trigger和Scheduler实际用到的都是JobDetail。

它接收一个 Job 实现类(JobDetail,描述 Job 的实现类及其他相关的静态信息,如 Job 名字、描述、关联监听器等信息),以便运行时通过 newInstance() 的反射机制实例化 Job。

Trigger接口

触发器,描述触发 Job 的时间触发规则,主要有 SimpleTrigger 和 CronTrigger 这两个实现类。系统时间走到触发器指定的时间的时候,触发器就会触发任务的执行。

Scheduler接口

  • 调度器,Quartz通过调度器来注册、暂停、删除Trigger和JobDetail。
  • Trigger 和 JobDetail 可以注册到 Scheduler 中,两者在 Scheduler 中拥有各自的组及名称。组及名称是 Scheduler 查找定位容器中某一对象的依据, Trigger 的组及名称必须唯一, JobDetail 的组和名称也必须唯一(但可以和 Trigger 的组和名称相同,因为它们是不同类型的)。
  • Scheduler 定义了多个接口方法,允许外部通过组及名称访问和控制容器中 Trigger 和JobDetail(是通过SchedulerContext获得的)。

Calendar接口

一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。例如:每周星期一早上10:00执行任务,但是如果碰到法定的节日,则不执行,这时就需要在Trigger触发机制的基础上使用Calendar进行定点排除。

Calendar有如下实现类:

基础实例

简介

有的博客里边说需要@EnableScheduling,经本人测试并不需要。

依赖

如果SpringBoot版本是2.0.0以后的,则在spring-boot-starter中已经包含了quart的依赖,则可以直接使用spring-boot-starter-quartz依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

如果是1.5.9则要使用以下添加依赖:

<dependency>
  <groupId>org.quartz-scheduler</groupId>
  <artifactId>quartz</artifactId>
  <version>2.3.0</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
</dependency>

 本处为了测试,还引入了swagger和web依赖。其相关依赖以及如何使能本处就不写了。

任务

任务1

package com.example.demo.schedule;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Job1 implements Job {
    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private void before() {
        System.out.println("任务1:开始执行-" + dateFormat.format(new Date()));
    }

    @Override
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        before();
        System.out.println("任务1:业务逻辑。。。");
        after();
    }

    private void after() {
        System.out.println("任务1:执行结束");
        System.out.println();
    }
}

任务2

package com.example.demo.schedule;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Job2 implements Job {
    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private void before() {
        System.out.println("任务2:开始执行-" + dateFormat.format(new Date()));
    }

    @Override
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        before();
        System.out.println("任务2:业务逻辑。。。");
        after();
    }

    private void after() {
        System.out.println("任务2:执行结束");
        System.out.println();
    }
}

工具类

package com.example.demo.util;

import com.example.demo.schedule.Job1;
import com.example.demo.schedule.Job2;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class QuartzSchedulerManager {
    @Autowired
    private Scheduler scheduler;

    // 开始执行定时器
    public void startJob() throws SchedulerException {
        startJob1(scheduler);
        startJob2(scheduler);
        scheduler.start();
    }

    // 获取Job信息
    public String getJobInfo(String name, String group) throws SchedulerException {
        TriggerKey triggerKey = new TriggerKey(name, group);
        CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        return String.format("time:%s,state:%s", cronTrigger.getCronExpression(),
                scheduler.getTriggerState(triggerKey).name());
    }

    // 修改某个任务的执行时间
    public boolean modifyJob(String name, String group, String time) throws SchedulerException {
        Date date = null;
        TriggerKey triggerKey = new TriggerKey(name, group);
        CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        String oldTime = cronTrigger.getCronExpression();
        if (!oldTime.equalsIgnoreCase(time)) {
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(time);
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name, group)
                    .withSchedule(cronScheduleBuilder).build();
            date = scheduler.rescheduleJob(triggerKey, trigger);
        }
        return date != null;
    }

    // 暂停所有任务
    public void pauseAllJob() throws SchedulerException {
        scheduler.pauseAll();
    }

    // 暂停某个任务
    public void pauseJob(String name, String group) throws SchedulerException {
        JobKey jobKey = new JobKey(name, group);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        if (jobDetail == null)
            return;
        scheduler.pauseJob(jobKey);
    }

    // 恢复所有任务
    public void resumeAllJob() throws SchedulerException {
        scheduler.resumeAll();
    }

    // 恢复某个任务
    public void resumeJob(String name, String group) throws SchedulerException {
        JobKey jobKey = new JobKey(name, group);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        if (jobDetail == null)
            return;
        scheduler.resumeJob(jobKey);
    }

    // 删除某个任务
    public void deleteJob(String name, String group) throws SchedulerException {
        JobKey jobKey = new JobKey(name, group);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        if (jobDetail == null)
            return;
        scheduler.deleteJob(jobKey);
    }

    // 启动任务1
    private void startJob1(Scheduler scheduler) throws SchedulerException {
        // 通过JobBuilder构建JobDetail实例,JobDetail规定其job只能是实现Job接口的实例
        JobDetail jobDetail = JobBuilder.newJob(Job1.class).withIdentity("job1", "group1").build();
        // 基于表达式构建触发器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
        // CronTrigger表达式触发器 继承于Trigger。TriggerBuilder 用于构建触发器实例
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("job1", "group1")
                .withSchedule(cronScheduleBuilder).build();
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }

    // 启动任务2
    private void startJob2(Scheduler scheduler) throws SchedulerException {
        // 通过JobBuilder构建JobDetail实例,JobDetail规定其job只能是实现Job接口的实例
        JobDetail jobDetail = JobBuilder.newJob(Job2.class).withIdentity("job2", "group2").build();
        // 基于表达式构建触发器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
        // CronTrigger表达式触发器 继承于Trigger。TriggerBuilder 用于构建触发器实例
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("job2", "group2")
                .withSchedule(cronScheduleBuilder).build();
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }
}

说明

TriggerBuilder.newTrigger().withIdentity()有三个重载:
    withIdentity(String name, String group)
    withIdentity(String name)  //内部调用new TriggerKey(name, null)
    withIdentity(TriggerKey triggerKey)

服务启动时运行

package com.example.demo.config;

import com.example.demo.util.QuartzSchedulerManager;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;

@Configuration
public class QuartzListener implements ApplicationListener<ContextRefreshedEvent> {
    @Autowired
    private QuartzSchedulerManager quartzSchedulerManager;

    // 初始启动quartz
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        try {
            quartzSchedulerManager.startJob();
            System.out.println("任务已经启动...");
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
    // 初始注入scheduler
    @Bean
    public Scheduler scheduler() throws SchedulerException{
        SchedulerFactory schedulerFactoryBean = new StdSchedulerFactory();
        return schedulerFactoryBean.getScheduler(); 
    }
}

控制器

注意:Quartz提供了一个验证cron是否合法的方法:CronExpression.isValidExpression(String cron);

package com.example.demo.controller;
import com.example.demo.util.QuartzSchedulerManager;
import org.quartz.CronExpression;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("")
public class QuartzController {
    @Autowired
    private QuartzSchedulerManager quartzSchedulerManager;

    // @Description: 获取定时器信息
    @GetMapping("/info")
    public String getQuartzJob(String name, String group) {
        String info = null;
        try {
            info = quartzSchedulerManager.getJobInfo(name, group);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return info;
    }

    // @Description: 修改定时器的 执行时间
    @PostMapping("/modify")
    public boolean modifyQuartzJob(String name, String group, String time) {
        boolean flag = true;

        if (!CronExpression.isValidExpression(time)) {
            throw new RuntimeException("非法的cron表达式");
        }
        
        try {
            flag = quartzSchedulerManager.modifyJob(name, group, time);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return flag;
    }

    // @Description: 启动所有定时器
    @PostMapping("/start")
    public void startQuartzJob() {
        try {
            quartzSchedulerManager.startJob();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    // @Description: 暂停指定 定时器
    @PostMapping(value = "/pause")
    public void pauseQuartzJob(String name, String group) {
        try {
            quartzSchedulerManager.pauseJob(name, group);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    // 暂停所有定时器
    @PostMapping(value = "/pauseAll")
    public void pauseAllQuartzJob() {
        try {
            quartzSchedulerManager.pauseAllJob();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    // 删除指定定时器
    @PostMapping(value = "/delete")
    public void deleteJob(String name, String group) {
        try {
            quartzSchedulerManager.deleteJob(name, group);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

启动服务

 下边的交替输出。

任务1:开始执行-2020-11-04 18:12:20
任务1:业务逻辑。。。
任务1:执行结束

任务2:开始执行-2020-11-04 18:12:20
任务2:业务逻辑。。。
任务2:执行结束

controller测试 

查看特定任务信息:http://localhost:8080/info?name=job1&group=group1

结果:

time:0/5 * * * * ?,state:NORMAL

controller其他测试都是通过的,本处不再展示。 

Calendar

简介

一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。例如:每周星期一早上10:00执行任务,但是如果碰到法定的节日,则不执行,这时就需要在Trigger触发机制的基础上使用Calendar进行定点排除。

Calendar有如下实现类:

Calendar 名称用法精度
BaseCalendarorg.quartz.impl.calendar.BaseCalendar为高级的 Calendar 实现了基本的功能,实现了 org.quartz.Calendar 接口
AnnualCalendarorg.quartz.impl.calendar.AnnualCalendar指定年中一天或多天。
CronCalendarorg.quartz.impl.calendar.CronCalendar    指定CronExpression表达的时间集合。     例:“* * 0-7,18-23?* *”排除每天所有营业时间(上午8点至下午5点)。     若CronTrigger具有给定的cron表达式并且与具有相同表达式的CronCalendar相关联,则日历将排除触发器包含的所有时间,并且它们将彼此抵消。
DailyCalendarorg.quartz.impl.calendar.DailyCalendar        指定每天营业时间(上午8点 – 5点)。 每个DailyCalendar仅允许指定单个时间范围,并且该时间范围可能不会跨越每日边界(即,您不能指定从上午8点至凌晨5点的时间范围)。 如果属性invertTimeRange为false(默认),则时间范围定义触发器不允许触发的时间范围。 如果invertTimeRange为true,则时间范围被反转 – 也就是排除在定义的时间范围之外的所有时间。毫秒
HolidayCalendarorg.quartz.impl.calendar.HolidayCalendar从 Trigger 中排除/包含节假日
MonthlyCalendarorg.quartz.impl.calendar.MonthlyCalendar排除/包含月份中的指定数天,例如,可用于排除每月的最后一天
WeeklyCalendarorg.quartz.impl.calendar.WeeklyCalendar排除/包含星期中的任意周几,例如,可用于排除周末,默认周六和周日

实例

说明:下方所有实例,前边都有

@Autowired
private Scheduler sched;

AnnualCalendar

This implementation of the Calendar excludes a set of days of the year. You may use it to exclude bank holidays which are on the same date every year
@Autowired
private Scheduler sched;

AnnualCalendar holidays = new AnnualCalendar();
//2014,7,21 实际是2014年8月21日
Calendar calendar = new GregorianCalendar(2014, 7, 21);
holidays.setDayExcluded(calendar, true);
sched.addCalendar("holidays", holidays, false, false);

SimpleTrigger trigger = newTrigger() 
  .withIdentity("trigger1", "group1")
   .startNow()
   .withSchedule(simpleSchedule()
   .withIntervalInHours(1)
   .repeatForever())  
   .modifiedByCalendar("holidays")
  .build();

上边日历设置成2014-07-21 并不是只有在2014年才生效,它会在每一年的这个日期都生效 。原因:看计算时间的源码,并没有计算设置的年份:

public boolean isDayExcluded(java.util.Calendar day) {
	...
	Iterator<java.util.Calendar> iter = excludeDays.iterator();
	while (iter.hasNext()) {
		java.util.Calendar cl = (java.util.Calendar) iter.next();
		// remember, the list is sorted
		if (dmonth < cl.get(java.util.Calendar.MONTH)) {
			return false;
		}
		if (dday != cl.get(java.util.Calendar.DAY_OF_MONTH)) {
			continue;
		}
		if (dmonth != cl.get(java.util.Calendar.MONTH)) {
			continue;
		}
		return true;
	}

	return false;
}

CronCalendar

This implementation of the Calendar may be used (you don't have to) as a base class for more sophisticated one's.

写一个表达式来排除一个时间范围,比如可以设置为排除所有的非工作时间: 在早8点-晚5点触发,其他时间暂停,代码如下

CronCalendar calendar = new CronCalendar("* * 0-7,18-23 ? * *");
sched.addCalendar("business", calendar, false, false);

DailyCalendar

 This implementation of the Calendar excludes (or includes - see below) a specified time range each day. For example, you could use this calendar to exclude business hours (8AM - 5PM) every day. Each DailyCalendar only allows a single time range to be specified, and that time range may not cross daily boundaries (i.e. you cannot specify a time range from 8PM - 5AM). If the property invertTimeRange is false (default), the time range defines a range of times in which triggers are not allowed to fire. If invertTimeRange is true, the time range is inverted – that is, all times outside the defined time range are excluded.
 
Note when using DailyCalendar, it behaves on the same principals as, for example, WeeklyCalendar. WeeklyCalendar defines a set of days that are excluded every week. Likewise, DailyCalendar defines a set of times that are excluded every day.

时间范围日历,定义一个时间范围,可以让触发器在这个时间范围内触发,或者在这个时间范围内不触发,每一个DailyCalendar的实例只能设置一次时间范围,并且这个时间范围不能超过一天的边界,比如你不能定义一个时间范围是(晚上8点至第二天早上5点),如果invertTimeRange这个属性等于false(默认),那么定义的时间范围内触发器不会触发,相反如果invertTimeRange=true 那么只有在这个时间范围内触发器才会触发,这个时间范围以外的时间都被排除。

Calendar s = Calendar.getInstance();
s.setTime(new Date());
        
Calendar e = Calendar.getInstance();
e.setTime(futureDate(10, IntervalUnit.SECOND));
        
DailyCalendar dailyCalendar = new DailyCalendar(s, e);
//DailyCalendar dailyCalendar = new DailyCalendar("20:57:00", "20:59:00");
dailyCalendar.setInvertTimeRange(true);
sched.addCalendar("dailyCalendar", dailyCalendar, false, false);
SimpleTrigger trigger = newTrigger() 
	.withIdentity("trigger1", "group1")
	.startNow()
	.withSchedule(simpleSchedule()
	.withIntervalInSeconds(3)
	.repeatForever())
	.modifiedByCalendar("dailyCalendar")
	.build();

HolidayCalendar

This implementation of the Calendar stores a list of holidays (full days that are excluded from scheduling).
 
The implementation DOES take the year into consideration, so if you want to exclude July 4th for the next 10 years, you need to add 10 entries to the exclude list.

该日历与AnnualCalendar一致,区别就是设置的year是有效的,也就是说如果你希望在未来的10年中 7月4日这天 这个日历生效,那么你需要添加10个日期,分别是 2014-7-4 ,2015-7-4…… 2024-7-4 这样才行。 

HolidayCalendar holidays = new HolidayCalendar();
Calendar calendar = new GregorianCalendar(2014, 7, 21);
holidays.addExcludedDate(calendar.getTime());
sched.addCalendar("holidays", holidays, false, false);

MonthlyCalendar

This implementation of the Calendar excludes a set of days of the month. You may use it to exclude every first day of each month for example. But you may define any day of a month.

月日历,你可以定义一个月当中的若干天,例如你可以设置每个月的第一天触发器不进行触发,当然你还可以定义一个月当中的任何一天。

下面例子给出每个月2,3,4号不触发的日历

MonthlyCalendar monthlyCalendar = new MonthlyCalendar();
monthlyCalendar.setDayExcluded(2, true);
monthlyCalendar.setDayExcluded(3, true);
monthlyCalendar.setDayExcluded(4, true);
sched.addCalendar("monthlyCalendar", monthlyCalendar, false, false);

WeeklyCalendar

This implementation of the Calendar excludes a set of days of the week. You may use it to exclude weekends for example. But you may define any day of the week. By default it excludes SATURDAY and SUNDAY.

星期日历,可以定义在一个星期当中的星期几几几 是不触发的日期,例如你可以定义么每个周末(星期天)触发器不触发,你也可以定义一周当中的任何一天或是几天。默认情况SATURDAY ,SUNDAY 这两天是没排除的。

下面的例子设置了每个星期四触发器不触发,并且默认情况周六和周天也是不触发的,这个是默认设置。如果需要周六周日也触发,那么把它清掉就可以了(weeklyCalendar.setDayExcluded(Calendar.SATURDAY , false)像这样)。一个需要注意的地方就是传入参数不能直接写数字星期几,因为老外的日子计算的与我们不一样,需要传入(java.util.Calendar)的常量字段,这样才准确。

WeeklyCalendar weeklyCalendar = new WeeklyCalendar();
weeklyCalendar.setDayExcluded(Calendar.THURSDAY, true);
sched.addCalendar("weeklyCalendar", weeklyCalendar, false, false);

组合日历的使用

上面的例子都是每一个触发器(trigger)关联一个日历的例子,我们在构建触发器的时候通过.modifiedByCalendar(“日历的key”)关联一个注册到引擎当中的日历,这种情况已经能够满足我们大部分的需求。但是系统的需求往往是复杂多变的,假设有这样一种情况,需要一个触发器在 每周一到周五,早8点-晚晚5点 每隔1小时执行,那么该如何使用日历呢?

其实我们不用日历,使用一个CronTrigger也是可以搞定的,我们这里只不过是抛砖引玉而已。那让我们来写一个组合日历使用的例子:

DailyCalendar dailyCalendar = new DailyCalendar("8:00:00", "17:00:00");
dailyCalendar.setInvertTimeRange(false);
        
WeeklyCalendar weeklyCalendar = new WeeklyCalendar(dailyCalendar);
sched.addCalendar("weeklyCalendar", weeklyCalendar, false, false);

我们写一个时间间隔的日历dailyCalendar,将其作为参数传递给weeklyCalendar就可以了,这样引擎在计算日历日期的时候会先判断dailyCalendar的时间范围,然后再判断weeklyCalendar是时间范围,当条件都满足的是否,触发器才会被触发,我们分析一下源码:

@Override
public boolean isTimeIncluded(long timeStamp) {
	if (excludeAll == true) {
		return false;
	}

	// Test the base calendar first. Only if the base calendar not already
	// excludes the time/date, continue evaluating this calendar instance.
	if (super.isTimeIncluded(timeStamp) == false) { return false; }

	java.util.Calendar cl = createJavaCalendar(timeStamp);
	int wday = cl.get(java.util.Calendar.DAY_OF_WEEK);

	return !(isDayExcluded(wday));
}

我们发现它首先调用 if (super.isTimeIncluded(timeStamp) == false) { return false; } 奥秘就在这里,我们继续看。

public boolean isTimeIncluded(long timeStamp) {

	if (timeStamp <= 0) {
		throw new IllegalArgumentException(
				"timeStamp must be greater 0");
	}

	if (baseCalendar != null) {
		if (baseCalendar.isTimeIncluded(timeStamp) == false) { return false; }
	}

	return true;
}

这里先判断了baseCalendar,这个对象就是在构造参数传递进去的dailyCalendar , 也就是它先试用dailyCalendar 进行日期计算,然后自己在计算,这样就完成了日历的组合使用。

往quartz的引擎中注册日历的方法

 addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers) 

这个方法有四个参数

1、calName 日历的名字,在构建触发器时通过modifiedByCalendar(“”)这里使用。
2、calendar 日历对象。
3、replace 当日历已经存在的情况下是否替换,true=替换, false=不替换 如果不替换还出现重复的情况会抛出异常。
4、updateTriggers 这个参数比较重要,它的意思是当一个已经存在与调度引擎中的触发器,并且已经引用了一个日历,比如:一个(触发器A)关联了一个日历,这个日历过滤每个星期日。

现在过了一段时间这个日历更新了(星期六也过滤),那么这个属性是用来指示触发器是否使用新的日历。不然的话(触发器A)仍然使用旧版本的日历,如果在有新添加到引擎中的触发器才会使用新日历。

持久化

简介

实现持久化有2种方式。
方式1:自己维护一张表,操作定时任务时同时修改这张表(推荐)。 
方式2:使用官方的建表语句(不推荐,因为它要创建11张表,这太多了)。

自己维护表

 表大概这样

drop table if exists t_quartz;

create table t_quartz
(
   id                   bigint(20) not null auto_increment comment '主键id',
   task_name            varchar(32) comment '任务名',
   cron_expression      varchar(32) comment 'cron表达式',
   param                varchar(32) comment '参数',
   descript             varchar(11) comment '描述',
   quartz_status        tinyint(255) comment '启动状态(0--启动1--停止)',
   status               tinyint(1) default 0 comment '状态(0--正常1--停用)',
   del_flag             tinyint(1) default 0 comment '删除状态(0,正常,1已删除)',
   create_time          datetime comment '创建时间',
   create_user_id       bigint(20) comment '创建人的id',
   primary key (id)
)type = InnoDB;

alter table t_quartz comment '定时任务信息表';

官方表

官方网址:Quartz建表的sql

对于mysql的建表语句:

-- In your Quartz properties file, you'll need to set
-- org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
-- By: Ron Cordell - roncordell
--  I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM.
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(190) NOT NULL,
JOB_GROUP VARCHAR(190) 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(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
JOB_NAME VARCHAR(190) NOT NULL,
JOB_GROUP VARCHAR(190) 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(190) 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(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) 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(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) 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(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) 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(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) 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(190) 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(190) 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(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
INSTANCE_NAME VARCHAR(190) 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(190) NULL,
JOB_GROUP VARCHAR(190) 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(190) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
ENGINE=InnoDB;

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

CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);

CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);

CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);

commit;

修改application.yml

spring:
  quartz:
    # 将任务等保存化到数据库
    job-store-type: jdbc  #默认是memory
    properties:
      org:
        quartz:
          jobStore:
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate

其他详细配置

spring:
  quartz:
    # 程序结束时会等待quartz相关的内容结束
    wait-for-jobs-to-complete-on-shutdown: true
    # QuartzScheduler启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录
    overwrite-existing-jobs: true
	jdbc:
	  # 每次启动重新创建数据库中Quartz相关的表。若自己事先创建,可不配置下边两项
      initialize-schema: always
	  schema: classpath:schema/tables_mysql.sql

quartz.properties

org.quartz.scheduler.instanceName = MyScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
 
 
# 线程池配置
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
 
 
# 持久化配置
org.quartz.jobStore.misfireThreshold = 50000
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

# 支持集群
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.useProperties:true
org.quartz.jobStore.clusterCheckinInterval = 15000
 
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = qzDS

#数据源连接信息,quartz默认使用c3p0数据源可以被自定义数据源覆盖
org.quartz.dataSource.qzDS.driver = 
org.quartz.dataSource.qzDS.URL = 
org.quartz.dataSource.qzDS.user = root
org.quartz.dataSource.qzDS.password = 123456
org.quartz.dataSource.qzDS.maxConnections = 10

注意:quartz.properties里边的配置,都可以放到application.yml里的spring. quartz.properties下边:

spring:
  quartz:
    properties:
      org:
        quartz:
        ..

原因:在QuartzAutoConfiguration类内,会自动调用SchedulerFactoryBean的setQuartzProperties方法,把spring.quartz.properties内的所有配置进行设置:

@Bean
@ConditionalOnMissingBean
public SchedulerFactoryBean quartzScheduler() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(new AutowireCapableBeanJobFactory(this.applicationContext.getAutowireCapableBeanFactory()));
        // 如果配置了spring.quartz.properties
        if (!this.properties.getProperties().isEmpty()) {
        //  将所有properties设置到QuartzProperties
            schedulerFactoryBean.setQuartzProperties(this.asProperties(this.properties.getProperties()));
        }
......省略部分代码
0

评论0

请先

显示验证码
没有账号?注册  忘记密码?

社交账号快速登录