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进行简单的认识及使用。

相关内容

热门资讯

走进小城看消费丨江西资溪:低碳...   夏日时节下午4点,江西省抚州市资溪县大觉山景区漂流终点依然热闹。来自南昌的游客余鑫漂流结束后没有...
【中原晨会0625】市场分析专... 来源:市场资讯 (来源:中原证券研究所) 本期重点研报目录 【中原策略】市场分析:电子半导体领涨 ...
南向资金连买4日!低费率+可月... 6月25日早盘,港股红利资产震荡整理。截至11时14分,港股红利低波ETF招商(520550)下跌0...
618成交破百万!紫荆花用一套... 一年一度的618年中大促,是消费市场的晴雨表,也是品牌间最激烈的角力场。当各大品牌在直播间里铆足了劲...
原创 黄... 2026年6月25日的国际金价已经从前期的5500美元高点跌到4200美元下方,累计跌幅超过22%,...
英伟达CEO:Vera Rub... 截至9:38,中证半导体材料设备主题指数(931743)涨2.36%创新高;权重股中,中微公司涨3....
再被催债16亿!“钢铁大王”戴... 澎湃新闻记者 贺梨萍 因“铁本事件”入狱五年的戴国芳重返钢铁行业,但他并没有完成从阶下囚再到“钢铁大...
周三原油价格下跌 随着美国和伊朗在和平谈判中取得进展,越来越多的油轮公开穿越霍尔木兹海峡,原油在战时的价格上涨已经蒸发...
这种蛋白是大脑衰老的开关 这种蛋白是大脑衰老的开关 清晨,假设一位五十岁左右的王女士发现自己常常把手机放在熟悉的抽屉里又找不到...
信通院牵头算力Token出海生... 盘面上,截至11:04,中证科创创业50指数(931643)涨1.68%,创历史新高;权重股中,芯原...
海外 774 亿营收背后:日本... 文 | 游戏价值论 6月23日,彭博社报道了腾讯正在围绕出售多家日本游戏工作室少数股权开展谈判,包...
餐饮“抢人”大战:把店开到公交... 作者 |餐饮老板内参 内参君 医院、公交站、演唱会…餐饮品牌,正在无孔不入 在北京儿童医院,肯德基...
快讯 | 外资扫货!陈翊庭:港... 港交所行政总裁陈翊庭在接受《中国证券报》专访时指出,国际资本对中国资产的看法已彻底扭转,布局中国市场...
2777.77元!A股“股王”... 25日早盘,昨天创下历史新高的A股“股王”联讯仪器,今天上午继续走强,盘中股价再度刷新历史新高。 截...
原创 今... 欧洲自己的媒体直接下结论,欧盟衰退躲不掉,内部分裂拦不住,现在就连欧洲顶尖工业巨头,都偷偷在用中国的...
黄仁勋股东大会放言:本轮AI基... 在当地时间6月24日的英伟达(NVDA.O)2026年度股东大会上,股东批准了该公司全部10名董事会...
国际油价大跌 新华社消息, 纽约原油期货主力合约价格24日盘中跌破每桶70美元,为伊朗战事爆发以来首次。 市场分析...
马云带队插秧,什么信号? 一场别开生面的“务农”,让外界看到了一个不一样的阿里巴巴。 近日,阿里巴巴合伙人、高德董事长刘振飞在...
全球最大产能,最高丰度达99.... 本文转自【科技日报】; 6月23日,高丰度硼-10同位素技术暨产业化成果发布会在山东省东营市举办,全...
黄金大跳水!金饰克价年内暴跌近... 25日,现货黄金盘中震荡,截至发稿,报3985.070美元/盎司,跌0.17%。 当地时间24日,...