SpringBoot+Quartz 定时任务调度管理

将定时任务持久化到数据库,方便启动,暂停,删除等操作。

 

pom:

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

配置:

SpringBoot+Quartz 定时任务调度管理
## quartz
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
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 =120000
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = mysqld
org.quartz.dataSource.mysqld.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.mysqld.URL = jdbc:mysql://rm-uf6u04q23mt4mx803yo.mysql.rds.aliyuncs.com/zy_store_db_test?characterEncoding=utf-8
org.quartz.dataSource.mysqld.user =zy
org.quartz.dataSource.mysqld.password =5Ay3vpXVJxdsm8gB
org.quartz.dataSource.mysqld.maxConnections = 5
org.quartz.dataSource.mysqld.validationQuery = select 0 from dual
View Code

 

Controller:

SpringBoot+Quartz 定时任务调度管理
package com.zy.quartz.controller;

import com.baomidou.mybatisplus.plugins.Page;
import com.zy.core.base.BaseOut;
import com.zy.core.base.BoxOut;
import com.zy.core.base.ZyBaseController;
import com.zy.core.model.BDic;
import com.zy.quartz.model.Job;
import com.zy.quartz.model.in.JobIn;
import com.zy.quartz.service.JobService;
import jodd.util.StringPool;
import lombok.extern.slf4j.Slf4j;
import org.redisson.executor.CronExpression;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @author xxx
 * @description 定时任务控制器
 * @date 2020/9/27
 */
@Slf4j
@RestController
@RequestMapping("/job/")
public class JobController extends ZyBaseController {


    @Autowired
    private JobService jobService;

    /**
     * @description:任务列表
     * @param jobIn
     * @return: com.zy.core.base.BaseOut
     * @author: xxx
     * @Date: 2020/9/27 15:53
     */
    @PostMapping("list")
    public BaseOut jobList(@RequestBody JobIn jobIn) {
        try{
            Page<Job> jobList = jobService.findJobs(jobIn);
            return setSuccessBaseOut(jobList);
        }catch (Exception e){
            log.error("查询定时任务失败",e.getMessage());
            return BoxOut.build(BDic.FAIL, "查询定时任务失败");
        }
    }

    /**
     * @description:校验CRON表达式是否正确
     * @param jobIn
     * @return: boolean
     * @author: xxx
     * @Date: 2020/9/27 16:18
     */
    @PostMapping("cron/check")
    public BaseOut checkCron(@RequestBody JobIn jobIn) {
        try {
            boolean bol = CronExpression.isValidExpression(jobIn.getCronExpression());
            if(bol){
                return BoxOut.build(BDic.SUCCESS,"CRON表达式正确");
            }else{
                return BoxOut.build(BDic.FAIL,"CRON表达式错误");
            }
        } catch (Exception e) {
            log.error("CRON表达式错误",e.getMessage());
            return BoxOut.build(BDic.FAIL, "CRON表达式错误");
        }
    }

    /**
     * @description:添加定时任务
     * @param job
     * @return: com.zy.core.base.BaseOut
     * @author: xxx
     * @Date: 2020/9/27 16:18
     */
    @PostMapping("add")
    public BaseOut addJob(@RequestBody  Job job) {
        try{
            jobService.createJob(job);
            return BoxOut.build(BDic.SUCCESS,"创建定时任务成功");
        }catch (Exception e){
            log.error("创建定时任务失败",e.getMessage());
            return BoxOut.build(BDic.FAIL, "创建定时任务失败");
        }
    }

    /**
     * @description:删除定时任务
     * @param jobIn
     * @return: com.zy.core.base.BaseOut
     * @author: xxx
     * @Date: 2020/9/27 16:18
     */
    @PostMapping("del")
    public BaseOut deleteJob(@RequestBody JobIn jobIn) {
        try{
            String[] ids = jobIn.getJobIds().split(StringPool.COMMA);
            jobService.deleteJobs(ids);
            return BoxOut.build(BDic.SUCCESS,"删除定时任务成功");
        }catch (Exception e){
            log.error("删除定时任务失败",e.getMessage());
            return BoxOut.build(BDic.FAIL, "删除定时任务失败");
        }
    }

    /**
     * @description:修改定时任务
     * @param jobIn
     * @return: com.zy.core.base.BaseOut
     * @author: xxx
     * @Date: 2020/9/27 16:18
     */
    @PostMapping("edit")
    public BaseOut updateJob(@RequestBody JobIn jobIn) {
        try{
            jobService.updateJob(jobIn);
            return BoxOut.build(BDic.SUCCESS,"修改定时任务成功");
        }catch (Exception e){
            log.error("修改定时任务失败",e.getMessage());
            return BoxOut.build(BDic.FAIL, "修改定时任务失败");
        }
    }

    /**
     * @description:执行定时任务
     * @param jobIn
     * @return: com.zy.core.base.BaseOut
     * @author: xxx
     * @Date: 2020/9/27 16:19
     */
    @PostMapping("run")
    public BaseOut runJob(@RequestBody JobIn jobIn) {
        try{
            jobService.run(jobIn.getJobIds());
            return BoxOut.build(BDic.SUCCESS,"执行定时任务成功");
        }catch (Exception e){
            log.error("执行定时任务失败",e.getMessage());
            return BoxOut.build(BDic.FAIL, "执行定时任务失败");
        }
    }

    /**
     * @description:暂停定时任务
     * @param jobIn
     * @return: com.zy.core.base.BaseOut
     * @author: xxx
     * @Date: 2020/9/27 16:19
     */
    @PostMapping("pause")
    public BaseOut pauseJob(@RequestBody JobIn jobIn) {
        try{
            jobService.pause(jobIn.getJobIds());
            return BoxOut.build(BDic.SUCCESS,"暂停定时任务成功");
        }catch (Exception e){
            log.error("暂停定时任务失败",e.getMessage());
            return BoxOut.build(BDic.FAIL, "暂停定时任务失败");
        }
    }

    /**
     * @description:恢复定时任务
     * @param jobIn
     * @return: com.zy.core.base.BaseOut
     * @author: xxx
     * @Date: 2020/9/27 16:19
     */
    @PostMapping("resume")
    public BaseOut resumeJob(@RequestBody JobIn jobIn) {
        try {
            this.jobService.resume(jobIn.getJobIds());
            return BoxOut.build(BDic.SUCCESS,"恢复定时任务成功");
        }catch (Exception e){
            log.error("恢复定时任务失败",e.getMessage());
            return BoxOut.build(BDic.FAIL, "恢复定时任务失败");
        }
    }

}








package com.zy.quartz.controller;

import com.baomidou.mybatisplus.plugins.Page;
import com.zy.core.base.BaseOut;
import com.zy.core.base.BoxOut;
import com.zy.core.base.ZyBaseController;
import com.zy.core.model.BDic;
import com.zy.quartz.model.Job;
import com.zy.quartz.model.JobLog;
import com.zy.quartz.model.in.JobIn;
import com.zy.quartz.model.in.JobLogIn;
import com.zy.quartz.service.JobLogService;
import com.zy.quartz.service.JobService;
import jodd.util.StringPool;
import lombok.extern.slf4j.Slf4j;
import org.redisson.executor.CronExpression;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author xxx
 * @description 定时任务控制器
 * @date 2020/9/27
 */
@Slf4j
@RestController
@RequestMapping("/job/log/")
public class JobLogController extends ZyBaseController {


    @Autowired
    private JobLogService jobLogService;

    /**
     * @description:任务列表
     * @param jobLogIn
     * @return: com.zy.core.base.BaseOut
     * @author: xxx
     * @Date: 2020/9/27 15:53
     */
    @PostMapping("list")
    public BaseOut jobList(@RequestBody JobLogIn jobLogIn) {
        try{
            Page<JobLog> jobList = jobLogService.findJobLogs(jobLogIn);
            return setSuccessBaseOut(jobList);
        }catch (Exception e){
            log.error("查询任务日志失败",e.getMessage());
            return BoxOut.build(BDic.FAIL, "查询任务日志失败");
        }
    }



    /**
     * @description:删除定时任务
     * @param logIds
     * @return: com.zy.core.base.BaseOut
     * @author: xxx
     * @Date: 2020/9/27 16:18
     */
    @PostMapping("del")
    public BaseOut deleteJob(@RequestBody String logIds) {
        try{
            String[] ids = logIds.split(StringPool.COMMA);
            jobLogService.deleteJobLogs(ids);
            return BoxOut.build(BDic.SUCCESS,"删除定时任务成功");
        }catch (Exception e){
            log.error("删除任务日志失败",e.getMessage());
            return BoxOut.build(BDic.FAIL, "删除任务日志失败");
        }
    }

}
View Code

 

Service:

SpringBoot+Quartz 定时任务调度管理
package com.zy.quartz.service;

import com.baomidou.mybatisplus.plugins.Page;
import com.zy.core.base.ZyBaseService;
import com.zy.quartz.mapper.JobMapper;
import com.zy.quartz.model.Job;
import com.zy.quartz.model.in.JobIn;
import com.zy.quartz.utils.ScheduleUtils;
import jodd.util.StringPool;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * @description:任务调度服务
 * @return:
 * @author: xxx
 * @Date: 2020/9/30 15:20
 */
@Slf4j
@Service("JobService")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class JobService extends ZyBaseService<Job, JobMapper> {

    @Autowired
    private Scheduler scheduler;

    @Autowired
    private JobMapper jobMapper;


    /**
     * @description:项目启动时,初始化定时任务
     * @return: void
     * @author: xxx
     * @Date: 2020/9/27 16:45
     */
    @PostConstruct
    public void init() {
//        List<Job> scheduleJobList = jobMapper.queryList(new JobIn());
//        // 如果不存在,则创建
//        scheduleJobList.forEach(scheduleJob -> {
//            CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getJobId());
//            if (cronTrigger == null) {
//                ScheduleUtils.createScheduleJob(scheduler, scheduleJob);
//            } else {
//                ScheduleUtils.updateScheduleJob(scheduler, scheduleJob);
//            }
//        });
    }

    /**
     * @description:根据ID获取任务
     * @param jobId
     * @return: com.zy.game.model.Job
     * @author: xxx
     * @Date: 2020/9/27 16:44
     */
    public Job findJob(Long jobId) {
        return jobMapper.getById(jobId);
    }

    /**
     * @description:获取定时任务
     * @param job
     * @return: com.baomidou.mybatisplus.plugins.Page<com.zy.game.model.Job>
     * @author: xxx
     * @Date: 2020/9/27 16:45
     */
    public Page<Job> findJobs(JobIn job) {
        Page<Job> page = getPageBeanBPI(job,Job.class);
        page.setOpenSort(false);
        List<Job> jobList = jobMapper.queryList(page,job);
        page.setRecords(jobList);
        return page;
    }

    /**
     * @description:创建定时任务
     * @param job
     * @return: void
     * @author: xxx
     * @Date: 2020/9/27 16:45
     */
    @Transactional
    public void createJob(Job job) {
        job.setCreateTime(new Date());
        job.setStatus(Job.ScheduleStatus.PAUSE.getValue());
        jobMapper.insert(job);
        ScheduleUtils.createScheduleJob(scheduler, job);
    }

    /**
     * @description:修改定时任务
     * @return: void
     * @author: xxx
     * @Date: 2020/9/27 16:46
     */
    @Transactional
    public void updateJob(JobIn jobIn) {
        Job job = new Job();
        BeanUtils.copyProperties(jobIn,job);
        ScheduleUtils.updateScheduleJob(scheduler, job);
        jobMapper.updateById(job);
    }

    /**
     * @description:删除定时任务
     * @param jobIds
     * @return: void
     * @author: xxx
     * @Date: 2020/9/27 16:46
     */
    @Transactional
    public void deleteJobs(String[] jobIds) {
        List<String> list = Arrays.asList(jobIds);
        list.forEach(jobId -> ScheduleUtils.deleteScheduleJob(scheduler, Long.valueOf(jobId)));
        jobMapper.deleteBatchIds(list);
    }

    /**
     * @description:暂停定时任务
     * @param jobIds
     * @param status
     * @return: int
     * @author: xxx
     * @Date: 2020/9/27 16:46
     */
    @Transactional
    public void updateBatch(String jobIds, String status) {
        List<String> list = Arrays.asList(jobIds.split(StringPool.COMMA));
       list.forEach(s -> {
           Job job = new Job();
           job.setStatus(status);
           job.setJobId(Long.valueOf(s));
           jobMapper.updateById(job);
       });
    }

    /**
     * @description:执行定时任务
     * @param jobIds
     * @return: void
     * @author: xxx
     * @Date: 2020/9/27 16:46
     */
    @Transactional
    public void run(String jobIds) {
        String[] list = jobIds.split(StringPool.COMMA);
        Arrays.stream(list).forEach(jobId -> ScheduleUtils.run(scheduler, this.findJob(Long.valueOf(jobId))));
    }

    /**
     * @description: 暂停定时任务
     * @param jobIds
     * @return: void
     * @author: xxx
     * @Date: 2020/9/27 16:46
     */
    @Transactional
    public void pause(String jobIds) {
        String[] list = jobIds.split(StringPool.COMMA);
        Arrays.stream(list).forEach(jobId -> ScheduleUtils.pauseJob(scheduler, Long.valueOf(jobId)));
        this.updateBatch(jobIds, Job.ScheduleStatus.PAUSE.getValue());
    }

    /**
     * @description:恢复定时任务
     * @param jobIds
     * @return: void
     * @author: xxx
     * @Date: 2020/9/27 16:47
     */
    @Transactional
    public void resume(String jobIds) {
        String[] list = jobIds.split(StringPool.COMMA);
        Arrays.stream(list).forEach(jobId -> ScheduleUtils.resumeJob(scheduler, Long.valueOf(jobId)));
        this.updateBatch(jobIds, Job.ScheduleStatus.NORMAL.getValue());
    }
}








package com.zy.quartz.service;

import com.baomidou.mybatisplus.plugins.Page;
import com.zy.core.base.ZyBaseService;
import com.zy.quartz.mapper.JobLogMapper;
import com.zy.quartz.model.JobLog;
import com.zy.quartz.model.in.JobLogIn;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;

/**
 * @description:任务日志服务
 * @return:
 * @author: xxx
 * @Date: 2020/9/30 15:20
 */
@Slf4j
@Service("JobLogService")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class JobLogService extends ZyBaseService<JobLog, JobLogMapper> {

    @Autowired
    private JobLogMapper jobLogMapper;

    public Page<JobLog> findJobLogs(JobLogIn jobLogIn) {
        Page<JobLog> page = getPageBeanBPI(jobLogIn,JobLog.class);
        page.setOpenSort(false);
        List<JobLog> jobList = jobLogMapper.queryList(page,jobLogIn);
        page.setRecords(jobList);
        return page;
    }

    @Transactional
    public void saveJobLog(JobLog log) {
        jobLogMapper.insert(log);
    }

    @Transactional
    public void deleteJobLogs(String[] jobLogIds) {
        List<String> list = Arrays.asList(jobLogIds);
        jobLogMapper.deleteBatchIds(list);
    }

}
View Code

 

Mapper:

SpringBoot+Quartz 定时任务调度管理
package com.zy.quartz.mapper;


import com.zy.core.base.ZyBaseMapper;
import com.zy.quartz.model.Job;
import com.zy.quartz.model.in.JobIn;
import com.zy.quartz.model.out.JobOut;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author MrBird
 */
@Component
public interface JobMapper extends ZyBaseMapper<Job> {
    
    List<JobOut> queryList(RowBounds rowBounds, JobIn jobIn);

    Job getById(Long jobId);
}






package com.zy.quartz.mapper;


import com.zy.core.base.ZyBaseMapper;
import com.zy.quartz.model.JobLog;
import com.zy.quartz.model.in.JobLogIn;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author MrBird
 */
@Component
public interface JobLogMapper extends ZyBaseMapper<JobLog> {

    List<JobLog> queryList(RowBounds rowBounds, JobLogIn jobLogIn);
}
View Code SpringBoot+Quartz 定时任务调度管理
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zy.quartz.mapper.JobMapper">

    <select id="queryList" resultType="com.zy.quartz.model.out.JobOut" parameterType="com.zy.quartz.model.in.JobIn">
        select
        t.job_id as jobId,
        t.bean_name as beanName,
        t.method_name as methodName,
        t.params,
        t.cron_expression as cronExpression,
        t.status,
        t.remark,
        t.create_time as createTime
        from zy_job as t
        where t.enable = 'Y'
        <if test="beanName!=null and beanName !=''">
            and t.bean_name like concat('%', #{beanName}, '%')
        </if>
        <if test="methodName!=null and methodName !=''">
            and t.method_name like concat('%', #{methodName}, '%')
        </if>
        <if test="cronExpression!=null and cronExpression !=''">
            and t.cron_expression = #{cronExpression}
        </if>
        <if test="status!=null and status !=''">
            and t.status = #{status}
        </if>
        <if test="remark!=null and remark !=''">
            and t.remark like concat('%', #{remark}, '%')
        </if>
        <if test="beginTime != null and beginTime != ''">
            and DATE_FORMAT(t.create_time,'%Y-%m-%d') <![CDATA[ >= ]]> #{beginTime}
        </if>
        <if test="endTime != null and endTime != ''">
            and DATE_FORMAT(t.create_time,'%Y-%m-%d') <![CDATA[ <= ]]> #{endTime}
        </if>
        order by t.create_time desc,t.job_id asc
        </select>

    <select id="getById" resultType="com.zy.quartz.model.Job" parameterType="java.lang.Long">
            select job_id as jobId,
                   bean_name as beanName,
                   method_name as  methodName,
                   params,
                   cron_expression as cronExpression,
                   status,
                   remark,
                   create_time as createTime
            from zy_job
            where enable = 'Y' and job_id = #{jobId}
        </select>

</mapper>





<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zy.quartz.mapper.JobLogMapper">

    <select id="queryList" resultType="com.zy.quartz.model.JobLog" parameterType="com.zy.quartz.model.in.JobLogIn">
        select
            t.log_id as logId,
            t.job_id as jobId,
            t.bean_name as beanName,
            t.method_name as methodName,
            t.params,
            t.status,
            t.error,
            t.times,
            t.create_time as createTime
        from zy_job_log t
        where t.log_id = t.log_id
        <if test="jobId!=null">
            and t.job_id = #{jobId}
        </if>
        <if test="beanName!=null and beanName !=''">
            and t.bean_name like concat('%', #{beanName}, '%')
        </if>
        <if test="methodName!=null and methodName !=''">
            and t.method_name like concat('%', #{methodName}, '%')
        </if>
        <if test="error!=null and error !=''">
            and t.error like concat('%', #{error}, '%')
        </if>
        <if test="status!=null and status !=''">
            and t.status = #{status}
        </if>
        <if test="beginTime != null and beginTime != ''">
            and DATE_FORMAT(t.create_time,'%Y-%m-%d') <![CDATA[ >= ]]> #{beginTime}
        </if>
        <if test="endTime != null and endTime != ''">
            and DATE_FORMAT(t.create_time,'%Y-%m-%d') <![CDATA[ <= ]]> #{endTime}
        </if>
        order by t.create_time desc

    </select>

</mapper>
View Code

 

Model:

SpringBoot+Quartz 定时任务调度管理
package com.zy.quartz.model.in;

import com.zy.core.base.BasePageIn;
import com.zy.quartz.model.Job;
import lombok.Data;

import java.util.Date;

/**
 * @author zxq
 * @description 定时任务入参实体
 * @date 2020/9/27
 */
@Data
public class JobIn extends BasePageIn {

    //定时任务ID字符串,逗号间隔
    private String jobIds;
    //定时任务ID
    private Long jobId;
    //spring bean名
    private String beanName;
    //方法名
    private String methodName;
    //参数
    private String params;
    //cron表达式
    private String cronExpression;
    //状态;0=正常,1=暂停
    private String status;
    //备注
    private String remark;
    //是否可用 默认为Y (Y:是,N:否)
    private String enable;
    //创建人
    private String createBy;
    //开始时间
    private transient String beginTime;
    //结束时间
    private transient String endTime;

}


package com.zy.quartz.model.in;

import com.zy.core.base.BasePageIn;
import lombok.Data;

/**
 * @author zxq
 * @description 任务日志
 * @date 2020/9/30
 */
@Data
public class JobLogIn extends BasePageIn {

    //日志ID字符串,逗号间隔
    private String logIds;
    //任务ID
    private Long jobId;
    //spring bean名称
    private String beanName;
    //方法名
    private String methodName;
    //参数
    private String params;
    //失败信息
    private String error;
    //状态;0=正常,1=暂停
    private String status;
    //耗时(单位:毫秒)
    private String times;
    //开始时间
    private transient String beginTime;
    //结束时间
    private transient String endTime;
}



package com.zy.quartz.model.out;


import com.zy.quartz.model.Job;
import lombok.Data;

import java.util.Date;

/**
 * @author zxq
 * @description 定时任务出参实体
 * @date 2020/9/27
 */
@Data
public class JobOut {


    private String jobIds;

    private Long jobId;

    private String beanName;

    private String methodName;

    private String params;

    private String cronExpression;

    private String status;

    private String remark;

    private Date createTime;

    private String enable;

    private String createBy;

}




package com.zy.quartz.model;

import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;
import lombok.Data;

import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.Date;

/**
 * @description:定时任务信息表
 * @return:
 * @author: zxq
 * @Date: 2020/9/27 10:49
 */
@Data
@TableName("zy_job")
public class Job implements Serializable {

    private static final long serialVersionUID = 400066840871805700L;

    /**
     * 任务调度参数 key
     */
    public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";

    public enum ScheduleStatus {
        /**
         * 正常
         */
        NORMAL("0"),
        /**
         * 暂停
         */
        PAUSE("1");

        private String value;

        ScheduleStatus(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }
    }

    @TableId(value = "job_id", type = IdType.AUTO)
    private Long jobId;

    @TableField("bean_name")
    private String beanName;

    @TableField("method_name")
    private String methodName;

    @TableField("params")
    private String params;

    @TableField("cron_expression")
    private String cronExpression;

    @TableField("status")
    private String status;

    @TableField("remark")
    private String remark;

    @TableField("create_time")
    private Date createTime;

    /**
     * 是否可用 默认为Y (Y:是,N:否)
     */
    @TableField("enable")
    private String enable;

    /**
     *
     */
    @TableField("create_by")
    private String createBy;


}





package com.zy.quartz.model;


import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;
import com.zy.core.base.BasePageIn;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * @description:调度日志信息表
 * @return:
 * @author: zxq
 * @Date: 2020/9/27 10:49
 */
@Data
@TableName("zy_job_log")
public class JobLog implements Serializable {

    private static final long serialVersionUID = -7114915445674333148L;
    // 任务执行成功
    public static final String JOB_SUCCESS = "0";
    // 任务执行失败
    public static final String JOB_FAIL = "1";

    @TableId(value = "log_id", type = IdType.AUTO)
    private Long logId;

    @TableField("job_id")
    private Long jobId;

    @TableField("bean_name")
    private String beanName;

    @TableField("method_name")
    private String methodName;

    @TableField("params")
    private String params;

    @TableField("status")
    private String status;

    @TableField("error")
    private String error;

    @TableField("times")
    private Long times;

    @TableField("create_time")
    private Date createTime;


}
View Code

 

Utils:

SpringBoot+Quartz 定时任务调度管理
package com.zy.quartz.utils;


import com.zy.quartz.model.Job;
import com.zy.quartz.model.JobLog;
import com.zy.quartz.service.JobLogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.JobExecutionContext;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 定时任务
 *
 * @author MrBird
 */
@Slf4j
public class ScheduleJob extends QuartzJobBean {

    private ExecutorService service = Executors.newSingleThreadExecutor();

    @Override
    protected void executeInternal(JobExecutionContext context) {
        Job scheduleJob = (Job) context.getMergedJobDataMap().get(Job.JOB_PARAM_KEY);

        // 获取spring bean
        JobLogService scheduleJobLogService = SpringContextUtil.getBean(JobLogService.class);

        JobLog jobLog = new JobLog();
        jobLog.setJobId(scheduleJob.getJobId());
        jobLog.setBeanName(scheduleJob.getBeanName());
        jobLog.setMethodName(scheduleJob.getMethodName());
        jobLog.setParams(scheduleJob.getParams());
        jobLog.setCreateTime(new Date());

        long startTime = System.currentTimeMillis();

        try {
            // 执行任务
            log.info("任务准备执行,任务ID:{}", scheduleJob.getJobId());
            ScheduleRunnable task = new ScheduleRunnable(scheduleJob.getBeanName(), scheduleJob.getMethodName(),
                    scheduleJob.getParams());
            Future<?> future = service.submit(task);
            future.get();
            long times = System.currentTimeMillis() - startTime;
            jobLog.setTimes(times);
            // 任务状态 0:成功 1:失败
            jobLog.setStatus(JobLog.JOB_SUCCESS);

            log.info("任务执行完毕,任务ID:{} 总共耗时:{} 毫秒", scheduleJob.getJobId(), times);
        } catch (Exception e) {
            log.error("任务执行失败,任务ID:" + scheduleJob.getJobId(), e);
            long times = System.currentTimeMillis() - startTime;
            jobLog.setTimes(times);
            // 任务状态 0:成功 1:失败
            jobLog.setStatus(JobLog.JOB_FAIL);
            jobLog.setError(StringUtils.substring(e.toString(), 0, 2000));
        } finally {
            scheduleJobLogService.saveJobLog(jobLog);
        }
    }
}






package com.zy.quartz.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;

/**
 * 执行定时任务
 *
 * @author MrBird
 */
@Slf4j
public class ScheduleRunnable implements Runnable {

    private Object target;
    private Method method;
    private String params;

    ScheduleRunnable(String beanName, String methodName, String params) throws NoSuchMethodException, SecurityException {
        this.target = SpringContextUtil.getBean(beanName);
        this.params = params;

        if (StringUtils.isNotBlank(params)) {
            this.method = target.getClass().getDeclaredMethod(methodName, String.class);
        } else {
            this.method = target.getClass().getDeclaredMethod(methodName);
        }
    }

    @Override
    public void run() {
        try {
            ReflectionUtils.makeAccessible(method);
            if (StringUtils.isNotBlank(params)) {
                method.invoke(target, params);
            } else {
                method.invoke(target);
            }
        } catch (Exception e) {
            log.error("执行定时任务失败", e);
        }
    }

}









package com.zy.quartz.utils;

import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import com.zy.quartz.model.Job;

/**
 * 定时任务工具类
 *
 * @author MrBird
 */
@Slf4j
public class ScheduleUtils {

    protected ScheduleUtils() {

    }

    private static final String JOB_NAME_PREFIX = "TASK_";

    /**
     * 获取触发器 key
     */
    private static TriggerKey getTriggerKey(Long jobId) {
        return TriggerKey.triggerKey(JOB_NAME_PREFIX + jobId);
    }

    /**
     * 获取jobKey
     */
    private static JobKey getJobKey(Long jobId) {
        return JobKey.jobKey(JOB_NAME_PREFIX + jobId);
    }

    /**
     * 获取表达式触发器
     */
    public static CronTrigger getCronTrigger(Scheduler scheduler, Long jobId) {
        try {
            return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
        } catch (SchedulerException e) {
            log.error("获取Cron表达式失败", e);
        }
        return null;
    }

    /**
     * 创建定时任务
     */
    public static void createScheduleJob(Scheduler scheduler, Job scheduleJob) {
        try {
            // 构建job信息
            JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getJobId()))
                    .build();

            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();

            // 按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getJobId()))
                    .withSchedule(scheduleBuilder).build();

            // 放入参数,运行时的方法可以获取
            jobDetail.getJobDataMap().put(Job.JOB_PARAM_KEY, scheduleJob);

            scheduler.scheduleJob(jobDetail, trigger);

            // 暂停任务
            if (scheduleJob.getStatus().equals(Job.ScheduleStatus.PAUSE.getValue())) {
                pauseJob(scheduler, scheduleJob.getJobId());
            }
        } catch (SchedulerException e) {
            log.error("创建定时任务失败", e);
        }
    }

    /**
     * 更新定时任务
     */
    public static void updateScheduleJob(Scheduler scheduler, Job scheduleJob) {
        try {
            TriggerKey triggerKey = getTriggerKey(scheduleJob.getJobId());

            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();

            CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getJobId());

            if (trigger == null) {
                throw new SchedulerException("获取Cron表达式失败");
            } else {
                // 按新的 cronExpression表达式重新构建 trigger
                trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
                // 参数
                trigger.getJobDataMap().put(Job.JOB_PARAM_KEY, scheduleJob);
            }

            scheduler.rescheduleJob(triggerKey, trigger);

            // 暂停任务
            if (scheduleJob.getStatus().equals(Job.ScheduleStatus.PAUSE.getValue())) {
                pauseJob(scheduler, scheduleJob.getJobId());
            }

        } catch (SchedulerException e) {
            log.error("更新定时任务失败", e);
        }
    }

    /**
     * 立即执行任务
     */
    public static void run(Scheduler scheduler, Job scheduleJob) {
        try {
            // 参数
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(Job.JOB_PARAM_KEY, scheduleJob);

            scheduler.triggerJob(getJobKey(scheduleJob.getJobId()), dataMap);
        } catch (SchedulerException e) {
            log.error("执行定时任务失败", e);
        }
    }

    /**
     * 暂停任务
     */
    public static void pauseJob(Scheduler scheduler, Long jobId) {
        try {
            scheduler.pauseJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            log.error("暂停定时任务失败", e);
        }
    }

    /**
     * 恢复任务
     */
    public static void resumeJob(Scheduler scheduler, Long jobId) {
        try {
            scheduler.resumeJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            log.error("恢复定时任务失败", e);
        }
    }

    /**
     * 删除定时任务
     */
    public static void deleteScheduleJob(Scheduler scheduler, Long jobId) {
        try {
            scheduler.deleteJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            log.error("删除定时任务失败", e);
        }
    }
}







package com.zy.quartz.utils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @description:Spring Context 工具类
 * @return:
 * @author: zxq
 * @Date: 2020/9/27 11:07
 */
@Component
public class SpringContextUtil implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtil.applicationContext = applicationContext;
    }

    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }
    public static <T> T getBean(Class<T> clazz){
        return applicationContext.getBean(clazz);
    }

    public static <T> T getBean(String name, Class<T> requiredType) {
        return applicationContext.getBean(name, requiredType);
    }

    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }

    public static Class<?> getType(String name) {
        return applicationContext.getType(name);
    }

}
View Code

 

SQL:

SpringBoot+Quartz 定时任务调度管理
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;


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

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

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

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

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

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

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

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

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

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

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

SET FOREIGN_KEY_CHECKS=0;
View Code

 

效果图:

 

SpringBoot+Quartz 定时任务调度管理

 

上一篇:Quartz定时任务


下一篇:二分法及其应用