依赖:
<quartz.version>2.3.0</quartz.version><dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>${quartz.version}</version><exclusions><exclusion><groupId>com.mchange</groupId><artifactId>c3p0</artifactId></exclusion><exclusion><groupId>com.zaxxer</groupId><artifactId>HikariCP-java6</artifactId></exclusion></exclusions></dependency>
定时任务配置ScheduleConfig
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;import javax.sql.DataSource;
import java.util.Properties;/*** 定时任务配置** @author Mark sunlightcs@gmail.com*/
@Configuration
public class ScheduleConfig {@Beanpublic SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {SchedulerFactoryBean factory = new SchedulerFactoryBean();factory.setDataSource(dataSource);//quartz参数Properties prop = new Properties();prop.put("org.quartz.scheduler.instanceName", "GlobalScheduler");prop.put("org.quartz.scheduler.instanceId", "AUTO");//线程池配置prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");prop.put("org.quartz.threadPool.threadCount", "20");prop.put("org.quartz.threadPool.threadPriority", "5");prop.put("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread", "true");//JobStore配置prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore");//集群配置prop.put("org.quartz.jobStore.isClustered", "false");prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");prop.put("org.quartz.jobStore.misfireThreshold", "12000");prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");//prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");//PostgreSQL数据库,需要打开此注释//prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");factory.setQuartzProperties(prop);factory.setSchedulerName("LeaseScheduler");//延时启动factory.setStartupDelay(30);factory.setApplicationContextSchedulerContextKey("applicationContextKey");//可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了//factory.setOverwriteExistingJobs(false);//设置自动启动,默认为truefactory.setAutoStartup(true);factory.setWaitForJobsToCompleteOnShutdown(true);return factory;}
}
定时任务管理相关表结构:
CREATE TABLE `schedule_job` (`id` bigint NOT NULL COMMENT 'id',`bean_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'spring bean名称',`params` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '参数',`cron_expression` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'cron表达式',`status` tinyint unsigned DEFAULT NULL COMMENT '任务状态 0:暂停 1:正常',`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注',`creator` bigint DEFAULT NULL COMMENT '创建者',`create_date` datetime DEFAULT NULL COMMENT '创建时间',`updater` bigint DEFAULT NULL COMMENT '更新者',`update_date` datetime DEFAULT NULL COMMENT '更新时间',PRIMARY KEY (`id`) USING BTREE,KEY `idx_create_date` (`create_date`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='定时任务';CREATE TABLE `schedule_job_log` (`id` bigint NOT NULL COMMENT 'id',`job_id` bigint NOT NULL COMMENT '任务id',`bean_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'spring bean名称',`params` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '参数',`status` tinyint unsigned NOT NULL COMMENT '任务状态 0:失败 1:成功',`error` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '失败信息',`times` int NOT NULL COMMENT '耗时(单位:毫秒)',`create_date` datetime DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE,KEY `idx_job_id` (`job_id`) USING BTREE,KEY `idx_create_date` (`create_date`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='定时任务日志';
基础实体类
package io.global.iot.common.entity;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;import java.io.Serializable;
import java.util.Date;/*** 基础实体类,所有实体都需要继承*/
@Data
public abstract class SysBaseEntity implements Serializable {/*** id*/@TableIdprivate Long id;/*** 创建者*/@TableField(fill = FieldFill.INSERT)private Long creator;/*** 创建时间*/@TableField(fill = FieldFill.INSERT)private Date createDate;
}
Entity、DTO、Dao、Dao映射文件、Service、Service实现
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.entity;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.global.iot.common.entity.SysBaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;import java.util.Date;/*** 定时任务** @author Mark sunlightcs@gmail.com*/
@Data
@EqualsAndHashCode(callSuper=false)
@TableName("schedule_job")
public class ScheduleJobEntity extends SysBaseEntity {private static final long serialVersionUID = 1L;/*** spring bean名称*/private String beanName;/*** 参数*/private String params;/*** cron表达式*/private String cronExpression;/*** 任务状态 0:暂停 1:正常*/private Integer status;/*** 备注*/private String remark;/*** 更新者*/@TableField(fill = FieldFill.INSERT_UPDATE)private Long updater;/*** 更新时间*/@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateDate;
}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.entity;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.io.Serializable;
import java.util.Date;/*** 定时任务日志** @author Mark sunlightcs@gmail.com*/
@Data
@TableName("schedule_job_log")
public class ScheduleJobLogEntity implements Serializable {private static final long serialVersionUID = 1L;/*** id*/@TableIdprivate Long id;/*** 任务id*/private Long jobId;/*** spring bean名称*/private String beanName;/*** 参数*/private String params;/*** 任务状态 0:失败 1:成功*/private Integer status;/*** 失败信息*/private String error;/*** 耗时(单位:毫秒)*/private Integer times;/*** 创建时间*/private Date createDate;}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.dto;import com.fasterxml.jackson.annotation.JsonProperty;
import io.global.iot.common.validator.group.AddGroup;
import io.global.iot.common.validator.group.DefaultGroup;
import io.global.iot.common.validator.group.UpdateGroup;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
import java.io.Serializable;
import java.util.Date;/*** 定时任务** @author Mark sunlightcs@gmail.com* @since 1.0.0*/
@Data
@Schema(description = "定时任务")
public class ScheduleJobDTO implements Serializable {private static final long serialVersionUID = 1L;@Schema(description = "id")@Null(message="{id.null}", groups = AddGroup.class)@NotNull(message="{id.require}", groups = UpdateGroup.class)private Long id;@Schema(description = "spring bean名称")@NotBlank(message = "{schedule.bean.require}", groups = DefaultGroup.class)private String beanName;@Schema(description = "参数")private String params;@Schema(description = "cron表达式")@NotBlank(message = "{schedule.cron.require}", groups = DefaultGroup.class)private String cronExpression;@Schema(description = "任务状态 0:暂停 1:正常")@Range(min=0, max=1, message = "{schedule.status.range}", groups = DefaultGroup.class)private Integer status;@Schema(description = "备注")private String remark;@Schema(description = "创建时间")@JsonProperty(access = JsonProperty.Access.READ_ONLY)private Date createDate;}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;import java.io.Serializable;
import java.util.Date;/*** 定时任务日志** @author Mark sunlightcs@gmail.com* @since 1.0.0*/
@Data
@Schema(description = "定时任务日志")
public class ScheduleJobLogDTO implements Serializable {private static final long serialVersionUID = 1L;@Schema(description = "id")private Long id;@Schema(description = "任务id")private Long jobId;@Schema(description = "spring bean名称")private String beanName;@Schema(description = "参数")private String params;@Schema(description = "任务状态 0:失败 1:成功")private Integer status;@Schema(description = "失败信息")private String error;@Schema(description = "耗时(单位:毫秒)")private Integer times;@Schema(description = "创建时间")private Date createDate;}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.dao;import io.global.iot.common.dao.BaseDao;
import io.global.iot.modules.job.entity.ScheduleJobEntity;
import org.apache.ibatis.annotations.Mapper;import java.util.Map;/*** 定时任务** @author Mark sunlightcs@gmail.com*/
@Mapper
public interface ScheduleJobDao extends BaseDao<ScheduleJobEntity> {/*** 批量更新状态*/int updateBatch(Map<String, Object> map);
}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.dao;
import io.global.iot.common.dao.BaseDao;
import io.global.iot.modules.job.entity.ScheduleJobLogEntity;
import org.apache.ibatis.annotations.Mapper;/*** 定时任务日志** @author Mark sunlightcs@gmail.com*/
@Mapper
public interface ScheduleJobLogDao extends BaseDao<ScheduleJobLogEntity> {}
<?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="io.global.iot.modules.job.dao.ScheduleJobDao"><!-- 批量更新状态 --><update id="updateBatch">update schedule_job set status = #{status} where id in<foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach></update></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="io.global.iot.modules.job.dao.ScheduleJobLogDao"></mapper>
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.service;import io.global.iot.common.page.PageData;
import io.global.iot.common.service.BaseService;
import io.global.iot.modules.job.dto.ScheduleJobDTO;
import io.global.iot.modules.job.entity.ScheduleJobEntity;import java.util.Map;/*** 定时任务** @author Mark sunlightcs@gmail.com*/
public interface ScheduleJobService extends BaseService<ScheduleJobEntity> {PageData<ScheduleJobDTO> page(Map<String, Object> params);ScheduleJobDTO get(Long id);/*** 保存定时任务*/void save(ScheduleJobDTO dto);/*** 更新定时任务*/void update(ScheduleJobDTO dto);/*** 批量删除定时任务*/void deleteBatch(Long[] ids);/*** 批量更新定时任务状态*/int updateBatch(Long[] ids, int status);/*** 立即执行*/void run(Long[] ids);/*** 暂停运行*/void pause(Long[] ids);/*** 恢复运行*/void resume(Long[] ids);
}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.service;import io.global.iot.common.page.PageData;
import io.global.iot.common.service.BaseService;
import io.global.iot.modules.job.dto.ScheduleJobLogDTO;
import io.global.iot.modules.job.entity.ScheduleJobLogEntity;import java.util.Map;/*** 定时任务日志** @author Mark sunlightcs@gmail.com*/
public interface ScheduleJobLogService extends BaseService<ScheduleJobLogEntity> {PageData<ScheduleJobLogDTO> page(Map<String, Object> params);ScheduleJobLogDTO get(Long id);
}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.global.iot.common.constant.Constant;
import io.global.iot.common.page.PageData;
import io.global.iot.common.service.impl.BaseServiceImpl;
import io.global.iot.common.utils.ConvertUtils;
import io.global.iot.modules.job.dao.ScheduleJobDao;
import io.global.iot.modules.job.dto.ScheduleJobDTO;
import io.global.iot.modules.job.entity.ScheduleJobEntity;
import io.global.iot.modules.job.service.ScheduleJobService;
import io.global.iot.modules.job.utils.ScheduleUtils;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.quartz.Scheduler;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;@Service
public class ScheduleJobServiceImpl extends BaseServiceImpl<ScheduleJobDao, ScheduleJobEntity> implements ScheduleJobService {@Resourceprivate Scheduler scheduler;@Overridepublic PageData<ScheduleJobDTO> page(Map<String, Object> params) {IPage<ScheduleJobEntity> page = baseDao.selectPage(getPage(params, Constant.CREATE_DATE, false),getWrapper(params));return getPageData(page, ScheduleJobDTO.class);}@Overridepublic ScheduleJobDTO get(Long id) {ScheduleJobEntity entity = baseDao.selectById(id);return ConvertUtils.sourceToTarget(entity, ScheduleJobDTO.class);}private QueryWrapper<ScheduleJobEntity> getWrapper(Map<String, Object> params){String beanName = (String)params.get("beanName");QueryWrapper<ScheduleJobEntity> wrapper = new QueryWrapper<>();wrapper.like(StringUtils.isNotBlank(beanName), "bean_name", beanName);return wrapper;}@Override@Transactional(rollbackFor = Exception.class)public void save(ScheduleJobDTO dto) {ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class);entity.setStatus(Constant.ScheduleStatus.NORMAL.getValue());this.insert(entity);ScheduleUtils.createScheduleJob(scheduler, entity);}@Override@Transactional(rollbackFor = Exception.class)public void update(ScheduleJobDTO dto) {ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class);ScheduleUtils.updateScheduleJob(scheduler, entity);this.updateById(entity);}@Override@Transactional(rollbackFor = Exception.class)public void deleteBatch(Long[] ids) {for(Long id : ids){ScheduleUtils.deleteScheduleJob(scheduler, id);}//删除数据this.deleteBatchIds(Arrays.asList(ids));}@Overridepublic int updateBatch(Long[] ids, int status){Map<String, Object> map = new HashMap<>(2);map.put("ids", ids);map.put("status", status);return baseDao.updateBatch(map);}@Override@Transactional(rollbackFor = Exception.class)public void run(Long[] ids) {for(Long id : ids){ScheduleUtils.run(scheduler, this.selectById(id));}}@Override@Transactional(rollbackFor = Exception.class)public void pause(Long[] ids) {for(Long id : ids){ScheduleUtils.pauseJob(scheduler, id);}updateBatch(ids, Constant.ScheduleStatus.PAUSE.getValue());}@Override@Transactional(rollbackFor = Exception.class)public void resume(Long[] ids) {for(Long id : ids){ScheduleUtils.resumeJob(scheduler, id);}updateBatch(ids, Constant.ScheduleStatus.NORMAL.getValue());}}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.global.iot.common.constant.Constant;
import io.global.iot.common.page.PageData;
import io.global.iot.common.service.impl.BaseServiceImpl;
import io.global.iot.common.utils.ConvertUtils;
import io.global.iot.modules.job.dao.ScheduleJobLogDao;
import io.global.iot.modules.job.dto.ScheduleJobLogDTO;
import io.global.iot.modules.job.entity.ScheduleJobLogEntity;
import io.global.iot.modules.job.service.ScheduleJobLogService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;import java.util.Map;@Service
public class ScheduleJobLogServiceImpl extends BaseServiceImpl<ScheduleJobLogDao, ScheduleJobLogEntity> implements ScheduleJobLogService {@Overridepublic PageData<ScheduleJobLogDTO> page(Map<String, Object> params) {IPage<ScheduleJobLogEntity> page = baseDao.selectPage(getPage(params, Constant.CREATE_DATE, false),getWrapper(params));return getPageData(page, ScheduleJobLogDTO.class);}private QueryWrapper<ScheduleJobLogEntity> getWrapper(Map<String, Object> params){String jobId = (String)params.get("jobId");QueryWrapper<ScheduleJobLogEntity> wrapper = new QueryWrapper<>();wrapper.eq(StringUtils.isNotBlank(jobId), "job_id", jobId);return wrapper;}@Overridepublic ScheduleJobLogDTO get(Long id) {ScheduleJobLogEntity entity = baseDao.selectById(id);return ConvertUtils.sourceToTarget(entity, ScheduleJobLogDTO.class);}}
工具类:
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.utils;import io.global.iot.common.constant.Constant;
import io.global.iot.common.exception.ExceptionUtils;
import io.global.iot.common.utils.SpringContextUtils;
import io.global.iot.modules.job.entity.ScheduleJobEntity;
import io.global.iot.modules.job.entity.ScheduleJobLogEntity;
import io.global.iot.modules.job.service.ScheduleJobLogService;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;import java.lang.reflect.Method;
import java.util.Date;/*** 定时任务** @author Mark sunlightcs@gmail.com*/
public class ScheduleJob extends QuartzJobBean {private Logger logger = LoggerFactory.getLogger(getClass());@Overrideprotected void executeInternal(JobExecutionContext context) {ScheduleJobEntity scheduleJob = (ScheduleJobEntity) context.getMergedJobDataMap().get(ScheduleUtils.JOB_PARAM_KEY);//数据库保存执行记录ScheduleJobLogEntity log = new ScheduleJobLogEntity();log.setJobId(scheduleJob.getId());log.setBeanName(scheduleJob.getBeanName());log.setParams(scheduleJob.getParams());log.setCreateDate(new Date());//任务开始时间long startTime = System.currentTimeMillis();try {//执行任务logger.info("任务准备执行,任务ID:{}", scheduleJob.getId());Object target = SpringContextUtils.getBean(scheduleJob.getBeanName());Method method = target.getClass().getDeclaredMethod("run", String.class);method.invoke(target, scheduleJob.getParams());//任务执行总时长long times = System.currentTimeMillis() - startTime;log.setTimes((int)times);//任务状态log.setStatus(Constant.SUCCESS);logger.info("任务执行完毕,任务ID:{} 总共耗时:{} 毫秒", scheduleJob.getId(), times);} catch (Exception e) {logger.error("任务执行失败,任务ID:{}", scheduleJob.getId(), e);//任务执行总时长long times = System.currentTimeMillis() - startTime;log.setTimes((int)times);//任务状态log.setStatus(Constant.FAIL);log.setError(ExceptionUtils.getErrorStackTrace(e));}finally {//获取spring beanScheduleJobLogService scheduleJobLogService = SpringContextUtils.getBean(ScheduleJobLogService.class);scheduleJobLogService.insert(log);}}
}
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.utils;import io.global.iot.common.constant.Constant;
import io.global.iot.common.exception.CommonException;
import io.global.iot.common.exception.ErrorCode;
import io.global.iot.modules.job.entity.ScheduleJobEntity;
import org.quartz.*;/*** 定时任务工具类** @author Mark sunlightcs@gmail.com*/
public class ScheduleUtils {private final static String JOB_NAME = "TASK_";/*** 任务调度参数key*/public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";/*** 获取触发器key*/public static TriggerKey getTriggerKey(Long jobId) {return TriggerKey.triggerKey(JOB_NAME + jobId);}/*** 获取jobKey*/public static JobKey getJobKey(Long jobId) {return JobKey.jobKey(JOB_NAME + jobId);}/*** 获取表达式触发器*/public static CronTrigger getCronTrigger(Scheduler scheduler, Long jobId) {try {return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));} catch (SchedulerException e) {throw new CommonException(ErrorCode.JOB_ERROR, e);}}/*** 创建定时任务*/public static void createScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {try {//构建job信息JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getId())).build();//表达式调度构建器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()).withMisfireHandlingInstructionDoNothing();//按新的cronExpression表达式构建一个新的triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getId())).withSchedule(scheduleBuilder).build();//放入参数,运行时的方法可以获取jobDetail.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob);scheduler.scheduleJob(jobDetail, trigger);//暂停任务if(scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()){pauseJob(scheduler, scheduleJob.getId());}} catch (SchedulerException e) {throw new CommonException(ErrorCode.JOB_ERROR, e);}}/*** 更新定时任务*/public static void updateScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {try {TriggerKey triggerKey = getTriggerKey(scheduleJob.getId());//表达式调度构建器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()).withMisfireHandlingInstructionDoNothing();CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getId());//按新的cronExpression表达式重新构建triggertrigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();//参数trigger.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob);scheduler.rescheduleJob(triggerKey, trigger);//暂停任务if(scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()){pauseJob(scheduler, scheduleJob.getId());}} catch (SchedulerException e) {throw new CommonException(ErrorCode.JOB_ERROR, e);}}/*** 立即执行任务*/public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) {try {//参数JobDataMap dataMap = new JobDataMap();dataMap.put(JOB_PARAM_KEY, scheduleJob);scheduler.triggerJob(getJobKey(scheduleJob.getId()), dataMap);} catch (SchedulerException e) {throw new CommonException(ErrorCode.JOB_ERROR, e);}}/*** 暂停任务*/public static void pauseJob(Scheduler scheduler, Long jobId) {try {scheduler.pauseJob(getJobKey(jobId));} catch (SchedulerException e) {throw new CommonException(ErrorCode.JOB_ERROR, e);}}/*** 恢复任务*/public static void resumeJob(Scheduler scheduler, Long jobId) {try {scheduler.resumeJob(getJobKey(jobId));} catch (SchedulerException e) {throw new CommonException(ErrorCode.JOB_ERROR, e);}}/*** 删除定时任务*/public static void deleteScheduleJob(Scheduler scheduler, Long jobId) {try {scheduler.deleteJob(getJobKey(jobId));} catch (SchedulerException e) {throw new CommonException(ErrorCode.JOB_ERROR, e);}}
}
初始化定时任务数据:
/*** Copyright (c) 2018 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package io.global.iot.modules.job.init;import io.global.iot.modules.job.dao.ScheduleJobDao;
import io.global.iot.modules.job.entity.ScheduleJobEntity;
import io.global.iot.modules.job.utils.ScheduleUtils;
import jakarta.annotation.Resource;
import org.quartz.CronTrigger;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;import java.util.List;/*** 初始化定时任务数据** @author Mark sunlightcs@gmail.com*/
@Component
public class JobCommandLineRunner implements CommandLineRunner {@Autowiredprivate Scheduler scheduler;@Resourceprivate ScheduleJobDao scheduleJobDao;@Overridepublic void run(String... args) {List<ScheduleJobEntity> scheduleJobList = scheduleJobDao.selectList(null);for(ScheduleJobEntity scheduleJob : scheduleJobList){CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getId());//如果不存在,则创建if(cronTrigger == null) {ScheduleUtils.createScheduleJob(scheduler, scheduleJob);}else {ScheduleUtils.updateScheduleJob(scheduler, scheduleJob);}}}
}
测试定时任务:
package io.global.iot.modules.job.task;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;/*** @author wanglu* @date 2021/10/26 10:14*/
@Slf4j
@Component("cardStatusChangeTask")
public class CardStatusChangeTask implements ITask {@Overridepublic void run(String params) {log.info("satTask params: {}", params);long startTime = System.currentTimeMillis();log.info("satTask定时任务正在执行 speedTime: {}", System.currentTimeMillis() - startTime);}
}
INSERT INTO `global_iot`.`schedule_job`(`id`, `bean_name`, `params`, `cron_expression`, `status`, `remark`, `creator`, `create_date`, `updater`, `update_date`) VALUES (1481537149966696450, 'cardStatusChangeTask', 'cardStatusChange', '*/10 * * * * ?', 1, '卡片状态变更', 1067246875800000001, '2023-09-25 14:28:54', 1067246875800000001, '2023-09-25 14:28:54');
quartz自带表结构
-- quartz自带表结构
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 DEFAULT CHARSET=utf8;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 DEFAULT CHARSET=utf8;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 DEFAULT CHARSET=utf8;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 DEFAULT CHARSET=utf8;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 DEFAULT CHARSET=utf8;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 DEFAULT CHARSET=utf8;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 DEFAULT CHARSET=utf8;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 DEFAULT CHARSET=utf8;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 DEFAULT CHARSET=utf8;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 DEFAULT CHARSET=utf8;CREATE TABLE QRTZ_LOCKS (SCHED_NAME VARCHAR(120) NOT NULL,LOCK_NAME VARCHAR(40) NOT NULL,PRIMARY KEY (SCHED_NAME,LOCK_NAME))ENGINE=InnoDB DEFAULT CHARSET=utf8;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);
项目结构:
测试结果: