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,实现数据库方式执行定时任务。把定时任务信息存进数据库,项目启动后自动执行定时任务。 1.引入依赖包: <dependency> <groupId>org.springframework.boot</groupId> <ar…...
java中多个list怎么用List表示?
如果你有多个List对象,想要将它们合并成一个List对象,可以使用addAll()方法来实现。addAll()方法将会把一个List中的元素逐个添加到另一个List中。 以下是一个示例,展示了如何将多个List对象合并为一个List对象: 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转换成长图
语言:python 3 用法:点击运行后,弹出窗口,选择文件夹,程序运行会将文件夹内的所有PPT文件全部转换成PPT长图,图片名称与PPT文件名称相同,保存位置相同。 如运行中报错,需要自行根据…...
C++信息学奥赛2046:【例5.15】替换字母
这段代码的功能是对输入的字符串进行处理,将字符串中的字符 a 替换为字符 b 后输出结果。 #include<bits/stdc.h> using namespace std; int main() {string s; // 定义字符串变量s,用来存储输入的字符串char a, b; // 定义字符变量a和bÿ…...
每天一道leetcode:1306. 跳跃游戏 III(图论中等广度优先遍历)
今日份题目: 这里有一个非负整数数组 arr,你最开始位于该数组的起始下标 start 处。当你位于下标 i 处时,你可以跳到 i arr[i] 或者 i - arr[i]。 请你判断自己是否能够跳到对应元素值为 0 的 **任一** 下标处。 注意,不管是什…...
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方式一:通过set_event接口方式二:通过enable 跟踪trace信息 相关参考 SCSI日志调试功能 SCSI子系统支持内核选项CONFIG_SCSI_LOGGING配置日志调试…...
【Unity3D】水面特效
1 前言 水波特效 中通过屏幕后处理实现了环形水波效果,本文通过 Shader Graph 实现了模拟水面特效,包含以下特效细节。 深水区和浅水区颜色差异;水面有波纹,并且在移动;水面起伏波动;水面边缘有水泡&#…...
CSS中的flex布局详细讲解
Flex 布局 Flex 布局是一种现代的 CSS 布局模型,用于实现灵活的盒子布局。它提供了强大的布局能力,使得元素可以自动调整大小、对齐和分布,适用于构建响应式和可伸缩的布局。 Flex 布局使用 flex 容器和 flex 项目的概念。容器是一个父元素…...
Python功能制作之简单的音乐播放器
需要导入的库: pip install PyQt5 源码: 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具体的代码及结果导入工具包设置超参数定义优化器,以及损失函数训练时的迭代过程训练结果的展示 1.项目介绍 通过用minist数据集进行训练,得到一个GAN模型,可以生成与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++实现
题目链接: 二叉树遍历_牛客题霸_牛客网二叉树的前序、中序、后序遍历的定义: 前序遍历:对任一子树,先访问根,然后遍历其左子树,最。题目来自【牛客题霸】https://www.nowcoder.com/share/jump/43719512169…...
CSS笔记
介绍 CSS导入方式 三种方法都将文字设置成了红色 CSS选择器 元素选择器 id选择器 图中div将颜色控制为红色,#name将颜色控制为蓝色,谁控制的范围最小,谁就生效,所以第二个div是蓝色的。id属性值要唯一,否则报错。 clas…...
链栈Link-Stack
0、节点结构体定义 typedef struct SNode{int data;struct SNode *next; } SNode, *LinkStack; 1、初始化 bool InitStack(LinkStack &S) //S为栈顶指针(存数据的头节点) {S NULL;return true; } 2、入栈 bool Push(LinkStack &S, int e) {…...
Ubuntu 20系统WIFI设置静态IP地址,以及断连问题
最近工作需要购置了一台GPU机器,然后搭建了深度学习的运行环境,在工作中将这台机器当做深度学习的服务器来使用,前期已经配置好多用户以及基础环境。但最近通过xshell连接总是不间断的出现断连现象。 补充一点,Ubuntu系统中与网…...
(一)idea连接GitHub的全部流程(注册GitHub、idea集成GitHub、增加合作伙伴、跨团队合作、分支操作)
(二)Git在公司中团队内合作和跨团队合作和分支操作的全部流程(一篇就够)https://blog.csdn.net/m0_65992672/article/details/132336481 4.1、简介 Git是一个免费的、开源的*分布式**版本控制**系统*,可以快速高效地…...
-bash: java: command not found笔记
文章目录 场景解决方案找java的方法find命令进行查找根据java进程找寻具体位置 场景 linux系统执行java命令时报错: -bash: java: command not found。 解决方案 可能是没有安装java(这种情况比较少)或者安装了java但是没有设置环境变量(一般是这种情况)。 找ja…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
