SpringBoot Quartz 定时服务

开发环境: 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 地址:GitHub - ningzaichun/springboot-quartz: springboot-quartz),我本地使用的是Mybatis,不大想用Mybatis-Plus,就得借鉴下大佬源码进行学习。

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

          

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

2. 依赖及设置

        2.1 Quartz表

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

        

        2.2 MSSQL单独建表

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

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE 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]

GO

SET ANSI_PADDING OFF
GO

        2.3 Maven添加Quartz依赖

        <!-- 开启quartz  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
            <version>2.6.5</version>
        </dependency>

        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: jdbc
    org:
      quartz:
        jobStore:
          class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
  #配置数据源
  datasource:
    url: jdbc:sqlserver://127.0.0.1:1433;SelectMethod=cursor;databaseName=EFMIS
    username: sa
    password: 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 {

    @Autowired
    private IQuartzJobService service;

    /**
     * 创建任务时候设置的json参数
     */
    private String parameter;

    public void setParameter(String parameter) {
        this.parameter = parameter;
    }

    @Override
    public 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 {

    @Autowired
    private 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
     */
    @Override
    public 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
     */
    @Override
    public 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
     */
    @Override
    public boolean deleteAndStopJob(QuartzJob quartzJob) {
        return schedulerDelete(quartzJob.getJobclassname().trim());
    }

}

        3.5 定义空值类QuartzJobController

@Slf4j
@RestController
@RequestMapping("/api")
public class QuartzJobController {

    @Autowired
    private 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<String,List<QuartzJob>> map= new HashMap<String,List<QuartzJob>>();
        List<QuartzJob> list= new ArrayList<QuartzJob>();
        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进行简单的认识及使用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot Quartz定时任务是一种在Spring Boot项目中实现定时任务调度的方法。它可以根据设定的时间间隔或者Cron表达式来执行任务。通过引用中提供的源代码和配置文件,可以直接导入Spring Boot项目并配置好数据库即可使用。 为了实现定时任务,需要在项目中创建一个继承自QuartzJobBean的Job类,并重写executeInternal方法。该方法定义了定时任务的具体逻辑,可以在其中执行需要定时执行的业务操作。 另外,还需要创建一个Quartz定时任务的配置类,其中使用@Configuration注解标记为配置类,配置相关的定时任务信息。在该配置类中,可以通过@Bean注解创建JobDetail和Trigger实例,分别定义任务的具体细节和触发器的配置信息。在这个配置类中,我提供了一个示例,用于演示如何配置一个定时任务。 综上所述,Spring Boot Quartz定时任务是一种灵活可靠的定时任务调度方法,可以通过Spring Boot的特性方便地实现任务的定时执行。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [springboot+quartz定时任务实现 纯代码](https://download.csdn.net/download/qq_38971617/12284286)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [SpringBoot整合Quartz实现定时任务](https://blog.csdn.net/qq_29305715/article/details/123517569)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值