Spring Boot は Quartz を統合します

1: プロジェクトの pom ファイルに Quartz の依存関係を追加します。

<!-- quartz定时任务 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

2: application.yml ファイルを変更する

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/quartzJob?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    password: root
    username: root
    driver-class-name: com.mysql.cj.jdbc.Driver

  quartz:
    job-store-type: jdbc
    jdbc:
      initialize-schema: always

ymlファイルを参照してください

spring:
  ## quartz定时任务,采用数据库方式
  quartz:
    job-store-type: jdbc
    initialize-schema: embedded
    #定时任务启动开关,true-开  false-关
    auto-startup: true
    #延迟1秒启动定时任务
    startup-delay: 1s
    #启动时更新己存在的Job
    overwrite-existing-jobs: true
    properties:
      org:
        quartz:
          scheduler:
            instanceName: MyScheduler
            instanceId: AUTO
          jobStore:
            class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix: QRTZ_
            isClustered: false
            misfireThreshold: 12000
            clusterCheckinInterval: 15000
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 1
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true

知らせ:

1. Quartz を初めて実行するときに、Quartz に必要なテーブルを自動的に生成する必要がある場合は、quartzJob? その後の構成は次のとおりです:allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai

初めて実行した後、必要に応じて変更できます。

2. 初期化スキーマ: 常に設定ファイル内にあります。設定の always 属性は、テーブルが初期化される (削除と作成を実行する) たびに再生成されることを意味します。生成後は、テーブルを再生成しないように変更できます。

上記 2 つの条件が同時に満たされた場合にのみ、Quartz は初めて実行されるときに必要なテーブルを自動的に生成できます。

3-1: 方法 1: プロジェクトを開始すると、以下に示すようにデータベースに 11 個のテーブルが生成されます。クリックすると、各テーブルの詳細な紹介が表示されます。

ここに画像の説明を挿入します

3-2: 方法 2: Quartz に必要な SQL ファイルを直接実行して、Quartz に必要な 11 個のテーブルを生成することもできます。

以下に示すように、quartz の ja パッケージの org.quartz.impl.jdbcjobstore パス内
ここに画像の説明を挿入します
ここに画像の説明を挿入します

4: これに基づいて、ビジネス関係がある可能性のある情報を統合するための追加のテーブルを追加します。

ここに画像の説明を挿入します

テーブルを作成するSQL文

CREATE TABLE `sys_quartz_job` (
  `id` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  `create_by` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '创建人',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `del_flag` int DEFAULT NULL COMMENT '删除状态',
  `update_by` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '修改人',
  `update_time` datetime DEFAULT NULL COMMENT '修改时间',
  `job_class_name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '任务类名',
  `cron_expression` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT 'cron表达式',
  `parameter` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '参数',
  `meeting_record_id` int DEFAULT NULL COMMENT '会议室记录id',
  `description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '描述',
  `status` int DEFAULT NULL COMMENT '状态 0正常 -1停止',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC;

5: Quartzプロジェクト図の作成

ここに画像の説明を挿入します

6:コントローラー

package org.jeecg.modules.quartz.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.ImportExcelUtil;
import org.jeecg.modules.quartz.entity.QuartzJob;
import org.jeecg.modules.quartz.service.IQuartzJobService;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @Description: 定时任务
 */
@RestController
@RequestMapping("/sys/quartzJob")
@Slf4j
@Api(tags = "定时任务接口")
public class QuartzJobController {
    
    
	@Autowired
	private IQuartzJobService quartzJobService;
	@Autowired
	private Scheduler scheduler;

	/**
	 * 分页列表查询
	 * 
	 * @param quartzJob
	 * @param pageNo
	 * @param pageSize
	 * @param req
	 * @return
	 */
	@RequestMapping(value = "/list", method = RequestMethod.GET)
	public Result<?> queryPageList(QuartzJob quartzJob, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
			@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, HttpServletRequest req) {
    
    
		QueryWrapper<QuartzJob> queryWrapper = QueryGenerator.initQueryWrapper(quartzJob, req.getParameterMap());
		Page<QuartzJob> page = new Page<QuartzJob>(pageNo, pageSize);
		IPage<QuartzJob> pageList = quartzJobService.page(page, queryWrapper);
        return Result.ok(pageList);

	}

	/**
	 * 添加定时任务
	 * 
	 * @param quartzJob
	 * @return
	 */
	//@RequiresRoles("admin")
	@RequestMapping(value = "/add", method = RequestMethod.POST)
	public Result<?> add(@RequestBody QuartzJob quartzJob) {
    
    
		quartzJobService.saveAndScheduleJob(quartzJob);
		return Result.ok("创建定时任务成功");
	}

	/**
	 * 更新定时任务
	 * 
	 * @param quartzJob
	 * @return
	 */
	//@RequiresRoles("admin")
	@RequestMapping(value = "/edit", method ={
    
    RequestMethod.PUT, RequestMethod.POST})
	public Result<?> eidt(@RequestBody QuartzJob quartzJob) {
    
    
		try {
    
    
			quartzJobService.editAndScheduleJob(quartzJob);
		} catch (SchedulerException e) {
    
    
			log.error(e.getMessage(),e);
			return Result.error("更新定时任务失败!");
		}
	    return Result.ok("更新定时任务成功!");
	}

	/**
	 * 通过id删除
	 * 
	 * @param id
	 * @return
	 */
	//@RequiresRoles("admin")
	@RequestMapping(value = "/delete", method = RequestMethod.DELETE)
	public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
    
    
		QuartzJob quartzJob = quartzJobService.getById(id);
		if (quartzJob == null) {
    
    
			return Result.error("未找到对应实体");
		}
		quartzJobService.deleteAndStopJob(id);
        return Result.ok("删除成功!");

	}

	/**
	 * 批量删除
	 * 
	 * @param ids
	 * @return
	 */
	//@RequiresRoles("admin")
	@RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE)
	public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
    
    
		if (ids == null || "".equals(ids.trim())) {
    
    
			return Result.error("参数不识别!");
		}
		for (String id : Arrays.asList(ids.split(SymbolConstant.COMMA))) {
    
    
			QuartzJob job = quartzJobService.getById(id);
			quartzJobService.deleteAndStopJob(id);
		}
        return Result.ok("删除定时任务成功!");
	}

	/**
	 * 暂停定时任务
	 * 
	 * @param id
	 * @return
	 */
	//@RequiresRoles("admin")
	@GetMapping(value = "/pause")
	@ApiOperation(value = "停止定时任务")
	public Result<Object> pauseJob(@RequestParam(name = "id") String id) {
    
    
		QuartzJob job = quartzJobService.getById(id);
		if (job == null) {
    
    
			return Result.error("定时任务不存在!");
		}
		quartzJobService.pause(job);
		return Result.ok("停止定时任务成功");
	}

	/**
	 * 启动定时任务
	 * 
	 * @param id
	 * @return
	 */
	//@RequiresRoles("admin")
	@GetMapping(value = "/resume")
	@ApiOperation(value = "启动定时任务")
	public Result<Object> resumeJob(@RequestParam(name = "id") String id) {
    
    
		QuartzJob job = quartzJobService.getById(id);
		if (job == null) {
    
    
			return Result.error("定时任务不存在!");
		}
		quartzJobService.resumeJob(job);
		//scheduler.resumeJob(JobKey.jobKey(job.getJobClassName().trim()));
		return Result.ok("启动定时任务成功");
	}

	/**
	 * 通过id查询
	 * 
	 * @param id
	 * @return
	 */
	@RequestMapping(value = "/queryById", method = RequestMethod.GET)
	public Result<?> queryById(@RequestParam(name = "id", required = true) String id) {
    
    
		QuartzJob quartzJob = quartzJobService.getById(id);
        return Result.ok(quartzJob);
	}

	/**
	 * 导出excel
	 * 
	 * @param request
	 * @param quartzJob
	 */
	@RequestMapping(value = "/exportXls")
	public ModelAndView exportXls(HttpServletRequest request, QuartzJob quartzJob) {
    
    
		// Step.1 组装查询条件
		QueryWrapper<QuartzJob> queryWrapper = QueryGenerator.initQueryWrapper(quartzJob, request.getParameterMap());
		// Step.2 AutoPoi 导出Excel
		ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
		List<QuartzJob> pageList = quartzJobService.list(queryWrapper);
		// 导出文件名称
		mv.addObject(NormalExcelConstants.FILE_NAME, "定时任务列表");
		mv.addObject(NormalExcelConstants.CLASS, QuartzJob.class);
        //获取当前登录用户
        //update-begin---author:wangshuai ---date:20211227  for:[JTC-116]导出人写死了------------
        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
		mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("定时任务列表数据", "导出人:"+user.getRealname(), "导出信息"));
        //update-end---author:wangshuai ---date:20211227  for:[JTC-116]导出人写死了------------
        mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
		return mv;
	}

	/**
	 * 通过excel导入数据
	 * 
	 * @param request
	 * @param response
	 * @return
	 */
	@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
	public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) throws IOException {
    
    
		MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
		Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
		// 错误信息
		List<String> errorMessage = new ArrayList<>();
		int successLines = 0, errorLines = 0;
		for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
    
    
            // 获取上传文件对象
			MultipartFile file = entity.getValue();
			ImportParams params = new ImportParams();
			params.setTitleRows(2);
			params.setHeadRows(1);
			params.setNeedSave(true);
			try {
    
    
				List<QuartzJob> listQuartzJobs = ExcelImportUtil.importExcel(file.getInputStream(), QuartzJob.class, params);
				//add-begin-author:taoyan date:20210909 for:导入定时任务,并不会被启动和调度,需要手动点击启动,才会加入调度任务中 #2986
				for(QuartzJob job: listQuartzJobs){
    
    
					job.setStatus(CommonConstant.STATUS_DISABLE);
				}
				List<String> list = ImportExcelUtil.importDateSave(listQuartzJobs, IQuartzJobService.class, errorMessage,CommonConstant.SQL_INDEX_UNIQ_JOB_CLASS_NAME);
				//add-end-author:taoyan date:20210909 for:导入定时任务,并不会被启动和调度,需要手动点击启动,才会加入调度任务中 #2986
				errorLines+=list.size();
				successLines+=(listQuartzJobs.size()-errorLines);
			} catch (Exception e) {
    
    
				log.error(e.getMessage(), e);
				return Result.error("文件导入失败!");
			} finally {
    
    
				try {
    
    
					file.getInputStream().close();
				} catch (IOException e) {
    
    
					e.printStackTrace();
				}
			}
		}
		return ImportExcelUtil.imporReturnRes(errorLines,successLines,errorMessage);
	}

	/**
	 * 立即执行
	 * @param id
	 * @return
	 */
	//@RequiresRoles("admin")
	@GetMapping("/execute")
	public Result<?> execute(@RequestParam(name = "id", required = true) String id) {
    
    
		QuartzJob quartzJob = quartzJobService.getById(id);
		if (quartzJob == null) {
    
    
			return Result.error("未找到对应实体");
		}
		try {
    
    
			quartzJobService.execute(quartzJob);
		} catch (Exception e) {
    
    
			//e.printStackTrace();
			log.info("定时任务 立即执行失败>>"+e.getMessage());
			return Result.error("执行失败!");
		}
		return Result.ok("执行成功!");
	}

}

7:サービス

package org.jeecg.modules.quartz.service;

import java.util.List;

import org.jeecg.modules.quartz.entity.QuartzJob;
import org.quartz.SchedulerException;

import com.baomidou.mybatisplus.extension.service.IService;

/**
 * @Description: 定时任务
 */
public interface IQuartzJobService extends IService<QuartzJob> {
    
    

    /**
     * 通过类名寻找定时任务
     * @param jobClassName 类名
     * @return List<QuartzJob>
     */
	List<QuartzJob> findByJobClassName(String jobClassName);

    /**
     * 保存定时任务
     * @param quartzJob
     * @return boolean
     */
	boolean saveAndScheduleJob(QuartzJob quartzJob);

    /**
     * 编辑定时任务
     * @param quartzJob
     * @return boolean
     * @throws SchedulerException
     */
	boolean editAndScheduleJob(QuartzJob quartzJob) throws SchedulerException;

    /**
     * 删除定时任务
     * @param id
     * @return boolean
     */
	boolean deleteAndStopJob(String id);

    /**
     * 恢复定时任务
     * @param quartzJob
     * @return
     */
	boolean resumeJob(QuartzJob quartzJob);

	/**
	 * 执行定时任务
	 * @param quartzJob
     * @throws Exception
	 */
	void execute(QuartzJob quartzJob) throws Exception;

	/**
	 * 暂停任务
	 * @param quartzJob
	 * @throws SchedulerException
	 */
	void pause(QuartzJob quartzJob);
}

8:サービス実装

package org.jeecg.modules.quartz.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.DateUtils;
import org.jeecg.modules.quartz.entity.QuartzJob;
import org.jeecg.modules.quartz.mapper.QuartzJobMapper;
import org.jeecg.modules.quartz.service.IQuartzJobService;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * @Description: 定时任务
 */
@Slf4j
@Service
public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob> implements IQuartzJobService {
    
    
	@Autowired
	private QuartzJobMapper quartzJobMapper;
	@Autowired
	private Scheduler scheduler;

	/**
	 * 立即执行的任务分组
	 */
	private static final String JOB_TEST_GROUP = "test_group";

	@Override
	public List<QuartzJob> findByJobClassName(String jobClassName) {
    
    
		return quartzJobMapper.findByJobClassName(jobClassName);
	}

	/**
	 * 保存&启动定时任务
	 */
	@Override
	@Transactional(rollbackFor = JeecgBootException.class)
	public boolean saveAndScheduleJob(QuartzJob quartzJob) {
    
    
		// DB设置修改
		quartzJob.setDelFlag(CommonConstant.DEL_FLAG_0);
		boolean success = this.save(quartzJob);
		if (success) {
    
    
			if (CommonConstant.STATUS_NORMAL.equals(quartzJob.getStatus())) {
    
    
				// 定时器添加
				this.schedulerAdd(quartzJob.getId(), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
			}
		}
		return success;
	}

	/**
	 * 恢复定时任务
	 */
	@Override
	@Transactional(rollbackFor = JeecgBootException.class)
	public boolean resumeJob(QuartzJob quartzJob) {
    
    
		schedulerDelete(quartzJob.getId());
		schedulerAdd(quartzJob.getId(), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
		quartzJob.setStatus(CommonConstant.STATUS_NORMAL);
		return this.updateById(quartzJob);
	}

	/**
	 * 编辑&启停定时任务
	 * @throws SchedulerException 
	 */
	@Override
	@Transactional(rollbackFor = JeecgBootException.class)
	public boolean editAndScheduleJob(QuartzJob quartzJob) throws SchedulerException {
    
    
		if (CommonConstant.STATUS_NORMAL.equals(quartzJob.getStatus())) {
    
    
			schedulerDelete(quartzJob.getId());
			schedulerAdd(quartzJob.getId(), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
		}else{
    
    
			scheduler.pauseJob(JobKey.jobKey(quartzJob.getId()));
		}
		return this.updateById(quartzJob);
	}

	/**
	 * 删除&停止删除定时任务
	 */
	@Override
	@Transactional(rollbackFor = JeecgBootException.class)
	public boolean deleteAndStopJob(String id) {
    
    
		schedulerDelete(id);
		boolean ok = this.removeById(id);
		return ok;
	}

	@Override
	public void execute(QuartzJob quartzJob) throws Exception {
    
    
		String jobName = quartzJob.getJobClassName().trim();
		Date startDate = new Date();
		String ymd = DateUtils.date2Str(startDate,DateUtils.yyyymmddhhmmss.get());
		String identity =  jobName + ymd;
		//3秒后执行 只执行一次
		// update-begin--author:sunjianlei ---- date:20210511--- for:定时任务立即执行,延迟3秒改成0.1秒-------
		startDate.setTime(startDate.getTime() + 100L);
		// update-end--author:sunjianlei ---- date:20210511--- for:定时任务立即执行,延迟3秒改成0.1秒-------
		// 定义一个Trigger
		SimpleTrigger trigger = (SimpleTrigger)TriggerBuilder.newTrigger()
				.withIdentity(identity, JOB_TEST_GROUP)
				.startAt(startDate)
				.build();
		// 构建job信息
		JobDetail jobDetail = JobBuilder.newJob(getClass(jobName).getClass()).withIdentity(identity).usingJobData("parameter", quartzJob.getParameter()).build();
		// 将trigger和 jobDetail 加入这个调度
		scheduler.scheduleJob(jobDetail, trigger);
		// 启动scheduler
		scheduler.start();
	}

	@Override
	@Transactional(rollbackFor = JeecgBootException.class)
	public void pause(QuartzJob quartzJob){
    
    
		schedulerDelete(quartzJob.getId());
		quartzJob.setStatus(CommonConstant.STATUS_DISABLE);
		this.updateById(quartzJob);
	}

	/**
	 * 添加定时任务
	 *
	 * @param jobClassName
	 * @param cronExpression
	 * @param parameter
	 */
	private void schedulerAdd(String id, String jobClassName, String cronExpression, String parameter) {
    
    
		try {
    
    
			// 启动调度器
			scheduler.start();

			// 构建job信息
			JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(id).usingJobData("parameter", parameter).build();

			// 表达式调度构建器(即任务执行的时间)
			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

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

			scheduler.scheduleJob(jobDetail, trigger);
		} catch (SchedulerException e) {
    
    
			throw new JeecgBootException("创建定时任务失败", e);
		} catch (RuntimeException e) {
    
    
			throw new JeecgBootException(e.getMessage(), e);
		}catch (Exception e) {
    
    
			throw new JeecgBootException("后台找不到该类名:" + jobClassName, e);
		}
	}

	/**
	 * 删除定时任务
	 * 
	 * @param id
	 */
	private void schedulerDelete(String id) {
    
    
		try {
    
    
			scheduler.pauseTrigger(TriggerKey.triggerKey(id));
			scheduler.unscheduleJob(TriggerKey.triggerKey(id));
			scheduler.deleteJob(JobKey.jobKey(id));
		} catch (Exception e) {
    
    
			log.error(e.getMessage(), e);
			throw new JeecgBootException("删除定时任务失败");
		}
	}

	private static Job getClass(String classname) throws Exception {
    
    
		Class<?> class1 = Class.forName(classname);
		return (Job) class1.newInstance();
	}

}

9:マッパー

package org.jeecg.modules.quartz.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.quartz.entity.QuartzJob;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

/**
 * @Description: 定时任务
 */
public interface QuartzJobMapper extends BaseMapper<QuartzJob> {
    
    

    /**
     * 根据jobClassName查询
     * @param jobClassName 任务类名
     * @return
     */
	public List<QuartzJob> findByJobClassName(@Param("jobClassName") String jobClassName);

}

10: マッパーの下の 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="org.jeecg.modules.quartz.mapper.QuartzJobMapper">

<!-- 根据jobClassName查询 -->
<select id="findByJobClassName" resultType="org.jeecg.modules.quartz.entity.QuartzJob">
		select * from  sys_quartz_job  where job_class_name = #{jobClassName}
	</select>
</mapper>

11: エンティティクラスエンティティ

package org.jeecg.modules.quartz.entity;

import java.io.Serializable;

import org.jeecg.common.aspect.annotation.Dict;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;

import lombok.Data;

/**
 * @Description: 定时任务
 */
@Data
@TableName("sys_quartz_job")
public class QuartzJob implements Serializable {
    
    
    private static final long serialVersionUID = 1L;

    /**
     * id
     */
    @TableId(type = IdType.ASSIGN_ID)
    private java.lang.String id;

    /**
     * 创建人
     */
    private java.lang.String createBy;

    /**
     * 创建时间
     */
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private java.util.Date createTime;

    /**
     * 删除状态
     */
    private java.lang.Integer delFlag;

    /**
     * 修改人
     */
    private java.lang.String updateBy;

    /**
     * 修改时间
     */
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private java.util.Date updateTime;

    /**
     * 任务类名
     */
    @Excel(name = "任务类名", width = 40)
    private java.lang.String jobClassName;

    /**
     * cron表达式
     */
    @Excel(name = "cron表达式", width = 30)
    private java.lang.String cronExpression;

    /**
     * 参数
     */
    @Excel(name = "参数", width = 15)
    private java.lang.String parameter;


    /**
     * 描述
     */
    @Excel(name = "描述", width = 40)
    private java.lang.String description;

    /**
     * 状态 0正常 -1停止
     */
    @Excel(name = "状态", width = 15, dicCode = "quartz_status")
    @Dict(dicCode = "quartz_status")
    private java.lang.Integer status;

}

12: 単純なジョブのケース

スケジューラがタスクを実行したい場合は、まずタスク関連のクラスが必要です。

3 つの一般的なケースを書きました。1 つはパラメータなしで同期する場合、1 つはパラメータなしで同期する場合、もう 1 つはパラメータを使用する場合です。

package org.jeecg.modules.quartz.job;

import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.util.DateUtils;
import org.quartz.*;

/**
 * @Description: 同步定时任务测试
 *
 * 此处的同步是指 当定时任务的执行时间大于任务的时间间隔时
 * 会等待第一个任务执行完成才会走第二个任务
 */
@PersistJobDataAfterExecution // 持久化JobDataMap里的数据,使下一个定时任务还能获取到这些值
@DisallowConcurrentExecution // 禁止并发多任务执行,所以永远只有一个任务在执行中
@Slf4j
public class AsyncJob implements Job {
    
    

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    
    
        log.info(" --- 同步任务调度开始 --- " + " Job Execution key:"+jobExecutionContext.getJobDetail().getKey());
        try {
    
    
            //此处模拟任务执行时间 5秒  任务表达式配置为每秒执行一次:0/1 * * * * ? *
            Thread.sleep(20000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        //测试发现 每5秒执行一次
        log.info(" --- 执行完毕,时间:"+DateUtils.now()+"---"  + " 线程名"+ Thread.currentThread().getName() );
    }

}

package org.jeecg.modules.quartz.job;

import org.jeecg.common.util.DateUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import lombok.extern.slf4j.Slf4j;

/**
 * 示例不带参定时任务
 */
@Slf4j
public class SampleJob implements Job {
    
    

	@Override
	public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    
    
		log.info(" Job Execution key:"+jobExecutionContext.getJobDetail().getKey());
		log.info(String.format(" Jeecg-Boot 普通定时任务 SampleJob !  时间:" + DateUtils.getTimestamp()));
	}
}

package org.jeecg.modules.quartz.job;

import org.jeecg.common.util.DateUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import lombok.extern.slf4j.Slf4j;

/**
 * 示例带参定时任务
 */
@Slf4j
public class SampleParamJob implements Job {
    
    

	/**
	 * 若参数变量名修改 QuartzJobController中也需对应修改
	 */
	private String parameter;

	public void setParameter(String parameter) {
    
    
		this.parameter = parameter;
	}

	@Override
	public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    
    
		log.info(" Job Execution key:"+jobExecutionContext.getJobDetail().getKey());
		log.info( String.format("welcome %s! Jeecg-Boot 带参数定时任务 SampleParamJob !   时间:" + DateUtils.now(), this.parameter));
	}
}

13: postman を使用してパラメータをテストする

ここに画像の説明を挿入します
これはパラメータ付きの 1 回限りのタスクです。タスクが正常に追加されると、保留中のタスクがテーブル qrtz_job_details に生成されます。処理が完了すると、このレコードは削除されます。周期的なタスクの場合は、常に存在する。

**misfireThreshold はタスクがその時間までに処理されなかった場合の最大時間を示し、この時間を超えると不発レポートとなります。

MisFire ポリシー定数は CronTrigger クラスで定義されており、次のようにリストされています:
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1
MISFIRE_INSTRUCTION_DO_NOTHING = 2
MISFIRE_INSTRUCTION_SMART_POLICY = 0
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1
JavaDoc の紹介と公式 Web サイトのドキュメント分析によると、対応する実行戦略は次のとおりです。
_INSTRUCTION _FIRE_ONCE_NOW: 1 回実行immediately, then as defined by Cron Time point execution
MISFIRE_INSTRUCTION_DO_NOTHING: Do nothing, wait for Cron to define the time point for the next task execution.
MISFIRE_INSTRUCTION_SMART_POLICY: Intelligent strategy, different execution for different Triggers. When CronTrigger is MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY: Make up all missedたとえば、タスクが 15 秒ごとに実行され、実行されたタスクが 4 分遅れた場合、MisFire が実行されると、4*(60/15) = 16 個のタスクが一度に実行されます。 missFire の実行戦略に影響を与えるパラメータは、
misfireThreshold です。

遭遇した落とし穴を要約する

** 2 つのサービスを開いて、同じデータベースに接続します。スケジュールされたタスクが到着すると、任意のサーバー上でランダムに実行されます (遭遇する落とし穴は、最初は実行されることもあれば、タイマーだと思って実行されないこともあります)フレームワークの問題です。これが 2 つのサービスを開くことの問題であることを確認し、クラスターにもタスクの負荷がかかっているため、クラスターを開かないように注意することが最善です)

** Quartz タイマーが実行される場合と実行されない場合がある問題は、データベース テーブル qrtz_scheduler_state のインスタンスがクリアされることです。

** 一時的なスケジュールされたビジネス例外の場合、次のように JobExecutionException がスローされる可能性があり、例外がスローされるまで繰り返し実行できますが、サーバーの再起動後は無効になります。

package org.jeecg.modules.quartz.job;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.util.DateUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

/**
 * 示例不带参定时任务
 */
@Slf4j
public class SampleJob implements Job {
    
    

	@Override
	public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    
    
		log.info(" Job Execution key:"+jobExecutionContext.getJobDetail().getKey());
		int a=0;
		try {
    
    
			a=a/a;
		} catch (Exception e) {
    
    
			log.error("执行任务出错了...");
			try {
    
    
				Thread.sleep(2000);
			} catch (InterruptedException e1) {
    
    
				e1.printStackTrace();
			}
			JobExecutionException e2 = new JobExecutionException(e);
			// this job will refire immediately
			e2.setRefireImmediately(true);
			throw e2;
		}
		log.info(String.format(" Jeecg-Boot 普通定时任务 SampleJob !  时间:" + DateUtils.getTimestamp()));
	}
}

拡張機能: @Scheduler のスケジュールされたタスクの実行は、同時実行の問題を解決するためにデフォルトで単一スレッドになります。

スケジュールされたタスクは、タスク実行スレッド プールに基づいて実行されます。Spring で設定されるタスク実行スレッド プールのデフォルトの数は 1 です。
プロジェクトに次の設定を追加することで、タスク実行スレッド プールの数を増やし、同時実行機能を向上させます。

方法1

@Configuration
public class ScheduledConfig {
    
    
    @Bean
    public TaskScheduler taskScheduler() {
    
    
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        return taskScheduler;
    }
}

方法-2

@Configuration
@EnableScheduling
@Slf4j
public class ScheduledConfig implements SchedulingConfigurer {
    
    
 
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
    
    
        TaskScheduler scheduler = this.taskScheduler();
        scheduledTaskRegistrar.setTaskScheduler(scheduler);
    }
 
    @Bean(destroyMethod = "shutdown")
    public ThreadPoolTaskScheduler taskScheduler() {
    
    
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(10);
        scheduler.setThreadNamePrefix("task-");
        scheduler.setAwaitTerminationSeconds(60);
        scheduler.setWaitForTasksToCompleteOnShutdown(true);
        return scheduler;
    }

おすすめ

転載: blog.csdn.net/qq_19891197/article/details/128867584