Quartz教程 - 9动态调度【完结】

更好的阅读体验:点这里www.doubibiji.com

9 动态调度

源码:获取源码0分下载,给个好评


什么是动态调度?

动态调度就是不停止服务器,动态的控制定时任务的添加、修改、删除、启动、暂停等操作。

为什么需要动态调度?

如果现在有定时任务想停止运行或者修改执行时间,如果没有动态调度,那么就需要停止服务器,修改代码重新运行项目,具有很大的局限性。所以如果能在 web 页面动态控制定时任务的运行就很方便了。


例如有下面这样一个页面来管理定时任务就好了:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里就不编写上面这个页面了,只实现后端的接口的实现。

下面来一步一步实现。


注意一下,如果你按照前面的内容进行了 Quartz 的持久化,那么 Quart 的表中可能有数据,但是下面操作重新整理了项目,删除了之前的一些任务类,这样启动项目的时候,Quartz会重新加载数据,找不到对应的任务类,会报错,所以需要先清空一下 Quartz 各个表的数据。

清空表的语句:

DELETE FROM QRTZ_LOCKS;
DELETE FROM QRTZ_CALENDARS;
DELETE FROM QRTZ_FIRED_TRIGGERS;
DELETE FROM QRTZ_PAUSED_TRIGGER_GRPS;
DELETE FROM QRTZ_SCHEDULER_STATE;
DELETE FROM QRTZ_BLOB_TRIGGERS;
DELETE FROM QRTZ_CRON_TRIGGERS;
DELETE FROM QRTZ_SIMPLE_TRIGGERS;
DELETE FROM QRTZ_SIMPROP_TRIGGERS;
DELETE FROM QRTZ_TRIGGERS;
DELETE FROM QRTZ_JOB_DETAILS;

1 新建SpringBoot项目

新建 SpringBoot 项目这里就不介绍了。可能你的项目已经是 SpringBoot 的项目了。


2 添加依赖

在项目的 pom.xml 中添加依赖

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

直接使用 starter 的方式集成。


3 建表

因为我们需要对任务进行创建、删除、更新任务,所以要将任务保存下来,所以需要建表来保存我们的任务。

正常情况下,我们是有一个任务列表页面,显示所有的任务,可以对这些任务进行操作,也就是一个 CRUD 的功能。

CREATE TABLE tz_schedule_job (
  `id`								varchar(32) 	NOT NULL 			COMMENT 'ID',
  `job_name` 					varchar(128) 	NOT NULL 			COMMENT '任务名称',
  `bean_name` 				varchar(128)	NOT NULL 			COMMENT 'spring容器中bean的名称',
  `method_name` 			varchar(128)	NOT NULL 			COMMENT 'bean中的方法名',
  `cron_expression` 	varchar(32) 	NOT NULL 			COMMENT 'cron表达式',
  `params` 						varchar(512) 	DEFAULT NULL 	COMMENT '调用任务传递的参数',
  `status` 						tinyint(1) 		NOT NULL 			COMMENT '状态:1启用 0停用',
  `remark` 						varchar(512) 	DEFAULT NULL 	COMMENT '任务备注',
  `create_time` 			datetime		 	DEFAULT NULL 	COMMENT '创建时间',
  `update_time` 			datetime		 	DEFAULT NULL 	COMMENT '更新时间',
  PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

4 创建pojo和mapper

这里使用 mybatis-plus 来操作 tz_schedule_job 表数据,所以需要创建表对应的实体类。

ScheduleJob.java

package com.doubibiji.springbootquartz.pojo;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;

@Data
@TableName("tz_schedule_job")
public class ScheduleJob implements Serializable {

    // 任务id
    @TableId
    private String id;
    private String jobName;
    private String beanName;
    private String methodName;
    private String cronExpression;
    private String params;
    private Integer status;
    private String remark;
    private Date createTime;
    private Date updateTime;
}

ScheduleJobMapper.java

package com.doubibiji.springbootquartz.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.doubibiji.springbootquartz.pojo.ScheduleJob;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface ScheduleJobMapper extends BaseMapper<ScheduleJob> {

    /**
     *  批量修改任务状态
     */
    int updateBatch(@Param("jobIds") String[] jobIds, @Param("status") int status);

}

在 resources/mapper 下新建 ScheduleJobMapper.xml

ScheduleJobMapper.xml

<?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.doubibiji.springbootquartz.mapper.ScheduleJobMapper">

	<!-- 批量更新状态 -->
	<update id="updateBatch">
		update tz_schedule_job set status = #{status} where id in
		<foreach item="jobId" collection="jobIds"  open="(" separator="," close=")">
			#{jobId}
		</foreach>
	</update>

</mapper>

5 业务类

业务类就是要处理定时任务实际要执行的业务逻辑的类。

后面定时任务会定时调用这个类中的方法,重复执行。

OrderServiceImpl.java

package com.doubibiji.springbootquartz.service.impl;

import com.doubibiji.springbootquartz.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service("orderService")
public class OrderServiceImpl implements OrderService {

    @Override
    public void checkOrderPayResult() {
        log.info("检查订单是否支付成功");
    }

    @Override
    public void checkOrderExpire() {
        log.info("检查订单是否超时");
    }
}

注意:一定要添加 @Service("orderService") 注解,后面会通过 orderService 名称到 Spring 容器中获取这个对象。


6 任务类

任务类只有一个,任务类接收实体类对象 ScheduleJob 。获取实体类中的 beanNamemethodName 属性,通过 beanName 到 Spring 容器中获取业务类对象,然后通过反射执行 methodName 指定的方法。

package com.doubibiji.springbootquartz.job;

import com.doubibiji.springbootquartz.pojo.ScheduleJob;
import com.doubibiji.springbootquartz.util.JobInvokeUtil;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.springframework.scheduling.quartz.QuartzJobBean;

@Slf4j
public class QuartzJob extends QuartzJobBean {

    public static final String SCHEDULE_JOB = "SCHEDULE_JOB_KEY";

    @Override
    protected void executeInternal(JobExecutionContext context) {
        // 获取到ScheduleJob对象
        ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get(SCHEDULE_JOB);
        // 在这个方法中,通过ScheduleJob对象中的类创建对象,并触发指定的方法
        JobInvokeUtil.invokeMethod(scheduleJob);
    }
}

这里和前面实现定时任务有一些不同,在前面,我们有一个定时任务就创建一个任务类。这里我们只创建一个任务类,所有的任务都是通过这一个任务类来调用。也就是我们有多个任务的话,通过不同的触发器来调用这一个任务。然后在这个任务中,通过不同的参数,来确定调用哪个业务方法。


7 工具类

工具类主要处理通过 beanNamemethodName 的值找到 Spring 容器中的 业务类对象,然后通过反射执行 methodName 指定的方法。

JobInvokeUtil.java

package com.doubibiji.springbootquartz.util;

import cn.hutool.core.util.StrUtil;
import com.doubibiji.springbootquartz.pojo.ScheduleJob;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;

/**
 * 任务执行工具
 */
public class JobInvokeUtil {
    /**
     * 执行方法
     */
    public static void invokeMethod(ScheduleJob scheduleJob) {
        // 获取到bean
        Object target = SpringContextUtils.getBean(scheduleJob.getBeanName());
        try {
            if (StrUtil.isNotEmpty(scheduleJob.getParams())) {
                Method method = target.getClass().getDeclaredMethod(scheduleJob.getMethodName(), String.class);
                ReflectionUtils.makeAccessible(method);
                // 调用bean中的方法
                method.invoke(target, scheduleJob.getParams());
            } else {
                Method method = target.getClass().getDeclaredMethod(scheduleJob.getMethodName());
                ReflectionUtils.makeAccessible(method);
                // 调用bean中的方法
                method.invoke(target);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("执行定时任务失败", e);
        }
    }
}

SpringContextUtils 是获取 Spring 容器中对象的工具类:

package com.doubibiji.springbootquartz.util;

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

/**
 * Spring Context 工具类
 */
@Component
public class SpringContextUtils implements ApplicationContextAware {
	public static ApplicationContext applicationContext; 

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

	public static Object getBean(String name) {
		return applicationContext.getBean(name);
	}

	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<? extends Object> getType(String name) {
		return applicationContext.getType(name);
	}

}

还有一个工具类,就是操作 Quartz 任务的工具类。

ScheduleManager.java

package com.doubibiji.springbootquartz.config;

import com.doubibiji.springbootquartz.constant.ScheduleStatus;
import com.doubibiji.springbootquartz.job.QuartzJob;
import com.doubibiji.springbootquartz.pojo.ScheduleJob;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 定时任务工具类
 */
@Component
public class ScheduleManager {
    private final static String JOB_NAME = "TASK_";

    @Autowired
    private Scheduler scheduler;

    /**
     * 获取触发器key
     */
    private TriggerKey getTriggerKey(ScheduleJob scheduleJob) {
        return TriggerKey.triggerKey(JOB_NAME + scheduleJob.getId());
    }

    /**
     * 获取jobKey
     */
    private JobKey getJobKey(ScheduleJob scheduleJob) {
        return JobKey.jobKey(JOB_NAME + scheduleJob.getId());
    }

    /**
     * 获取表达式触发器
     */
    public CronTrigger getCronTrigger(ScheduleJob scheduleJob) {
        try {
            return (CronTrigger) scheduler.getTrigger(getTriggerKey(scheduleJob));
        } catch (SchedulerException e) {
            throw new RuntimeException("获取定时任务CronTrigger出现异常", e);
        }
    }

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

            //表达式调度构建器,可以根据scheduleJob修改withMisfireHandling方法
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
                    .withMisfireHandlingInstructionFireAndProceed();

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

            // 放入参数,也就是要执行的任务信息,运行时的方法可以获取
            jobDetail.getJobDataMap().put(QuartzJob.SCHEDULE_JOB, scheduleJob);

            scheduler.scheduleJob(jobDetail, trigger);

            //暂停任务
            if (scheduleJob.getStatus().equals(ScheduleStatus.PAUSE.getType())) {
                pauseJob(scheduleJob);
            }
        } catch (SchedulerException e) {
            throw new RuntimeException("创建定时任务失败", e);
        }
    }

    /**
     * 更新定时任务
     */
    public void updateScheduleJob(ScheduleJob scheduleJob) {
        try {
            TriggerKey triggerKey = getTriggerKey(scheduleJob);

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

            CronTrigger trigger = getCronTrigger(scheduleJob);

            // 如果定时任务不存在,则创建定时任务
            if (trigger == null) {
                createScheduleJob(scheduleJob);
                return;
            }

            //按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

            // 传递参数
            trigger.getJobDataMap().put(QuartzJob.SCHEDULE_JOB, scheduleJob);

            scheduler.rescheduleJob(triggerKey, trigger);

            //暂停任务
            if (scheduleJob.getStatus().equals(ScheduleStatus.PAUSE.getType())) {
                pauseJob(scheduleJob);
            }

        } catch (SchedulerException e) {
            throw new RuntimeException("更新定时任务失败", e);
        }
    }

    /**
     * 立即执行任务
     */
    public void run(ScheduleJob scheduleJob) {
        try {
            //参数
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(QuartzJob.SCHEDULE_JOB, scheduleJob);

            scheduler.triggerJob(getJobKey(scheduleJob), dataMap);
        } catch (SchedulerException e) {
            throw new RuntimeException("立即执行定时任务失败", e);
        }
    }

    /**
     * 暂停任务
     */
    public void pauseJob(ScheduleJob scheduleJob) {
        try {
            scheduler.pauseJob(getJobKey(scheduleJob));
        } catch (SchedulerException e) {
            throw new RuntimeException("暂停定时任务失败", e);
        }
    }

    /**
     * 恢复任务
     */
    public void resumeJob(ScheduleJob scheduleJob) {
        try {
            scheduler.resumeJob(getJobKey(scheduleJob));
        } catch (SchedulerException e) {
            throw new RuntimeException("恢复定时任务失败", e);
        }
    }

    /**
     * 删除定时任务
     */
    public void deleteScheduleJob(ScheduleJob scheduleJob) {
        try {
            // 停止触发器
            scheduler.pauseTrigger(getTriggerKey(scheduleJob));
            // 移除触发器
            scheduler.unscheduleJob(getTriggerKey(scheduleJob));
            // 删除任务
            scheduler.deleteJob(getJobKey(scheduleJob));
        } catch (SchedulerException e) {
            throw new RuntimeException("删除定时任务失败", e);
        }
    }

}

解释一下 createScheduleJob(ScheduleJob scheduleJob) 方法。

该方法接收到 ScheduleJob 对象,通过该对象来创建定时任务,创建定时任务使用的就是前面的任务类,将 ScheduleJob 对象传递到任务类中,每次触发任务,在任务类中都通过传递的 ScheduleJob 对象,使用 JobInvokeUtil.invokeMethod(scheduleJob); 的方式调用业务类。

触发器使用 ScheduleJob 对象中的 cronExpression 属性来创建。

上面只是一个工具类,我们需要在维护任务的时候,调用上面的工具类。


8 任务维护

正常情况下,我们会有一个任务的维护页面,显示一个任务列表,然后在页面上通过按钮调用后端的接口。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里只实现后端的接口,就不实现页面了。

接口实现:

ScheduleJobController.java

package com.doubibiji.springbootquartz.controller;

import com.doubibiji.springbootquartz.pojo.ScheduleJob;
import com.doubibiji.springbootquartz.service.ScheduleJobService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;

/**
 * 定时任务
 */
@Slf4j
@RestController
@RequestMapping("/schedule")
public class ScheduleJobController {

	@Autowired
	private ScheduleJobService scheduleJobService;
	
	/**
	 * 定时任务列表
	 */
	@GetMapping("/list")
	public ResponseEntity<List<ScheduleJob>> list(){
		List<ScheduleJob> scheduleJobList = scheduleJobService.list();
		return ResponseEntity.ok(scheduleJobList);
	}
	
	/**
	 * 保存定时任务
	 */
	@PostMapping
	public ResponseEntity<Void> save(@RequestBody ScheduleJob scheduleJob){
		scheduleJobService.saveAndStart(scheduleJob);
		return ResponseEntity.ok().build();
	}
	
	/**
	 * 修改定时任务
	 */
	@PutMapping
	public ResponseEntity<Void> update(@RequestBody ScheduleJob scheduleJob){
		scheduleJobService.updateScheduleJob(scheduleJob);
		return ResponseEntity.ok().build();
	}
	
	/**
	 * 删除定时任务
	 */
	@DeleteMapping
	public ResponseEntity<Void> delete(@RequestBody String[] jobIds){
		scheduleJobService.deleteBatch(jobIds);
		return ResponseEntity.ok().build();
	}
	
	/**
	 * 立即执行一次任务
	 */
	@PostMapping("/run")
	public ResponseEntity<Void> run(@RequestBody String[] jobIds){
		scheduleJobService.run(jobIds);
		return ResponseEntity.ok().build();
	}
	
	/**
	 * 暂停定时任务
	 */
	@PostMapping("/pause")
	public ResponseEntity<Void> pause(@RequestBody String[] jobIds){
		scheduleJobService.pause(jobIds);
		return ResponseEntity.ok().build();
	}
	
	/**
	 * 恢复定时任务
	 */
	@PostMapping("/resume")
	public ResponseEntity<Void> resume(@RequestBody String[] jobIds){
		scheduleJobService.resume(jobIds);
		return ResponseEntity.ok().build();
	}

}

Service 实现

在 Service 中实现任务数据的维护,更新数据库的数据,同时调用 Quartz 工具类,修改任务的调度。

ScheduleJobServiceImpl.java

package com.doubibiji.springbootquartz.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.doubibiji.springbootquartz.config.ScheduleManager;
import com.doubibiji.springbootquartz.constant.ScheduleStatus;
import com.doubibiji.springbootquartz.mapper.ScheduleJobMapper;
import com.doubibiji.springbootquartz.pojo.ScheduleJob;
import com.doubibiji.springbootquartz.service.ScheduleJobService;
import org.quartz.CronTrigger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

@Service
public class ScheduleJobServiceImpl implements ScheduleJobService {
	
	@Autowired
	private ScheduleJobMapper scheduleJobMapper;
	@Autowired
	private ScheduleManager scheduleManager;

	/**
	 * 项目启动时,初始化定时器
	 */
	@PostConstruct
	public void init(){
		// 初始化的时候,查询到所有的定时任务,查看哪些定时任务需要启动
		List<ScheduleJob> scheduleJobList = scheduleJobMapper.selectList(null);

		for (ScheduleJob scheduleJob : scheduleJobList) {
			// 从调度器中获取触发器,查看是否需要创建、恢复、暂停
			CronTrigger trigger = scheduleManager.getCronTrigger(scheduleJob);
			// 如果定时任务不存在,则创建定时任务
			if (trigger == null) {
				scheduleManager.createScheduleJob(scheduleJob);
			}
			else if (ScheduleStatus.NORMAL.getType().equals(scheduleJob.getStatus())) {
				scheduleManager.resumeJob(scheduleJob);
			}
			else if (ScheduleStatus.PAUSE.getType().equals(scheduleJob.getStatus())) {
				scheduleManager.pauseJob(scheduleJob);
			}
		}
	}

	/**
	 * 查询所有的定时任务
	 */
	public List<ScheduleJob> list() {
		// 初始化的时候,查询到所有的定时任务,查看哪些定时任务需要启动
		List<ScheduleJob> scheduleJobList = scheduleJobMapper.selectList(null);
		return scheduleJobList;
	}

	/**
	 * 保存并开始定时任务
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void saveAndStart(ScheduleJob scheduleJob) {
		// 先查看是否已经有相同的定时任务了
		LambdaQueryWrapper<ScheduleJob> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(ScheduleJob::getBeanName, scheduleJob.getBeanName());
		queryWrapper.eq(ScheduleJob::getMethodName, scheduleJob.getMethodName());
		long sameCount = scheduleJobMapper.selectCount(queryWrapper);
		if (sameCount > 0) {
			throw new RuntimeException("与存在相同的定时任务");
		}

		scheduleJob.setCreateTime(new Date());
		scheduleJob.setStatus(ScheduleStatus.NORMAL.getType());
		scheduleJobMapper.insert(scheduleJob);
        // 创建定时任务
        scheduleManager.createScheduleJob(scheduleJob);
	}

	/**
	 * 更新任务
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updateScheduleJob(ScheduleJob scheduleJob) {
		// 先查看是否已经有相同的定时任务了
		LambdaQueryWrapper<ScheduleJob> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(ScheduleJob::getBeanName, scheduleJob.getBeanName());
		queryWrapper.eq(ScheduleJob::getMethodName, scheduleJob.getMethodName());
		queryWrapper.ne(ScheduleJob::getId, scheduleJob.getId());
		long sameCount = scheduleJobMapper.selectCount(queryWrapper);
		if (sameCount > 0) {
			throw new RuntimeException("与存在相同的定时任务");
		}

		scheduleJob.setUpdateTime(new Date());
		scheduleManager.updateScheduleJob(scheduleJob);
		scheduleJobMapper.updateById(scheduleJob);
	}

	/**
	 * 删除任务
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void deleteBatch(String[] jobIds) {
		List<String> ids = Arrays.asList(jobIds);
		List<ScheduleJob> scheduleJobList = scheduleJobMapper.selectBatchIds(ids);
		for (ScheduleJob scheduleJob : scheduleJobList) {
			// 删除定时任务
			scheduleManager.deleteScheduleJob(scheduleJob);
		}
		scheduleJobMapper.deleteBatchIds(ids);
	}

	/**
	 * 立即执行一次任务
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
    public void run(String[] jobIds) {
    	for(String jobId : jobIds){
    		scheduleManager.run(scheduleJobMapper.selectById(jobId));
    	}
    }

	/**
	 * 暂停任务
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
    public void pause(String[] jobIds) {
		List<String> ids = Arrays.asList(jobIds);
		List<ScheduleJob> scheduleJobList = scheduleJobMapper.selectBatchIds(ids);
		for (ScheduleJob scheduleJob : scheduleJobList) {
			scheduleManager.pauseJob(scheduleJob);
		}

		// 更新任务状态
    	updateBatch(jobIds, ScheduleStatus.PAUSE.getType());
    }

	/**
	 * 恢复任务
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
    public void resume(String[] jobIds) {
		List<String> ids = Arrays.asList(jobIds);
		List<ScheduleJob> scheduleJobList = scheduleJobMapper.selectBatchIds(ids);
		for (ScheduleJob scheduleJob : scheduleJobList) {
			scheduleManager.resumeJob(scheduleJob);
		}

		// 更新任务状态
    	updateBatch(jobIds, ScheduleStatus.NORMAL.getType());
    }

	/**
	 *	批量更新任务状态
	 */
	public int updateBatch(String[] jobIds, int status) {
		return scheduleJobMapper.updateBatch(jobIds, status);
	}

}

9 项目配置

application.yaml

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/doubibiji-db?allowMultiQueries=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      minimum-idle: 3
      maximum-pool-size: 10
      idle-timeout: 10000
      connection-test-query: select 1
      initialization-fail-timeout: 15000
      connection-timeout: 15000

  # quartz 配置
  quartz:
    job-store-type: jdbc             # 使用jdbc的方式持久化
    jdbc:
      initialize-schema: never       # 启动后,不创建数据库表
    # 属性配置
    properties:
      org:
        quartz:
          # 集群配置
          scheduler:
            instanceName: MyScheduler       # 名称自定义,一起集群的各个服务器,名称要一致
            instanceId: AUTO                # 实例ID,各个集群的服务器,不能重复,这里使用自动生成就好了
          jobStore:
            isClustered: true               # 是否进行集群配置
            misfireThreshold: 10000					# 设置任务调度器的 Misfire 阈值为 10000 毫秒


# mybaits-plus配置
mybatis-plus:
  # MyBatis Mapper所对应的XML文件位置
  mapper-locations: classpath*:/mapper/*Mapper.xml
  global-config:
    # 关闭MP3.0自带的banner
    banner: false
    db-config:
      # 主键类型 0:数据库ID自增 1.未定义 2.用户输入 3 id_worker 4.uuid 5.id_worker字符串表示
      id-type: assign_uuid
      #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
      field-strategy: NOT_NULL
      # 默认数据库表下划线命名
      table-underline: true

项目结构如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

10 调用

使用 Postman 调用接口进行测试。

创建任务:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

后台执行结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


暂停任务:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

11 可能出现的错误

在执行任务的时候,可能出现以下错误,ScheduleJob 传递到任务类中,再次获取报转换错误的问题。

java.lang.ClassCastException: class com.doubibiji.springbootquartz.pojo.ScheduleJob cannot be cast to class com.doubibiji.springbootquartz.pojo.ScheduleJob (com.doubibiji.springbootquartz.pojo.ScheduleJob is in unnamed module of loader 'app'; com.doubibiji.springbootquartz.pojo.ScheduleJob is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @50d4c396)
	at com.doubibiji.springbootquartz.job.QuartzJob.executeInternal(QuartzJob.java:17) ~[classes/:na]
	at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:75) ~[spring-context-support-5.3.31.jar:5.3.31]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.3.2.jar:na]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) ~[quartz-2.3.2.jar:na]

2024-01-05 21:12:03.223 ERROR 98290 --- [eduler_Worker-1] org.quartz.core.ErrorLogger              : Job (DEFAULT.TASK_92599230a8db58564fdda00b5813db12 threw an exception.

org.quartz.SchedulerException: Job threw an unhandled exception.
	at org.quartz.core.JobRunShell.run(JobRunShell.java:213) ~[quartz-2.3.2.jar:na]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) ~[quartz-2.3.2.jar:na]
Caused by: java.lang.ClassCastException: class com.doubibiji.springbootquartz.pojo.ScheduleJob cannot be cast to class com.doubibiji.springbootquartz.pojo.ScheduleJob (com.doubibiji.springbootquartz.pojo.ScheduleJob is in unnamed module of loader 'app'; com.doubibiji.springbootquartz.pojo.ScheduleJob is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @50d4c396)
	at com.doubibiji.springbootquartz.job.QuartzJob.executeInternal(QuartzJob.java:17) ~[classes/:na]
	at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:75) ~[spring-context-support-5.3.31.jar:5.3.31]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.3.2.jar:na]
	... 1 common frames omitted

将项目中的 spring-boot-devtools 去掉就可以了。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

源码:获取源码0分下载,给个好评

  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山石岐渡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值