当前位置: 首页 > news >正文

SpringBoot整合Quartz,实现数据库方式执行定时任务

springboot整合quartz,实现数据库方式执行定时任务。把定时任务信息存进数据库,项目启动后自动执行定时任务。

1.引入依赖包:

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

2.配置yml:

spring:
  # quartz
  quartz:
    #相关属性配置
    properties:
      org:
        quartz:
          scheduler:
            instanceName: DefaultQuartzScheduler
            instanceId: AUTO
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix: QRTZ_
            isClustered: false
            clusterCheckinInterval: 10000
            #以指示JDBCJobStore将JobDataMaps中的所有值都作为字符串,因此可以作为名称
            #- 值对存储而不是在BLOB列中以其序列化形式存储更多复杂的对象。
            #从长远来看,这是更安全的,因为您避免了将非String类序列化为BLOB的类版本问题。
            useProperties: false
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true
 
    # 数据库方式
    job-store-type: jdbc
 
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/quartz_test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
 
server:
  port: 8080

3.后端编码:

controller层:

@Controller
@RequestMapping(value = "/job")
@Slf4j
public class JobController {
 
    @Autowired
    private IJobService jobService;
 
 
 
    @PostMapping(value = "/add")
    @ResponseBody
    public Result addJob(@RequestBody JobInfo jobInfo) {
        return jobService.addJob(jobInfo);
    }
 
    @GetMapping(value = "/getAllJobs")
    @ResponseBody
    public List<JobInfo> getAllJobs() {
        return jobService.getAllJobs();
    }
 
    @PostMapping(value = "/pause")
    @ResponseBody
    public Result pauseJob(String name, String group) {
        return jobService.pauseJob(name,group) ? Result.success() : Result.error();
    }
 
    @PostMapping(value = "/resume")
    @ResponseBody
    public Result resumeJob(String name, String group) {
        return jobService.resumeJob(name,group) ? Result.success() : Result.error();
    }
 
    @PostMapping(value = "/reschedule")
    @ResponseBody
    public Result reScheduleJob(String name, String group, String cron) {
        return jobService.reScheduleJob(name, group, cron) ? Result.success() : Result.error();
    }
 
    @PostMapping(value = "/delete")
    @ResponseBody
    public Result deleteJob(String name, String group) {
        return jobService.deleteJob(name,group) ? Result.success() : Result.error();
    }
 
    /**
     * @description 校验是否是合法cron表达式
     * @param  cron
     * @return  com.cxh.cxhquartz.common.Result
     **/
    @PostMapping(value = "/checkCron")
    @ResponseBody
    public Result checkCron(String cron) {
        boolean valide = false;
        try {
            valide = CronExpression.isValidExpression(cron);
        }catch (Exception e){
            log.error(e.getMessage());
        }
        return valide ? Result.success() : Result.error();
    }
}

service层:

public interface IJobService {
 
    /**
     * @description 查询所有任务
     * @param
     * @return  java.util.List<com.cxh.cxhquartz.quartz.JobInfo>
     **/
     List<JobInfo> getAllJobs();
    /**
     * @description 恢复任务
     * @param  jobName
     * @Param  jobGroup
     * @return  boolean
     **/
     boolean resumeJob(String jobName,String jobGroup);
    /**
     * @description 停止任务
     * @param  jobName
     * @Param  jobGroup
     * @return  boolean
     **/
     boolean pauseJob(String jobName,String jobGroup);
    /**
     * @description 修改任务执行周期表达式
     * @param  jobName
     * @Param  jobGroup
     * @Param  cronExpression
     * @return  boolean
     **/
     boolean reScheduleJob(String jobName,String jobGroup,String cronExpression);
    /**
     * @description 删除任务
     * @param  jobName
     * @Param  jobGroup
     * @return  boolean
     **/
     boolean deleteJob(String jobName,String jobGroup);
    /**
     * @description 新增任务
     * @param  jobInfo
     * @return  int
     **/
    Result addJob(JobInfo jobInfo);
     /**
      * @description 判断任务是否存在
      * @param  jobKey
      * @return  int
      **/
     int isJobExist(JobKey jobKey);
}

@Slf4j
@Service
public class JobServiceImpl implements IJobService {
 
    @Autowired
    private Scheduler scheduler;
 
    @Override
    public List<JobInfo> getAllJobs() {
        List<JobInfo> jobInfoList = new ArrayList<>();
        try{
            List<String> groupList = scheduler.getJobGroupNames();
            for(String group : groupList){
                GroupMatcher<JobKey> groupMatcher = GroupMatcher.groupEquals(group);
                Set<JobKey> jobKeySet = scheduler.getJobKeys(groupMatcher);
                for(JobKey jobKey : jobKeySet){
                    JobInfo jobInfo = new JobInfo();
                    JobDetail jobDetail = scheduler.getJobDetail(jobKey);
                    jobInfo.setJobname(jobKey.getName());
                    jobInfo.setJobgroup(jobKey.getGroup());
                    jobInfo.setJobclassname(jobDetail.getJobClass().getName());
                    Trigger trigger = scheduler.getTrigger(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup()));
                    if(trigger != null){
                        Trigger.TriggerState state = scheduler.getTriggerState(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup()));
                        jobInfo.setTriggername(jobKey.getName());
                        jobInfo.setTriggergroup(jobKey.getGroup());
                        try{
                            CronTrigger cronTrigger = (CronTrigger) trigger;
                            jobInfo.setCronexpression(cronTrigger.getCronExpression());
                        } catch (Exception e){
                            log.error(e.getMessage());
                        }
                        if(trigger.getNextFireTime() != null){
                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                            jobInfo.setNextfiretime(sdf.format(trigger.getNextFireTime()));
                        }
                        jobInfo.setDescription(jobDetail.getDescription());
                        jobInfo.setState(state.name());
                        jobInfo.setId(UUID.randomUUID().toString());
                        jobInfoList.add(jobInfo);
                    } else {
                        jobInfo.setState("OVER");
                        jobInfo.setId(UUID.randomUUID().toString());
                        jobInfoList.add(jobInfo);
                    }
                }
            }
        } catch (Exception e){
            log.error(e.getMessage());
        }
        return jobInfoList;
    }
 
    @Override
    public boolean resumeJob(String jobName, String jobGroup) {
        boolean flag = true;
        try{
            scheduler.resumeJob(JobKey.jobKey(jobName, jobGroup));
        } catch (Exception e){
            flag = false;
            log.error(e.getMessage());
        }
        return flag;
    }
 
    @Override
    public boolean pauseJob(String jobName, String jobGroup) {
        boolean flag = true;
        try{
            scheduler.pauseJob(JobKey.jobKey(jobName, jobGroup));
        } catch (Exception e){
            flag = false;
            log.error(e.getMessage());
        }
        return flag;
    }
 
    @Override
    public boolean reScheduleJob(String jobName, String jobGroup, String cronExpression) {
        boolean flag = true;
        try{
           Trigger.TriggerState state = scheduler.getTriggerState(TriggerKey.triggerKey(jobName, jobGroup));
           CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(TriggerKey.triggerKey(jobName, jobGroup));
           if(cronTrigger != null && cronExpression != null && !cronTrigger.getCronExpression().equals(cronExpression)){
               CronTrigger cronTriggerNew = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)).build();
               scheduler.rescheduleJob(TriggerKey.triggerKey(jobName, jobGroup), cronTriggerNew);
               if(state.name().equals("PAUSED")){
                   this.pauseJob(jobName, jobGroup);
               }
           }
        } catch (Exception e){
            flag = false;
            log.error(e.getMessage());
        }
        return flag;
    }
 
    @Override
    public boolean deleteJob(String jobName, String jobGroup) {
        boolean flag = true;
        try{
            List<? extends Trigger> triggerList = scheduler.getTriggersOfJob(JobKey.jobKey(jobName, jobGroup));
            if(triggerList.size() > 0){
                if(!"PAUSED".equals(scheduler.getTriggerState(TriggerKey.triggerKey(jobName, jobGroup)).name())){
                    scheduler.pauseTrigger(TriggerKey.triggerKey(jobName, jobGroup));
                }
                scheduler.unscheduleJob(TriggerKey.triggerKey(jobName, jobGroup));
            }
            scheduler.deleteJob(JobKey.jobKey(jobName, jobGroup));
        } catch (Exception e){
            flag = false;
            log.error(e.getMessage());
        }
        return flag;
    }
 
    @Override
    public Result addJob(JobInfo jobInfo) {
        int isJobExist = this.isJobExist(JobKey.jobKey(jobInfo.getJobname(), jobInfo.getJobgroup()));
        if(isJobExist == 1){
            return Result.error("任务已存在!");
        }
        try{
            JobDetail jobDetail = null;
            if(isJobExist == 0){
                jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobInfo.getJobname(), jobInfo.getJobgroup()));
            } else if(isJobExist == -1){
                jobDetail = JobBuilder.newJob(
                        (Class<? extends Job>) Class.forName(jobInfo.getJobclassname()))
                        .withIdentity(jobInfo.getJobname(), jobInfo.getJobgroup())
                        .withDescription(jobInfo.getDescription())
                        .storeDurably().build();
            }
            //如果jobInfo的cron表达式为空,则创建常规任务,反之创建周期任务
            if(jobInfo.getCronexpression() != null){
                CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobInfo.getTriggername(), jobInfo.getTriggergroup())
                        .withSchedule(CronScheduleBuilder.cronSchedule(jobInfo.getCronexpression()))
                        .build();
                scheduler.scheduleJob(jobDetail, cronTrigger);
 
            } else {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobInfo.getJobname(), jobInfo.getJobgroup())
                        .startAt(sdf.parse(jobInfo.getNextfiretime()))
                        .withSchedule(SimpleScheduleBuilder.simpleSchedule().withRepeatCount(0))
                        .build();
                scheduler.scheduleJob(jobDetail, trigger);
            }
        } catch (SchedulerException e){
            log.error(e.getMessage());
            return Result.error("添加任务失败!");
        } catch (ParseException e){
            log.error(e.getMessage());
            return Result.error("时间转换出错!");
        } catch (Exception e){
            log.error(e.getMessage());
            return Result.error(e.getMessage());
        }
        return Result.success("添加任务成功!");
    }
 
    @Override
    public int isJobExist(JobKey jobKey) {
       int flag = -1;
       try{
           JobDetail jobDetail = scheduler.getJobDetail(jobKey);
           List<? extends  Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
           if(jobDetail != null && triggers.size() > 0){
               flag = 1;
           } else if(jobDetail != null && triggers.size() == 0){
                flag = 0;
           }
       } catch (Exception e){
           flag = -1;
           log.error(e.getMessage());
       }
       return flag;
    }
}

entity层:

@Data
public class JobInfo implements Serializable {
    private String id;
    private String jobname;
    private String jobgroup;
    private String jobclassname;
    private String triggername;
    private String triggergroup;
    private String cronexpression;
    private String description;
    private String prefiretime;
    private String nextfiretime;
    private String state;
}

定时任务:

@Slf4j
public class DemoJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobDetail jobDetail = jobExecutionContext.getJobDetail();
        log.info("任务名:" + jobDetail.getKey().getName() + ",组名:" +
                jobDetail.getKey().getGroup() + "------执行的定时任务工作内容!");
    }
}

4.创建数据库表:quartz保存定时任务信息表

  5.运行项目,打开postman发起请求:

 6.运行结果:

相关文章:

SpringBoot整合Quartz,实现数据库方式执行定时任务

springboot整合quartz&#xff0c;实现数据库方式执行定时任务。把定时任务信息存进数据库&#xff0c;项目启动后自动执行定时任务。 1.引入依赖包&#xff1a; <dependency> <groupId>org.springframework.boot</groupId> <ar…...

java中多个list怎么用List表示?

如果你有多个List对象&#xff0c;想要将它们合并成一个List对象&#xff0c;可以使用addAll()方法来实现。addAll()方法将会把一个List中的元素逐个添加到另一个List中。 以下是一个示例&#xff0c;展示了如何将多个List对象合并为一个List对象&#xff1a; import java.ut…...

postgresql 数据排序

postgresql 常见操作 排序总结 排序 -- 排序的时候null是最大的值(看一下) select employee_id,manager_id from employeesorder by manager_id desc;-- nulls first使null值排在第一位 select employee_id,manager_id from employeesorder by manager_id nulls first;-- null…...

虚拟机 net、桥接、主机三种网络模式寻根问底

虚拟机使用物理主机上的网络适配器直接连接到物理网络中。 这意味着虚拟机就像是通过网线直接连接到路由器一样,成为物理网络中的一个独立设备。 虚拟机可以获取一个永久的IP地址,通过DHCP或手动设置。 虚拟机和物理主机都可以访问对方以及公共网络中的其他设备,比如文件服务…...

python代码——批量将PPT转换成长图

语言&#xff1a;python 3 用法&#xff1a;点击运行后&#xff0c;弹出窗口&#xff0c;选择文件夹&#xff0c;程序运行会将文件夹内的所有PPT文件全部转换成PPT长图&#xff0c;图片名称与PPT文件名称相同&#xff0c;保存位置相同。 如运行中报错&#xff0c;需要自行根据…...

C++信息学奥赛2046:【例5.15】替换字母

这段代码的功能是对输入的字符串进行处理&#xff0c;将字符串中的字符 a 替换为字符 b 后输出结果。 #include<bits/stdc.h> using namespace std; int main() {string s; // 定义字符串变量s&#xff0c;用来存储输入的字符串char a, b; // 定义字符变量a和b&#xff…...

每天一道leetcode:1306. 跳跃游戏 III(图论中等广度优先遍历)

今日份题目&#xff1a; 这里有一个非负整数数组 arr&#xff0c;你最开始位于该数组的起始下标 start 处。当你位于下标 i 处时&#xff0c;你可以跳到 i arr[i] 或者 i - arr[i]。 请你判断自己是否能够跳到对应元素值为 0 的 **任一** 下标处。 注意&#xff0c;不管是什…...

76参考链接

参考链接 官方文件综合介绍[let 和 const](https://es6.ruanyifeng.com/#docs/reference#let 和 const)解构赋值字符串正则数值数组函数对象Symbol[Set 和 Map](https://es6.ruanyifeng.com/#docs/reference#Set 和 Map)[Proxy 和 Reflect](https://es6.ruanyifeng.com/#docs/…...

浅析Linux SCSI子系统:调试方法

文章目录 SCSI日志调试功能scsi_logging_level调整SCSI日志等级 SCSI trace events使能SCSI trace events方式一&#xff1a;通过set_event接口方式二&#xff1a;通过enable 跟踪trace信息 相关参考 SCSI日志调试功能 SCSI子系统支持内核选项CONFIG_SCSI_LOGGING配置日志调试…...

【Unity3D】水面特效

1 前言 水波特效 中通过屏幕后处理实现了环形水波效果&#xff0c;本文通过 Shader Graph 实现了模拟水面特效&#xff0c;包含以下特效细节。 深水区和浅水区颜色差异&#xff1b;水面有波纹&#xff0c;并且在移动&#xff1b;水面起伏波动&#xff1b;水面边缘有水泡&#…...

CSS中的flex布局详细讲解

Flex 布局 Flex 布局是一种现代的 CSS 布局模型&#xff0c;用于实现灵活的盒子布局。它提供了强大的布局能力&#xff0c;使得元素可以自动调整大小、对齐和分布&#xff0c;适用于构建响应式和可伸缩的布局。 Flex 布局使用 flex 容器和 flex 项目的概念。容器是一个父元素…...

Python功能制作之简单的音乐播放器

需要导入的库&#xff1a; pip install PyQt5 源码&#xff1a; import os from PyQt5.QtCore import Qt, QUrl from PyQt5.QtGui import QIcon, QPixmap from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent from PyQt5.QtWidgets import QApplication, QMainWind…...

GAN生成对抗模型根据minist数据集生成手写数字图片

文章目录 1.项目介绍2相关网站3具体的代码及结果导入工具包设置超参数定义优化器&#xff0c;以及损失函数训练时的迭代过程训练结果的展示 1.项目介绍 通过用minist数据集进行训练&#xff0c;得到一个GAN模型&#xff0c;可以生成与minist数据集类似的图片。 GAN是一种生成模…...

【K8S源码之Pod漂移】整体概况分析 controller-manager 中的 nodelifecycle controller(Pod的驱逐)

参考 k8s 污点驱逐详解-源码分析 - 掘金 k8s驱逐篇(5)-kube-controller-manager驱逐 - 良凯尔 - 博客园 k8s驱逐篇(6)-kube-controller-manager驱逐-NodeLifecycleController源码分析 - 良凯尔 - 博客园 k8s驱逐篇(7)-kube-controller-manager驱逐-taintManager源码分析 - 良…...

[保研/考研机试] KY212 二叉树遍历 华中科技大学复试上机题 C++实现

题目链接&#xff1a; 二叉树遍历_牛客题霸_牛客网二叉树的前序、中序、后序遍历的定义&#xff1a; 前序遍历&#xff1a;对任一子树&#xff0c;先访问根&#xff0c;然后遍历其左子树&#xff0c;最。题目来自【牛客题霸】https://www.nowcoder.com/share/jump/43719512169…...

CSS笔记

介绍 CSS导入方式 三种方法都将文字设置成了红色 CSS选择器 元素选择器 id选择器 图中div将颜色控制为红色&#xff0c;#name将颜色控制为蓝色&#xff0c;谁控制的范围最小&#xff0c;谁就生效&#xff0c;所以第二个div是蓝色的。id属性值要唯一&#xff0c;否则报错。 clas…...

链栈Link-Stack

0、节点结构体定义 typedef struct SNode{int data;struct SNode *next; } SNode, *LinkStack; 1、初始化 bool InitStack(LinkStack &S) //S为栈顶指针&#xff08;存数据的头节点&#xff09; {S NULL;return true; } 2、入栈 bool Push(LinkStack &S, int e) {…...

Ubuntu 20系统WIFI设置静态IP地址,以及断连问题

​最近工作需要购置了一台GPU机器&#xff0c;然后搭建了深度学习的运行环境&#xff0c;在工作中将这台机器当做深度学习的服务器来使用&#xff0c;前期已经配置好多用户以及基础环境。但最近通过xshell连接总是不间断的出现断连现象。 补充一点&#xff0c;Ubuntu系统中与网…...

(一)idea连接GitHub的全部流程(注册GitHub、idea集成GitHub、增加合作伙伴、跨团队合作、分支操作)

&#xff08;二&#xff09;Git在公司中团队内合作和跨团队合作和分支操作的全部流程&#xff08;一篇就够&#xff09;https://blog.csdn.net/m0_65992672/article/details/132336481 4.1、简介 Git是一个免费的、开源的*分布式**版本控制**系统*&#xff0c;可以快速高效地…...

-bash: java: command not found笔记

文章目录 场景解决方案找java的方法find命令进行查找根据java进程找寻具体位置 场景 linux系统执行java命令时报错&#xff1a; -bash: java: command not found。 解决方案 可能是没有安装java(这种情况比较少)或者安装了java但是没有设置环境变量(一般是这种情况)。 找ja…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术&#xff1a;基于互相关的相干体技术&#xff08;Correlation&#xff09;第二代相干体技术&#xff1a;基于相似的相干体技术&#xff08;Semblance&#xff09;基于多道相似的相干体…...

IP如何挑?2025年海外专线IP如何购买?

你花了时间和预算买了IP&#xff0c;结果IP质量不佳&#xff0c;项目效率低下不说&#xff0c;还可能带来莫名的网络问题&#xff0c;是不是太闹心了&#xff1f;尤其是在面对海外专线IP时&#xff0c;到底怎么才能买到适合自己的呢&#xff1f;所以&#xff0c;挑IP绝对是个技…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...