Spring Boot integra Quartz

1: agregar dependencia de cuarzo al archivo pom del proyecto

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

2: Modificar el archivo 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

Consulte el archivo 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

Aviso:

1. Si necesita que Quartz genere automáticamente las tablas requeridas para Quartz cuando se ejecuta por primera vez, ¿entonces quartzJob? La configuración posterior es: enableMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai.

Después de ejecutarlo por primera vez, podrás modificarlo según tus necesidades.

2. inicializar-esquema: siempre en el archivo de configuración. El atributo siempre de la configuración significa que la tabla se regenerará cada vez que se inicialice (realizando un borrado y una creación). Después de la generación, se puede modificar para nunca.

Solo cuando se cumplen las dos condiciones anteriores al mismo tiempo, Quartz puede generar automáticamente las tablas requeridas cuando se ejecuta por primera vez.

3-1: Método 1: Iniciar el proyecto generará 11 tablas en la base de datos, como se muestra a continuación, haga clic para ver la introducción detallada de cada tabla

Insertar descripción de la imagen aquí

3-2: Método 2: También puede ejecutar directamente los archivos SQL requeridos por Quartz para generar 11 tablas requeridas por Quartz.

En la ruta org.quartz.impl.jdbcjobstore del paquete ja de quartz, como se muestra a continuación
Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí

4: Sobre esta base, agregaremos una tabla adicional para integrarla con información con la que podamos tener relaciones comerciales.

Insertar descripción de la imagen aquí

declaración SQL para crear tabla

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: crear un diagrama de proyecto de cuarzo

Insertar descripción de la imagen aquí

6: Controlador

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: Servicio

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: ServicioImpl

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: mapeador

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 bajo asignador

<?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: entidad de clase de entidad

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: Caso de trabajo simple

Si el programador quiere ejecutar una tarea, primero debe tener una clase relacionada con la tarea.

Escribí tres casos comunes: uno está sincronizado sin parámetros, uno sin parámetros y el otro con parámetros.

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: Utilice cartero para probar con parámetros

Insertar descripción de la imagen aquí
Esta es una tarea única con parámetros. Después de que la tarea se agregue correctamente, se generará una tarea pendiente en la tabla qrtz_job_details. Una vez completado el procesamiento, este registro se eliminará. Si es una tarea cíclica, siempre existir.

** misfireThreshold indica el tiempo máximo si la tarea no se procesa antes del tiempo. Si se excede el tiempo, se convertirá en un informe de falla.

Las constantes de la política MisFire se definen en la clase CronTrigger y se enumeran a continuación:
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1
MISFIRE_INSTRUCTION_DO_NOTHING = 2
MISFIRE_INSTRUCTION_SMART_POLICY = 0
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1
Según la introducción de JavaDoc y el análisis del documento del sitio web oficial, la estrategia de ejecución correspondiente es la siguiente:
MISFIRE_INSTRUCTION _ FIRE_ONCE_NOW: Ejecutar una vez inmediatamente, luego según lo definido por Cron Ejecución del punto de tiempo
MISFIRE_INSTRUCTION_DO_NOTHING: no hacer nada, espere a que Cron defina el punto de tiempo para la siguiente ejecución de la tarea.
MISFIRE_INSTRUCTION_SMART_POLICY: estrategia inteligente, ejecución diferente para diferentes activadores. Cuando CronTrigger es MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY: recupere todo lo perdido puntos de tiempo de ejecución. Por ejemplo, si una tarea se ejecuta una vez cada 15 segundos y la tarea ejecutada se pierde por 4 minutos, cuando se ejecuta MisFire, se ejecutarán 4*(60/15) = 16 tareas a la vez. Otro El parámetro que afecta la
estrategia de ejecución de misFire es misfireThreshold.

Resumir los obstáculos encontrados

** Abra dos servicios y luego conéctese a la misma base de datos, si llega la tarea programada, se ejecutará en cualquier servidor de forma aleatoria (el error encontrado es que a veces se ejecuta y otras no se ejecuta al principio, pensando que es un temporizador problema del marco. Es mejor descubrir que se trata de un problema al abrir dos servicios y tener cuidado de no abrir el clúster, ya que el clúster también está bajo carga de tareas)

** El problema con el temporizador de cuarzo que a veces se ejecuta y otras no es que la instancia de la tabla de base de datos qrtz_scheduler_state está borrada.

** Para excepciones comerciales programadas temporalmente, JobExecutionException se puede generar de la siguiente manera: se puede ejecutar repetidamente hasta que se genere una excepción, pero no será válida después de reiniciar el servidor.

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()));
	}
}

Extensión: @Scheduler la ejecución de tareas programadas se realiza de forma predeterminada en un solo subproceso para lograr problemas de ejecución simultánea

Las tareas programadas se ejecutarán en función del grupo de subprocesos de ejecución de tareas. El número predeterminado de grupos de subprocesos de ejecución de tareas configurados en Spring es 1.
Al agregar la siguiente configuración al proyecto, aumente la cantidad de grupos de subprocesos de ejecución de tareas para mejorar las capacidades de ejecución concurrente:

Método 1

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

Método-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;
    }

Supongo que te gusta

Origin blog.csdn.net/qq_19891197/article/details/128867584
Recomendado
Clasificación