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

news2024/12/27 15:18:00

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.运行结果:

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

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

相关文章

牛客刷题第二弹

ASCII 定义在类中的变量是类的成员变量&#xff0c;可以不进行初始化&#xff0c;Java会自动进行初始化&#xff0c;如果是引用类型默认初始化为null,如果是基本类型例如int则会默认初始化为0 局部变量是定义在方法中的变量&#xff0c;必须要进行初始化&#xff0c;否则不同…

论文阅读及复现——《CT_ICP: Real-time Elastic LiDAR Odometry with Loop Closure》

论文阅读之——《CT_ICP: Real-time Elastic LiDAR Odometry with Loop Closure》带闭环的实时弹性激光雷达里程计 1. 主要贡献2. 相关说明3. 激光里程计3.1 里程计公式构建3.2 局部地图与健壮性 4. 回环检测与后端5. 实验结果5.1 里程计实验结果5.2 回环检测实验结果 6. 总结…

构造函数内的方法 直接写在构造函数内部 与 写在prototype上 的区别

文章目录 前言区别总结 前言 以前没注意过, 去创建一个构造函数的时候, 方法都是直接写在函数内的. 在构造函数需要多次实例化的情况下有缺点, 不过幸好以前项目里的构造函数也不需要多次实例化, 缺点没有生效. 区别 为了比较, 先在构造函数内部直接书写方法, 查看实例化结果…

问题来了!你知道你穿的防砸劳保鞋的保护包头都是什么材料

防砸劳保鞋是较为常见的一种劳保鞋&#xff0c;用于作业过程中保护工人的脚&#xff0c;减少或避免被坠落物、重物砸伤或压伤脚部的工作鞋。防砸安全鞋鞋前头装有防护包头&#xff0c;具有耐压力和抗冲击性能。主要适用于矿山、机械、建筑、钢铁、冶金、运输等行业。 你穿的防砸…

NLog 使用

环境 .net 6.0 控制台程序 第三方库 NLog.Extensions.Logging Microsoft.Extensions.DependencyInjection 创建 NLog 配置文件 记得设置始终赋值&#xff0c;生成到发布文件夹 <?xml version"1.0" encoding"utf-8" ?> <nlog xmlns"h…

骨传导运动蓝牙耳机哪个牌子好?

生命在于运动&#xff0c;每次在运动时&#xff0c;搭配上音乐&#xff0c;可以极大程度让心情更为轻松和愉悦&#xff0c;可以说骨传导蓝牙耳机是爱运动的朋友的福音&#xff0c;相较于其他耳机来说&#xff0c;佩戴稳定不用担心出现脱落的情况&#xff0c;也不会出现传统耳机…

C++ STL迭代器适配器(详解)

STL迭代器适配器 C STL迭代器适配器是什么&#xff1f; 通过学习 C STL 标准库中的容器我们知道&#xff0c;无论是序列式容器还是关联式容器&#xff08;包括哈希容器&#xff09;&#xff0c;要想遍历容器中存储的数据&#xff0c;就只能用使用该容器模板类中提供的迭代器。…

搜狗拼音占用了VSCode及微信小程序开发者工具快捷键Ctrl + Shit + K 搜狗拼音截图快捷键

修改搜狗拼音的快捷键 右键--更多设置--属性设置--按键--系统功能快捷键--系统功能快捷键设置--取消Ctrl Shit K的勾选--勾选截屏并设置为Ctrl Shit A 微信开发者工具设置快捷键 右键--Command Palette--删除行 微信开发者工具快捷键 删除行&#xff1a;Ctrl Shit K 或…

世界上第一台无人机长啥样?

2023 世界上第一台无人机长啥样&#xff1f; 01 无人机&#xff08;英文通常为drone或unmanned aerial vehicle&#xff0c;简称为UAV&#xff09;即无人驾驶的飞机&#xff0c;是相对于载人飞机而言&#xff0c;它利用无线电遥控设备和自身的程序控制装置来完成空中的飞行任务…

通讯应用目标客户匹配,电商界都在用的营销技巧

马克扎克伯格&#xff08;Mark Zuckerburg&#xff09;对通讯应用说过一句话&#xff1a;未来是私有的。他暗示&#xff0c;社交互动的未来将在私人消息应用上。本文将帮助您使用消息应用目标客户匹配在你和现有客户之间打开一个消息应用沟通渠道。 什么是目标客户匹配&#xf…

【高危】WinRAR<6.23 远程代码执行漏洞 (CVE-2023-40477)

漏洞描述 WinRAR 是一款适用于 Windows 系统的压缩包管理器&#xff0c;其恢复卷功能用于修复损坏或丢失的压缩文件数据。 WinRAR 6.23之前版本的恢复卷功能未对用户提供的数据有效验证从而导致内存越界访问&#xff0c;攻击者可诱使用户使用 WinRAR 打开恶意文件&#xff0c…

【SkyWalking】分布式服务追踪与调用链系统

1、基本介绍 SkyWalking是一个开源的观测平台&#xff0c;官网&#xff1a;Apache SkyWalking&#xff1b; 可监控&#xff1a;分布式追踪调用链 、jvm内存变化、监控报警、查看服务器基本配置信息。 2、SkyWalking架构原理 在整个skywalking的系统中&#xff0c;有三个角色&am…

APP外包开发上线问题解决

在APP上线过程中可能会遇到各种问题&#xff0c;这些问题可能涉及技术、审核、发布等方面。可能会遇到各种挑战&#xff0c;但通过充分的准备、测试和持续的改进&#xff0c;可以解决大部分问题并提升应用的质量和用户满意度。以下是一些常见的问题及其解决方案&#xff0c;希望…

面试想拿13K,HR说你只值10K,该怎样反驳?

最近&#xff0c;有一个朋友跑来跟我吐槽说&#xff0c;刚刚一场面试&#xff0c;笔试面试都过了&#xff0c;部门领导对他也很满意&#xff0c;就只剩最后hr面谈了。以为只是走走流程&#xff0c;谈谈入职时间的事&#xff0c;没想到hr上来就说&#xff1a; 求职表上你写的期…

使用StorageClass动态创建pv

rook-ceph安装部署到位后&#xff0c;就可以开始来尝试使用StorageClass来动态创建pv了。 有状态的中间件在kubernetes上落地基本上都会用到StorageClass来动态创建pv&#xff08;对于云上应用没有那么多烦恼&#xff0c;云硬盘很好用&#xff0c;但是对于自己学习和练习来说还…

2023年护网总结报告

目录 一、 概况 二、 演习背景 三、 护网前期工作准备 3.1 成立工作小组 3.2 攻击面自查自检 3.3 制定演习方案 3.4 加强监测设施 四、 护网工作落实情况 4.1 严格落实值班制度 4.2 威胁情报收集 4.3 强化应急响应 4.4 严格管控 4.5 保障业务连续性 五、 演习评估…

开发第一个gPRC的开发

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

利用anaconda安装指定版本tensorflow的两种方法,并配置于Pycharm上

引言 作为一个跨专业到人工智能的小白&#xff0c;刚开始学习Deep learning时难免会遇到很多安装python开源库的问题&#xff0c;经过自己这段时间的摸索&#xff0c;总结出了两种安装tensorflow指定版本的方法&#xff08;可以衍生到安装其他python开源库&#xff0c;方法一样…

17.2.2 【Linux】通过systemctl观察系统上所有的服务

使用 systemctl list-unit-files 会将系统上所有的服务通通列出来&#xff5e;而不像 list-units 仅以 unit 分类作大致的说明。 至于 STATE 状态就是前两个小节谈到的开机是否会载入的那个状态项目。主要有 enabled / disabled / mask / static 等等。 假设我不想要知道这么多…

mybatis动态SQL的运用

一、mybatis动态SQL update 表名 set name?,age? where id? 如果我们的前台没有传参&#xff0c;比如没有传入我们的name值&#xff0c;name就会把字段值改为null&#xff0c;这就违背了我们编码的初衷。 许多人会使用类似于where 1 1 来作为前缀&#xff0c;在代码中会用i…