SpringBoot Quartz 定时服务
创始人
2025-05-30 19:12:53
0

开发环境: IDEA 2022.1.4+ MSSQL

                SpringBoot+Mybatis+Quartz

还在搞整application.yml集成Mybatis设置以及SQL查询,暂未找到满意资料,代码候补。

代码地址: 候补

目录

1. 概述

2. 依赖及设置

        2.1 Quartz表

        2.2 MSSQL单独建表

        2.3 Maven添加Quartz依赖

        2.4 Application.yml配置数据源、Quartz设置

3. 代码实现

        3.1 定义定时任务类参数QuartzJob

        3.2 定义带参任务类SysmJob

        3.3 定义任务接口IQuartzJobService

        3.4 定义任务接口QuartzJobServiceImpl

        3.5 定义空值类QuartzJobController

4. 使用POSTMAN测试

        4.1 新增-启动

         4.2 更新-启动

 5. 结语


1. 概述

        在项目开发中,服务后台经常用到定时服务处理数据,像前段时间做的涉医实名就诊接口,就需要定时上传数据,在当前学习SpringBoot的基础上,在网上了解下解决方案,觉得Quartz不错,就想搞一搞,在网上找到了某位大佬的源码(SpringBoot+Mybatis-Plus+Quartz),我本地使用的是Mybatis,不大想用Mybatis-Plus,就得借鉴下大佬源码进行学习。

        涉及定时任务,肯定有相关的接口参数,这类参数,就可以通过Quartz进行参数传递。最终效果如下:

          

        本文对主要实现进行简单说明:

2. 依赖及设置

        2.1 Quartz表

        Quartz带有自己的表结构,这个在网上有很多,可以在网上直接查找,表的结构说明也可以在网上查找。我本地使用MSSQL,就修改了下脚本,在MSSQL里执行。效果如下:

        

        2.2 MSSQL单独建表

        方便操作测试,就在MSSQL单独建表来存储定时任务信息。

SET ANSI_NULLS ON
GOSET QUOTED_IDENTIFIER ON
GOSET ANSI_PADDING ON
GOCREATE TABLE [dbo].[TB_QUARTZ_JOB]([id] [int] IDENTITY(1,1) NOT NULL,[createby] [varchar](32) NOT NULL,[createtime] [datetime] NOT NULL,[jobclassname] [varchar](255) NOT NULL,[cronexpression] [varchar](255) NOT NULL,[parameter] [varchar](255) NULL,[description] [varchar](255) NOT NULL,CONSTRAINT [PK_TB_QUARTZ_JOB] PRIMARY KEY CLUSTERED 
([id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]GOSET ANSI_PADDING OFF
GO

        2.3 Maven添加Quartz依赖

        org.springframework.bootspring-boot-starter-quartz2.6.5

        2.4 Application.yml配置数据源、Quartz设置

        quartz的job-store-type设置为jdbc,设置这个后,就需要设置数据源datasource, 定时任务的设置才能保存到Quartz表中。

        关于org.quartz.jonstore.class的设置有区别,spring 2.5.x之前是另一个设置,我当前是spring 2.6.5,就设置的这个。

spring:# quartz定时任务配置quartz:# 数据库存储方式job-store-type: jdbcorg:quartz:jobStore:class: org.springframework.scheduling.quartz.LocalDataSourceJobStore#配置数据源datasource:url: jdbc:sqlserver://127.0.0.1:1433;SelectMethod=cursor;databaseName=EFMISusername: sapassword: 123qwe,.driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver

3. 代码实现

        主要的类说明如下如箭头所示:

               

        3.1 定义定时任务类参数QuartzJob

        其实Quartz是带有基础表的,如果直接去查那些表,感觉有点麻烦。就直接新建一个表(2.2 MSSQL单独建表)来存储如下类信息。

        主要说明:

        1. jobclassname 是任务的全路径,比如"com.ceaning.crudp.quartz.SysmJob"(见上图结构)

        2. parameter参数,我想着使用json格式来存储,毕竟一个接口对应的参数是比较多的。

/*** 服务后端定时任务类*/
@Data
public class QuartzJob {/*** id 自增*/private int id;/*** 任务类名(当主键用)*/private String jobclassname;/*** 创建人*/private String createby;/*** corn表达式*/private String cronexpression;/*** 任务参数*/private String parameter;/*** 任务描述*/private String description;/*** 创建时间*/@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")private Date createtime;}

        3.2 定义带参任务类SysmJob

        此时方便看效果,任务里直接打印当前接口参数(json)。

@Slf4j
@DisallowConcurrentExecution//定义不能同时并发执行相同的JobDetail
public class SysmJob implements Job {@Autowiredprivate IQuartzJobService service;/*** 创建任务时候设置的json参数*/private String parameter;public void setParameter(String parameter) {this.parameter = parameter;}@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {System.out.println(CommonConstant.FMR_DATE_19.format(new Date())+ parameter);if (1==1){return;}}}

        3.3 定义任务接口IQuartzJobService

        接口还是按大佬的想法来整,新增-启动、更新-启动、删除-停止。我在整理Quartz接口时候,我发现有好些接口,比如 resume***、pause***等。 我就不想整的太复杂了, 简单点好。

public interface IQuartzJobService {/*** 新增并启动* @param quartzJob* @return*/boolean addAndScheduleJob(QuartzJob quartzJob);/*** 更新并启动* @param quartzJob* @return*/boolean updateAndScheduleJob(QuartzJob quartzJob) throws SchedulerException;/*** 删除并停止* @param quartzJob* @return*/boolean deleteAndStopJob(QuartzJob quartzJob) throws SchedulerException;
}

        3.4 定义任务接口QuartzJobServiceImpl

        这里要注意的是,schedulerAdd方法中,一句" usingJobData("parameter", parameter) ", 这个参数名必须和类SysmJob的属性parameter一样。如果不想把参数整合成一个json集合,就可以使用usingJonData进行多次加入,感觉有点麻烦了,一次性加入好点。

@Slf4j
@Service
public class QuartzJobServiceImpl implements IQuartzJobService {@Autowiredprivate Scheduler scheduler;/*** 根据任务类别获取类* @param jobclassname* @return* @throws Exception*/private static Job getClass(String jobclassname) throws Exception{Class cls= Class.forName(jobclassname);return (Job)cls.newInstance();}/*** 调度器中新增任务* @param jobclassname* @param cronexpression* @param parameter*/private boolean schedulerAdd(String jobclassname, String cronexpression, String parameter){boolean bRet= false;try{scheduler.start();JobDetail detail= JobBuilder.newJob(getClass(jobclassname).getClass()).withIdentity(jobclassname).usingJobData("parameter", parameter).build();CronScheduleBuilder scheduleBuilder= CronScheduleBuilder.cronSchedule(cronexpression);CronTrigger trigger= TriggerBuilder.newTrigger().withIdentity(jobclassname).withSchedule(scheduleBuilder).build();scheduler.scheduleJob(detail,trigger);bRet= true;} catch (SchedulerException e){e.printStackTrace();} catch (RuntimeException e){e.printStackTrace();} catch (Exception e){e.printStackTrace();}return bRet;}/*** 从调度器中删除任务* @param jobclassname*/private boolean schedulerDelete(String jobclassname){boolean bRet= false;try{scheduler.pauseTrigger(TriggerKey.triggerKey(jobclassname));scheduler.unscheduleJob(TriggerKey.triggerKey(jobclassname));scheduler.deleteJob(JobKey.jobKey(jobclassname));bRet= true;}catch (Exception e){log.error(e.getMessage(), e);}return bRet;}/*** 保存&启动定时任务* @param quartzJob* @return*/@Overridepublic boolean addAndScheduleJob(QuartzJob quartzJob) {boolean bRet= false;this.schedulerAdd(quartzJob.getJobclassname().trim(), quartzJob.getCronexpression().trim(), quartzJob.getParameter().trim());bRet= true;return bRet;}/*** 编辑&启动定时任务* @param quartzJob* @return*/@Overridepublic boolean updateAndScheduleJob(QuartzJob quartzJob) throws SchedulerException{boolean bRet= false;schedulerDelete(quartzJob.getJobclassname());bRet= schedulerAdd(quartzJob.getJobclassname().trim(), quartzJob.getCronexpression().trim(), quartzJob.getParameter());return bRet;}/*** 删除&停止定时任务* @param quartzJob* @return*/@Overridepublic boolean deleteAndStopJob(QuartzJob quartzJob) {return schedulerDelete(quartzJob.getJobclassname().trim());}}

        3.5 定义空值类QuartzJobController

@Slf4j
@RestController
@RequestMapping("/api")
public class QuartzJobController {@Autowiredprivate QuartzJobServiceImpl service;private boolean crud(SysEnum.crudopr opr, QuartzJob quartzJob){boolean bRet= false;int iRet= 0;SqlSession sqlSession= null;try{sqlSession= MybatisUtils.getSqlSession();QuartzJobMapper mapper= sqlSession.getMapper(QuartzJobMapper.class);switch (opr){case insert:iRet= mapper.add(quartzJob);break;case update:iRet= mapper.update(quartzJob);break;case delete:iRet= mapper.delete(quartzJob);break;default:iRet= 0;}if (iRet>0){sqlSession.commit();bRet= true;} else{sqlSession.rollback();}} catch (Exception e){e.printStackTrace();} finally {if (sqlSession!= null){sqlSession.close();}}return bRet;}@GetMapping("/task/getList")public Result getJobList(){Map> map= new HashMap>();List list= new ArrayList();SqlSession sqlSession= null;try{sqlSession= MybatisUtils.getSqlSession();QuartzJobMapper mapper= sqlSession.getMapper(QuartzJobMapper.class);list= mapper.getList();map.put("data", list);return Result.ok(map);} catch (Exception e){e.printStackTrace();} finally {if (sqlSession!= null){sqlSession.close();}}return Result.error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500,"操作失败!");}@GetMapping("/task/findById")public QuartzJob findById(int id){QuartzJob quartzJob= null;SqlSession sqlSession= null;try{sqlSession= MybatisUtils.getSqlSession();QuartzJobMapper mapper= sqlSession.getMapper(QuartzJobMapper.class);quartzJob= mapper.findById(id);} catch (Exception e){e.printStackTrace();} finally {if (sqlSession!= null){sqlSession.close();}}return quartzJob;}@PostMapping("/task/addAndScheduleJob")public Result addAndScheduleJob(@RequestBody QuartzJob quartzJob){boolean bRet= service.addAndScheduleJob(quartzJob);if (bRet) {bRet= crud(SysEnum.crudopr.insert, quartzJob);if (bRet) {return Result.ok(quartzJob);} else {if (crud(SysEnum.crudopr.delete,quartzJob)){return Result.error("[addJob].[addAndScheduleJob]操作失败!");} else {return Result.error("[addJob].[addAndScheduleJob].[crud]操作失败!");}}} else {return Result.error("[addJob].[crud]操作失败!");}}@PostMapping("/task/updateAndScheduleJob")public Result updateAndScheduleJob(@RequestBody QuartzJob quartzJob) throws SchedulerException{boolean bRet= false;QuartzJob job= findById(quartzJob.getId());if (job== null){return Result.error("[updateJob].[findById]无有效记录!");}//优先处理服务bRet= service.updateAndScheduleJob(quartzJob);if (bRet) {//服务启动成功后 才允许修改业务数据if (crud(SysEnum.crudopr.update, quartzJob)) {return Result.ok("操作成功!");} else {return Result.ok("[updateAndScheduleJob].[crud]操作失败!");}} else {return Result.error("[updateAndScheduleJob]操作失败!");}}@PostMapping("/task/deleteAndStopJob")public Result deleteAndStopJob(@RequestBody QuartzJob quartzJob){boolean bRet= false;QuartzJob job= findById(quartzJob.getId());if (job== null){return Result.error("[deleteJob].[findById]无有效记录!");}//优先处理服务bRet= service.deleteAndStopJob(quartzJob);if (bRet) {//服务启动成功后 才允许删除业务数据if (crud(SysEnum.crudopr.delete, quartzJob)) {return Result.ok("操作成功!");} else {return Result.ok("[deleteJob].[deleteAndStopJob].[crud]操作失败!");}} else {return Result.error("[deleteJob].[deleteAndStopJob]操作失败!");}}}

4. 使用POSTMAN测试

        4.1 新增-启动

        新增任务的Cron表达式设置为一分钟一次执行

         4.2 更新-启动

        更新任务的Cron表达式修改为两分钟一次执行

 

 5. 结语

        对Quartz进行简单的认识及使用。

相关内容

热门资讯

路透解析“马斯克集团”:Spa... SpaceX 凤凰网科技讯 北京时间1月31日,据路透社报道,长期以来,埃隆·马斯克(Elon Mu...
启动“二改” 永辉在京完成21... 北京商报讯(记者 赵述评 实习记者 毛思怡)1月31日,永辉超市北京龙湖长楹天街店经一个多月闭店调改...
《宜宾散装白酒连锁经营规范》团... 近日,由宜宾市酒类协会牵头归口、宜宾安宁酒厂主导起草,四川谊宾酒业、宜宾学院、劲牌南溪酒业等多家本地...
印度牙医博士打造全印首款人形机... 2026 年 1 月 23 日,印度浦那的 Muks Robotics 正式宣布,自主研发的社交人形...
金银价创新高,引发全球“贵金属... 【环球时报记者 倪浩 环球时报特约记者 甄翔】连日来,国际市场金银价格持续大涨。1月29日当天,亚太...
财经观察丨“爱你老己”背后的消... 新华网北京1月31日电岁末年初,一句“爱你老己,明天见”席卷社交网络,成为年轻人自我关怀的新表达。热...
重磅!珠海科技产业集团与农行广... 1月30日,珠海科技产业集团与中国农业银行广东省分行在广州签署全面战略合作协议暨独立授信合作。农行广...
原创 黄... 谁能想到,2026年开年就上演金融魔幻现实主义! 国际黄金1月31日凌晨暴跌9.25%,盘中狂泻12...
云南省本级社会保险基金银行存款... 近日,云南省财政厅、云南省人力资源和社会保障厅、云南省医疗保障局联合印发《云南省本级社会保险基金银行...
病毒在身体里“安家”却相安无事... 很多人听说“乙肝携带者”,总会下意识和“乙肝患者”画上等号,担心自己或身边人被传染,也害怕携带者最终...
库迪确认:取消全场9.9元 来源:滚动播报 (来源:新消费日报) 有消息称,库迪咖啡发布门店价格策略和活动调整通知。通知指出,...
原创 雷... 不知道大家有没有发现,这个周六可能是进入2026年之后最消停的一个周六。因为各品牌基本上都没什么大事...
原创 特... 特朗普对委内瑞拉的举动,表面上看是一场能源棋局,实则背后隐藏着深刻的战略考量。对他而言,掌握能源就意...
原创 李... 01、“私募魔女”李蓓再引争议 半夏投资创始人、“私募魔女”李蓓,最近又成为投资圈的焦点。 1月2...
爱美客:AestheFill产... 上证报中国证券网讯(记者 王子霖)备受医美行业瞩目的AestheFill产品独家经销权纠纷迎来重要进...
雷军明晚直播,在北京小米汽车工... IT之家 1 月 31 日消息,今天午间,小米创办人、董事长兼 CEO 雷军在微博发文宣布,2 月 ...
字节阿里DeepSeek决战春... 新智元报道 编辑:艾伦 【新智元导读】这个春节,中国 AI 迎来「决战时刻」。据《The Info...
皇台酒业开始过年? 富凯摘要:有钱没钱喝酒过年。 作者|欧文 1月30日,白酒板块再现分化行情,皇台酒业却延续强势表现,...
深交所修订可持续发展报告编制指... 上证报中国证券网讯 据深交所1月30日消息,深交所发布实施《深圳证券交易所上市公司自律监管指南第3号...
面试餐饮|新手零经验,小红书开... 有没有餐饮人跟我一样?想靠小红书引流拓客,却卡在第一步:不知道怎么开店、怎么发笔记不踩雷,看着别人的...