springboot+Quartz通过数据库控制定时任务执行与时间

news2024/9/21 14:35:02

前言

在我们的springboot项目中,有很多种实现定时任务的方式
有用最简单的

@Scheduled 实现定时任务,即:

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
@EnableScheduling
public class ScheduledTasks {

    // 固定延迟任务
    @Scheduled(fixedDelay = 5000)
    public void fixedDelayTask() {
        System.out.println("任务以固定的5秒延迟执行");
    }

    // 固定频率任务
    @Scheduled(fixedRate = 5000)
    public void fixedRateTask() {
        System.out.println("任务以固定的每5秒执行");
    }

    // Cron 表达式任务
    @Scheduled(cron = "0 0/5 * * * ?")
    public void cronTask() {
        System.out.println("任务以固定的每5分钟执行");
    }
}

使用TaskScheduler 动态创建任务

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
public class SchedulerConfig {

    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(10);
        scheduler.setThreadNamePrefix("scheduler-");
        return scheduler;
    }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.ScheduledFuture;

@Component
public class DynamicScheduledTask {

    @Autowired
    private TaskScheduler taskScheduler;

    private ScheduledFuture<?> scheduledFuture;

    @PostConstruct
    public void scheduleTask() {
        scheduledFuture = taskScheduler.schedule(this::task, new CronTrigger("0 0/5 * * * ?"));
    }

    public void task() {
        System.out.println("定时任务执行");
    }
}

除此外还有各种定时框架

Quartz
XXL-JOB
Elastic-Job

当然还有其他,但是本次将会着重讲解如何用Quartz定时框架数据库来控制定时任务的执行与否以及执行时间长度

🎈🎈🎈🎈🎈🎈🎈🎈🎈🎈

Quartz定时框架

Quartz 是一个开源的调度框架,作为 Spring 默认的调度框架,Quartz 很容易与 Spring 集成实现灵活可配置的调度功能,它有很多优点

强大的调度功能
灵活的应用方式,支持任务和调度的多种组合方式,支持调度数据的多种存储方式;
分布式和集群能力

缺点也是有的

分布式支持不友好,没有内置 UI 管理控制台、相较于其他框架使用麻烦。

如下为Quartz相关的链接

Quartz 框架

Quzrtz框架官方文档

除此外,在 Quartz 体系结构中,有三个组件非常重要

Scheduler :调度器。使用Scheduler启动Trigger去执行Job。
Trigger :触发器。用来定义 Job(任务)触发条件、触发时间,触发间隔,终止时间等。
有四大类型:SimpleTrigger(简单的触发器)、CornTrigger(Cron表达式触发器)、DateIntervalTrigger(日期触发器)、CalendarIntervalTrigger(日历触发器)。当然,本次主要以SimpleTrigger和CornTrigger作为演示
Job :任务。具体要执行的业务逻辑,需要定时执行的逻辑

虽然我们本文章说用数据库的方式进行和Quartz进行配置,但是它也能用本地配置的的方式执行,详情如下:

类型优点缺点
RAM作业存储不要外部数据库,配置容易,运行速度快因为调度程序信息是存储在被分配给 JVM 的内存里面,所以,当应用程序停止运行时,所有调度信息将被丢失。另外因为存储到JVM内存里面,所以可以存储多少个 Job 和 Trigger 将会受到限制
JDBC 作业存储支持集群,因为所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应用服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启而导致执行失败的任务运行速度的快慢取决与连接数据库的快慢

✨✨✨✨✨✨✨✨✨

执行步骤

重要MAVEN依赖

此处给出Quartz的依赖,依赖版本随自己项目的springboot项目版本

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

建表与实体

Quartz虽然也有它自己的表,可以使用它的内置的数据库表进行搭建定时任务,但我追求简洁,方便,因此我本次自定义建表

CREATE TABLE `task_config` (
  `handle` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '主键',
  `task_id` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '定时任务编号',
  `task_name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '定时任务名称',
  `module_name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '执行分类',
  `enable` tinyint(1) NOT NULL COMMENT '任务是否执行',
  `task_run_type` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '定时执行方式,固定速率FIX或者corn表达式CORN',
  `task_run_fix` int(11) DEFAULT NULL COMMENT '固定速率时读取的配置',
  `task_run_corn` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'corn表达式时候读取的配置',
  `create_user` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人',
  `delete_flag` tinyint(1) NOT NULL COMMENT '是否任务失效',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_user` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '更新人',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`handle`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

对应建立实体如下:

import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class TaskConfig {
  private String handle;

  private String taskId;

  private String taskName;

  private String moduleName;

  private String enable;

  private String taskRunType;

  private Integer taskRunFix;
  
  private String taskRunCorn;

  private String createUser;

  private String deleteFlag;

  private LocalDateTime createTime;

  private String updateUser;

  private LocalDateTime updateTime;
}

JDBC数据库DAO

✒️✒️✒️✒️✒️✒️✒️✒️✒️

在我的项目中是使用JDBC的数据库连接方式,你们如果和我的处理方式不一致,可使用自己的数据库连接代码

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import com.hxc.DateUtils;
import com.hxc.Where;

import com.hxc.user.baseTable.TaskConfig;

public class TaskConfigDao {

  private static final String SQL_INS = "INSERT INTO task_config(handle,task_id,task_name,module_name,enable,task_run_type,task_run_fix,task_run_corn,create_user,delete_flag,create_time,update_user,update_time,handle,task_id,task_name,module_name,enable,task_run_type,task_run_fix,task_run_corn,create_user,delete_flag,create_time,update_user,update_time) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";

  private static final String SQL_UPD = "UPDATE task_config SET task_id=?,task_name=?,module_name=?,enable=?,task_run_type=?,task_run_fix=?,task_run_corn=?,create_user=?,delete_flag=?,create_time=?,update_user=?,update_time=?,task_id=?,task_name=?,module_name=?,enable=?,task_run_type=?,task_run_fix=?,task_run_corn=?,create_user=?,delete_flag=?,create_time=?,update_user=?,update_time=? WHERE handle=? AND handle=?";

  private static final String SQL_SEL = "SELECT handle,task_id,task_name,module_name,enable,task_run_type,task_run_fix,task_run_corn,create_user,delete_flag,create_time,update_user,update_time,handle,task_id,task_name,module_name,enable,task_run_type,task_run_fix,task_run_corn,create_user,delete_flag,create_time,update_user,update_time FROM task_config ";

  private final Connection conn;

  public TaskConfigDao(Connection conn) {
    this.conn = conn;
  }

  public int insert(TaskConfig data) {
    try (PreparedStatement ps = this.conn.prepareStatement(SQL_INS)) {
      ps.setString(1, data.getHandle());
      ps.setString(2, data.getTaskId());
      ps.setString(3, data.getTaskName());
      ps.setString(4, data.getModuleName());
      ps.setString(5, data.getEnable());
      ps.setString(6, data.getTaskRunType());
      ps.setObject(7, data.getTaskRunFix());
      ps.setString(8, data.getTaskRunCorn());
      ps.setString(9, data.getCreateUser());
      ps.setString(10, data.getDeleteFlag());
      DateUtils.setDateTz(ps, 11, data.getCreateTime());
      ps.setString(12, data.getUpdateUser());
      DateUtils.setDateTz(ps, 13, data.getUpdateTime());
      ps.setString(14, data.getHandle());
      ps.setString(15, data.getTaskId());
      ps.setString(16, data.getTaskName());
      ps.setString(17, data.getModuleName());
      ps.setString(18, data.getEnable());
      ps.setString(19, data.getTaskRunType());
      ps.setObject(20, data.getTaskRunFix());
      ps.setString(21, data.getTaskRunCorn());
      ps.setString(22, data.getCreateUser());
      ps.setString(23, data.getDeleteFlag());
      DateUtils.setDateTz(ps, 24, data.getCreateTime());
      ps.setString(25, data.getUpdateUser());
      DateUtils.setDateTz(ps, 26, data.getUpdateTime());

      return ps.executeUpdate();
    } catch (SQLException e) {
      throw new IllegalStateException("数据库查询错误, " + e.getMessage(), e);
    }
  }

  public int insert(List<TaskConfig> dataList){
    try (PreparedStatement ps = this.conn.prepareStatement(SQL_INS)) {
     for(TaskConfig data : dataList) {
      ps.setString(1, data.getHandle());
      ps.setString(2, data.getTaskId());
      ps.setString(3, data.getTaskName());
      ps.setString(4, data.getModuleName());
      ps.setString(5, data.getEnable());
      ps.setString(6, data.getTaskRunType());
      ps.setObject(7, data.getTaskRunFix());
      ps.setString(8, data.getTaskRunCorn());
      ps.setString(9, data.getCreateUser());
      ps.setString(10, data.getDeleteFlag());
      DateUtils.setDateTz(ps, 11, data.getCreateTime());
      ps.setString(12, data.getUpdateUser());
      DateUtils.setDateTz(ps, 13, data.getUpdateTime());
      ps.setString(14, data.getHandle());
      ps.setString(15, data.getTaskId());
      ps.setString(16, data.getTaskName());
      ps.setString(17, data.getModuleName());
      ps.setString(18, data.getEnable());
      ps.setString(19, data.getTaskRunType());
      ps.setObject(20, data.getTaskRunFix());
      ps.setString(21, data.getTaskRunCorn());
      ps.setString(22, data.getCreateUser());
      ps.setString(23, data.getDeleteFlag());
      DateUtils.setDateTz(ps, 24, data.getCreateTime());
      ps.setString(25, data.getUpdateUser());
      DateUtils.setDateTz(ps, 26, data.getUpdateTime());

      ps.addBatch();
     }
      return ps.executeBatch().length;
    } catch (SQLException e) {
      throw new IllegalStateException("数据库查询错误, " + e.getMessage(), e);
    }
  }

  public int update(TaskConfig data){
    try (PreparedStatement ps = this.conn.prepareStatement(SQL_UPD)) {
      ps.setString(1, data.getTaskId());
      ps.setString(2, data.getTaskName());
      ps.setString(3, data.getModuleName());
      ps.setString(4, data.getEnable());
      ps.setString(5, data.getTaskRunType());
      ps.setObject(6, data.getTaskRunFix());
      ps.setString(7, data.getTaskRunCorn());
      ps.setString(8, data.getCreateUser());
      ps.setString(9, data.getDeleteFlag());
      DateUtils.setDateTz(ps, 10, data.getCreateTime());
      ps.setString(11, data.getUpdateUser());
      DateUtils.setDateTz(ps, 12, data.getUpdateTime());
      ps.setString(13, data.getTaskId());
      ps.setString(14, data.getTaskName());
      ps.setString(15, data.getModuleName());
      ps.setString(16, data.getEnable());
      ps.setString(17, data.getTaskRunType());
      ps.setObject(18, data.getTaskRunFix());
      ps.setString(19, data.getTaskRunCorn());
      ps.setString(20, data.getCreateUser());
      ps.setString(21, data.getDeleteFlag());
      DateUtils.setDateTz(ps, 22, data.getCreateTime());
      ps.setString(23, data.getUpdateUser());
      DateUtils.setDateTz(ps, 24, data.getUpdateTime());
      ps.setString(25, data.getHandle());
      ps.setString(26, data.getHandle());

      return ps.executeUpdate();
    } catch (SQLException e) {
      throw new IllegalStateException("数据库查询错误, " + e.getMessage(), e);
    }
  }

  public int update(List<TaskConfig> dataList){
    try (PreparedStatement ps = this.conn.prepareStatement(SQL_UPD)) {
     for(TaskConfig data : dataList) {
      ps.setString(1, data.getTaskId());
      ps.setString(2, data.getTaskName());
      ps.setString(3, data.getModuleName());
      ps.setString(4, data.getEnable());
      ps.setString(5, data.getTaskRunType());
      ps.setObject(6, data.getTaskRunFix());
      ps.setString(7, data.getTaskRunCorn());
      ps.setString(8, data.getCreateUser());
      ps.setString(9, data.getDeleteFlag());
      DateUtils.setDateTz(ps, 10, data.getCreateTime());
      ps.setString(11, data.getUpdateUser());
      DateUtils.setDateTz(ps, 12, data.getUpdateTime());
      ps.setString(13, data.getTaskId());
      ps.setString(14, data.getTaskName());
      ps.setString(15, data.getModuleName());
      ps.setString(16, data.getEnable());
      ps.setString(17, data.getTaskRunType());
      ps.setObject(18, data.getTaskRunFix());
      ps.setString(19, data.getTaskRunCorn());
      ps.setString(20, data.getCreateUser());
      ps.setString(21, data.getDeleteFlag());
      DateUtils.setDateTz(ps, 22, data.getCreateTime());
      ps.setString(23, data.getUpdateUser());
      DateUtils.setDateTz(ps, 24, data.getUpdateTime());
      ps.setString(25, data.getHandle());
      ps.setString(26, data.getHandle());

      ps.addBatch();
     }
     return ps.executeBatch().length;
    } catch (SQLException e) {
      throw new IllegalStateException("数据库查询错误, " + e.getMessage(), e);
    }
  }

  public int delete(String handle){
    try (PreparedStatement ps = this.conn.prepareStatement("DELETE FROM task_config WHERE handle=?")) {
      ps.setString(1, handle);
      ps.setString(2, handle);

      return ps.executeUpdate();
    } catch (SQLException e) {
      throw new IllegalStateException("数据库查询错误, " + e.getMessage(), e);
    }
  }

  public List<TaskConfig> selectAll(){
    ArrayList<TaskConfig> result = new ArrayList<TaskConfig>();
    try (PreparedStatement ps = this.conn.prepareStatement(SQL_SEL)) {

      ResultSet rs = ps.executeQuery();
      while(rs.next()) {
        result.add(convert(rs));
      }
      return result;
    } catch (SQLException e) {
      throw new IllegalStateException("数据库查询错误, " + e.getMessage(), e);
    }
  }


  public TaskConfig selectByPK(String handle){
    TaskConfig result = null;
    try (PreparedStatement ps = this.conn.prepareStatement(SQL_SEL  + "WHERE handle=?")) {
      ps.setString(1, handle);
      ps.setString(2, handle);


      ResultSet rs = ps.executeQuery();
      if(rs.next()) {
        result = convert(rs);
      }
      return result;
    } catch (SQLException e) {
      throw new IllegalStateException("数据库查询错误, " + e.getMessage(), e);
    }
  }

  public List<TaskConfig> selectWhere(Where where){
    List<TaskConfig> result = new ArrayList<>();
    try (PreparedStatement ps = where.prepareStatement(conn, SQL_SEL)) {
      ResultSet rs = ps.executeQuery();
      while(rs.next()) {
        result.add(convert(rs));
      }
      return result;
    } catch (SQLException e) {
      throw new IllegalStateException("数据库查询错误, " + e.getMessage(), e);
    }
  }

  private TaskConfig convert(ResultSet rs) throws SQLException {
    TaskConfig data = new TaskConfig();
    int index = 1;
    data.setHandle(rs.getString(index++));
    data.setTaskId(rs.getString(index++));
    data.setTaskName(rs.getString(index++));
    data.setModuleName(rs.getString(index++));
    data.setEnable(rs.getString(index++));
    data.setTaskRunType(rs.getString(index++));
    data.setTaskRunFix((Integer)rs.getObject(index++));
    data.setTaskRunCorn(rs.getString(index++));
    data.setCreateUser(rs.getString(index++));
    data.setDeleteFlag(rs.getString(index++));
    data.setCreateTime(DateUtils.getDateTz(rs, index++));
    data.setUpdateUser(rs.getString(index++));
    data.setUpdateTime(DateUtils.getDateTz(rs, index++));
    data.setHandle(rs.getString(index++));
    data.setTaskId(rs.getString(index++));
    data.setTaskName(rs.getString(index++));
    data.setModuleName(rs.getString(index++));
    data.setEnable(rs.getString(index++));
    data.setTaskRunType(rs.getString(index++));
    data.setTaskRunFix((Integer)rs.getObject(index++));
    data.setTaskRunCorn(rs.getString(index++));
    data.setCreateUser(rs.getString(index++));
    data.setDeleteFlag(rs.getString(index++));
    data.setCreateTime(DateUtils.getDateTz(rs, index++));
    data.setUpdateUser(rs.getString(index++));
    data.setUpdateTime(DateUtils.getDateTz(rs, index++));

    return data;
  }
}

定时配置与更新

🖊️🖊️🖊️🖊️🖊️🖊️🖊️🖊️🖊️🖊️

import java.sql.Connection;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.CronScheduleBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import java.util.List;
import org.springframework.context.annotation.Configuration;

@Configuration
public class QuartzConfig {

  @Autowired
  private Scheduler scheduler;

  @Autowired
  private PrimeDB primeDB;

  @PostConstruct
  public void init() {
    try (Connection conn = primeDB.create()) {
      TaskConfigDao taskConfigDao = new TaskConfigDao(conn);
      List<TaskConfig> configs = taskConfigDao.selectAll();
      for (TaskConfig config : configs) {
        if ("0".equals(config.getEnable())) {
          // 如果任务未启用,则跳过
          continue;
        }

        if (uploadQuartzScheduledGroup(config)) {
          continue; // 无效的调度方式
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }


  public boolean uploadQuartzScheduledGroup(TaskConfig config) throws SchedulerException {
    String jobGroup = "jobGroup_" + config.getTaskId(); // 根据需要设置任务组
    String triggerGroup = "triggerGroup_" + config.getTaskId(); // 根据需要设置触发器组

    JobKey jobKey = new JobKey(config.getTaskId(), jobGroup);
    if (scheduler.checkExists(jobKey)) {
      scheduler.deleteJob(jobKey);
    }

    JobDetail jobDetail = JobBuilder.newJob(TaskJob.class)
        .withIdentity(config.getTaskId(), jobGroup) // 指定任务组
        .build();

    Trigger trigger;
    switch (config.getTaskRunType()) {
      case "FIX":
        trigger = TriggerBuilder.newTrigger()
            .withIdentity(config.getTaskId() + "_trigger", triggerGroup) // 指定触发器组
            .startNow()
            .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInMilliseconds(config.getTaskRunFix())
                .repeatForever())
            .build();
        break;
      case "CRON":
        trigger = TriggerBuilder.newTrigger()
            .withIdentity(config.getTaskId() + "_trigger", triggerGroup) // 指定触发器组
            .withSchedule(CronScheduleBuilder.cronSchedule(config.getTaskRunCorn()))
            .build();
        break;
      default:
        return true;
    }

    scheduler.scheduleJob(jobDetail, trigger);
    return false;
  }
}

我的逻辑使用基础的ScheduledTasks 定时读取一次数据库的定时任务配置表实时更新我们的定时执行

import java.sql.Connection;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@EnableScheduling
@Component
@Slf4j
public class TaskUpdater {

  @Autowired
  private Scheduler scheduler;

  @Autowired
  private PrimeDB primeDB;

  @Autowired
  private QuartzConfig quartzConfig;

  @Scheduled(fixedRate = 60000) // 每分钟检查一次
  public void updateTasks() {
    System.out.println("更新定时配置");
    try (Connection conn = primeDB.create()) {
      TaskConfigDao taskConfigDao = new TaskConfigDao(conn);
      List<TaskConfig> configs = taskConfigDao.selectAll();
      for (TaskConfig config : configs) {
        if ("0".equals(config.getEnable())) {
          // 如果任务未启用,则删除任务
          JobKey jobKey = new JobKey(config.getTaskId(), "jobGroup_" + config.getTaskId());
          if (scheduler.checkExists(jobKey)) {
            scheduler.deleteJob(jobKey);
          }
          continue;
        }
        // 调用封装的启用定时逻辑
        quartzConfig.uploadQuartzScheduledGroup(config);
      }
    } catch (Exception e) {
      log.error("定时同步定时任务设定失败:" + e);
    }
  }
}

定时业务逻辑

✏️✏️✏️✏️✏️✏️✏️✏️✏️✏️✏️

接下来我举例两个例子,一个是基础的固定速率执行,一个是使用corn表达式执行

业务逻辑执行主入口

import java.util.HashMap;
import java.util.Map;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.stereotype.Component;

@Component
public class TaskJob implements Job {

  private final Map<String, TaskHandler> handlers;

  @Override
  public void execute(JobExecutionContext context) {
    String taskId = context.getJobDetail().getKey().getName();
    TaskHandler handler = handlers.get(taskId);

    if (handler != null) {
      handler.handle();
    } else {
      System.out.println("没有找到处理逻辑对应的任务 ID: " + taskId);
    }
  }

  public TaskJob() {
    handlers = new HashMap<>();
    handlers.put("AA", new AATaskHandler());
    handlers.put("BB", new BBTaskHandler());
  }
}

这里,我写了两个例子,一个是AA任务ID,和BB任务ID
后续如果有新的业务逻辑,只需要往如下代码新增业务逻辑即可

 public TaskJob() {
    handlers = new HashMap<>();
    handlers.put("AA", new AATaskHandler());
    handlers.put("BB", new BBTaskHandler());
    handlers.put("CC", new BBTaskHandler());
    // 更多其他....
  }

那么我对应的AA和BB业务逻辑如下:

在之前先需要写一个interface 实现继承来达到加入列表的目的

public interface TaskHandler {
    void handle();
}
public class AATaskHandler implements TaskHandler{

  @Override
  public void handle() {
    System.out.println("AA定时任务执行");
  }
}

public class BBTaskHandler implements TaskHandler{

  @Override
  public void handle() {
    System.out.println("BB定时任务执行");
  }
}

数据库数据新增

我们在逻辑里已经编写了两个业务逻辑,那么对应我们的表数据如下:
在这里插入图片描述

INSERT INTO primedata.task_config
(handle, task_id, task_name, module_name, enable, task_run_type, task_run_fix, task_run_corn, create_user, delete_flag, create_time, update_user, update_time)
VALUES('111', 'AA', 'AA', 'AA', 1, 'FIX', 1000, NULL, NULL, 0, NULL, NULL, NULL);

INSERT INTO primedata.task_config
(handle, task_id, task_name, module_name, enable, task_run_type, task_run_fix, task_run_corn, create_user, delete_flag, create_time, update_user, update_time)
VALUES('222', 'BB', 'BB', 'BB', 1, 'CRON', NULL, '0/1 * * * * ? ', NULL, 0, NULL, NULL, NULL);

也就是
新增一个AA任务ID,挂载在AA任务分组下,使用固定速率每1秒执行一次
新增一个BB任务ID,挂载在BB任务分组下,使用CRON表达式每1秒执行一次

此时启动项目,控制台输出:
在这里插入图片描述
AA和BB正常执行

假如此时修改AA任务ID,变成3秒执行一次:
在这里插入图片描述
等待更新配置之后:
在这里插入图片描述

可以看到执行逻辑已经更新为3秒一次

更新关闭BB任务ID:

在这里插入图片描述
在这里插入图片描述

重要: 如上,我们只要更新了任务的状态,以及任务的执行时间,便能够简单快捷的实现定时任务的控制。
另外再额外配合开发一个页面做增删改查,便可以实现界面管理我们的定时任务调度

结语:

如上便是我们的springboot+Quartz通过数据库控制定时任务执行与时间具体的实现过程,如有遗漏后续会更新

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2055777.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Arthas相关命令

官方网站&#xff1a;命令列表 | arthas 也可以用idea的插件arthas-idea的插件根据你想定位的代码生成命令 jvm 相关 dashboard - 当前系统的实时数据面板getstatic - 查看类的静态属性heapdump - dump java heap, 类似 jmap 命令的 heap dump 功能jvm - 查看当前 JVM 的信息l…

Studying-刷题补充| 数组:58.区间和、44. 开发商购买土地

目录 58.区间和 44. 开发商购买土地 总结 58.区间和 文档讲解&#xff1a;代码随想录58.区间和 题目&#xff1a;58. 区间和&#xff08;第九期模拟笔试&#xff09; (kamacoder.com) 学习&#xff1a;本题最直接的做法是&#xff0c;将数组Array保存好后&#xff0c;通过…

linux - mathematica 安装教程

注意事项&#xff1a; 文件目录不能有空格&#xff0c;不能有中文 安装包 Mathematica - 12.1 安装 解压软件包 7z x Mathematica_12.1.1_LINUX_CN.zip运行安装器 命令运行后解压出Mathematica_12.1.1_LINUX_CN.sh, 运行该安装脚本 chmod x Mathematica_12.1.1_LINUX_CN…

STM32后备区域:读写BKP备份寄存器与使用RTC实时时钟详解

目录 STM32后备区域&#xff1a;读写BKP备份寄存器与使用RTC实时时钟详解 1 什么是STM32的后备区域 分割线* 2.1 BKP备份寄存器简介 2.2 BKP备份寄存器基本结构 2.3 BKP外设头文件 bkp.h介绍 2.4 读写 BKP备份寄存器 操作步骤 2.5 编写 读写备份寄存器 5.1 文件介绍 …

Centos7 系统下安装go语言开发环境

该文章简述在Centos7 amd64 系统中安装go开发环境的方法。 一、golang官网查看对应平台最新的golang版本 Golang 官网地址&#xff1a;All releases - The Go Programming Language 二、 安装GO的过程及相关命令 # 1、下载go&#xff0c;这里使用 go1.22.5 版本&#xff0c;可…

【ACL2024】基于长尾检索知识增强的大语言模型

近日&#xff0c;阿里云人工智能平台PAI与阿里集团安全部内容安全算法团队、华东师范大学何晓丰教授团队合作&#xff0c;在自然语言处理顶级会议ACL2024上发表论文《On the Role of Long-tail Knowledge in Retrieval Augmented Large Language Models》&#xff0c;论文主题为…

爆火游戏《黑神话:悟空》研发背后有哪些故事?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门面向中国程序员和企业提供企业级一体化 DevOps 平台&#xff0c;用来帮助用户实现需求管理、源代码托管、CI/CD、安全合规&#xff0c;而且所有的操作都是在一个平台上进行&#xff0c;省事省心省钱。可以一键安装极狐GitL…

美团的测试面试题,真的很难吗?

年前&#xff0c;我的一个粉丝留言给我说&#xff0c;他在面试美团的自动化测试岗的时候&#xff0c;不幸挂掉了。 越想越可惜&#xff0c;回想面试经过&#xff0c;好好总结了几个点&#xff0c;发现面试没过的主要原因是在几个关键的问题没有给到面试官想要的答案 美团的面…

寻访中国100家.NET中大企业 —— 第二站:苏州行

一&#xff1a;事情起因 在.NET圈里混了十多年&#xff0c;相信有不少人知道我专注于玩 .NET高级调试&#xff0c;如今技术上的硬实力还是能够解决市面上的一些疑难杂症&#xff0c;但软实力却在另一个极端&#xff0c;如&#xff08;人际交往&#xff0c;人情事故&#xff09…

[RCTF2015]EasySQL1

打开题目 点进去看看 注册个admin账户&#xff0c;登陆进去 一个个点开看&#xff0c;没发现flag 我们也可以由此得出结论&#xff0c;页面存在二次注入的漏洞&#xff0c;并可以大胆猜测修改密码的源代码 resoponse为invalid string的关键字是被过滤的关键字&#xff0c;Le…

氟化工特氟龙精馏装置:PFA氟化氢反应装置的应用

精馏装置是进行精馏的一种塔式气液接触装置。利用混合物中各组分具有不同的挥发度&#xff0c;即在同一温度下各组分的蒸气压不同这一性质&#xff0c;使液相中的轻组分&#xff08;低沸物&#xff09;转移到气相中。 实验精馏装置的组成 实验精馏装置通常由以下几部分组成&am…

Linux2.6设备驱动开发

一&#xff1a;Linux2.6驱动设备开发的特点 1&#xff1a;首先是属于字符型设备注册的方法之一 这种开发接口是在Linux2.6引入的&#xff0c;之前的版本不支持这种开发方式&#xff0c;也是目前最标准的开发方式。 2&#xff1a;Linux2.6的设备开发 不再去限制设备号&#xf…

(javaweb)SpringBootWeb案例(毕业设计)案例--部门管理

目录 1.准备工作 2.部门管理--查询功能 3.前后端联调 3.部门管理--新增功能 1.准备工作 mapper数据访问层相当于dao层 根据页面原型和需求分析出接口文档--前后端必须遵循这种规范 大部分情况下 接口文档由后端人员来编写 前后端进行交互基于restful风格接口 http的请求方式…

TypeScript学习笔记(二)——TypeScript 高级类型

目录 1. class 类 1.1 class 关键字的基本使用 1.2 类继承 1.3 类成员可见性 1.4 类成员只读修饰符 2. 类型兼容性 2.1 类型兼容性 2.2 接口兼容性 2.3 函数兼容性 3. 交叉类型 4. 泛型 4.1 创建泛型函数 4.2 泛型约束 4.3 多个泛型的类型变量约束 4.4 泛型接口…

【深度学习入门项目】基于支持向量机的手写数字识别

目录 导入必要的包1. 数据集2. 数据处理3. 训练过程4. 输出结果完整代码 本项目使用SVM训练模型&#xff0c;用于预测手写数字图片。 导入必要的包 numpy: 这个库是Python中常用的数学计算库。在这个项目中&#xff0c;我使用numpy来处理图像数据&#xff0c;将图像数据转换为…

FPGA开发——DS18B20读取温度并且在数码管上显示

一、简介 在上一篇文章中我们对于DS18B20的相关理论进行了详细的解释&#xff0c;同时也对怎样使用DS18B20进行了一个简单的叙述。在这篇文章我们通过工程来实现DS18B20的温度读取并且实现在数码管伤显示。 1、基本实现思路 根据不同时刻的操作&#xff0c;我们可以使用一个状…

基于vue框架的班级网站的设计与实现vg66m(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;班级,学生,班级活动,班级相册,班级开支,活动记录 开题报告内容 基于Vue框架的班级网站设计与实现 开题报告 一、引言 随着互联网技术的飞速发展&#xff0c;网络已经成为人们日常生活中不可或缺的一部分。在教育领域&#xff0c;班级…

大白话解析:深入浅出大模型RAG模块全解析

文章目录 什么是 RAG&#xff1f; 技术交流&资料通俗易懂讲解大模型系列 RAG模块化 什么是模块化RAG&#xff1f; 索引模块 块优化 滑动窗口从小到大元数据附加 结构化组织 层次化索引知识图谱文档组织 预检索模块 查询扩展 多查询子查询CoVe 查询转换 重写HyDE 查询路由…

TON链上游戏项目开发基本要求及模式创建与海外宣发策略

TON&#xff08;The Open Network&#xff09;是由Telegram开发的区块链平台&#xff0c;以其高速、低延迟、和高扩展性吸引了大量开发者和项目方。TON链上游戏项目作为一个新兴领域&#xff0c;结合了区块链技术和游戏产业&#xff0c;为用户提供了全新的游戏体验和经济激励。…

精益生产咨询:为企业量身定制的高效能蜕变计划!——张驰咨询

在当今这个快速变化、竞争激烈的市场环境中&#xff0c;企业如何保持持续的竞争优势&#xff0c;提高生产效率&#xff0c;降低成本&#xff0c;同时又能快速响应市场需求&#xff0c;成为了每一个企业家必须面对的重大课题。精益生产&#xff08;Lean Production&#xff09;作…