美文网首页
springboot整合quartz应用

springboot整合quartz应用

作者: 志华2020 | 来源:发表于2023-03-09 11:30 被阅读0次

    一、说明

        Quartz 是一个开源的作业调度框架,可基于内存或数据库的方式实现定时任务。基于内存一般实现简单的定时任务,比较适合不经常修改定时配置的时候使用,无法做到动态的修改定时任务。
        基于数据库实现定时任务的方式可实现动态修改定时任务,定时任务的任务信息会持久化到数据库,也支持集群。
    

    二、基于内存的定时

    • 项目代码结构
    springboot-quartz-simple
    │ pom.xml
    └─src
        └─main
            ├─java
            │  └─com
            │      └─hzhh123
            │          │  QuartzSimpleApplication.java
            │          │
            │          ├─config
            │          │      QuartzJobConfig.java
            │          │
            │          └─job
            │                  SimpleJob.java
            │
            └─resources
                    application.yml
    
    
    • 依赖
    <!-- 定时任务 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>
    
    • 新建一个Job
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.springframework.scheduling.quartz.QuartzJobBean;
    import org.springframework.stereotype.Component;
    
    @Component
    public class SimpleJob extends QuartzJobBean {
        @Override
        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
            System.out.println("simple job do ...");
        }
    }
    
    
    • 配置一个Detail和触发器
    import com.hzhh123.job.SimpleJob;
    import org.quartz.*;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class QuartzJobConfig {
        @Value("${schedule.task.simple}")
        private String testScheduleCron;
    
    
        @Bean
        public JobDetail simpleJobDetail(){
            return JobBuilder.newJob(SimpleJob.class).withIdentity("simpleJobDetail").storeDurably().build();
        }
    
        @Bean
        public Trigger simpleTrigger(){
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(testScheduleCron);
            return TriggerBuilder.newTrigger().forJob(simpleJobDetail())
                    .withIdentity("simpleTrigger")
                    .withSchedule(scheduleBuilder)
                    .build();
        }
    }
    

    三、基于数据库定时

    支持分布式集群,实现方式是基于数据库表锁。本文中定时使用的数据库是mysql,数据库操作框架是mybatis-plus
    
    • 项目代码结构
    springboot-quartz-jdbc
    │ pom.xml
    │
    └─src
        ├─main
        │  ├─java
        │  │  └─com
        │  │      └─hzhh123
        │  │          │  QuartzJdbcApplication.java
        │  │          │
        │  │          ├─config
        │  │          │      ApplicationConfig.java
        │  │          │      DruidConfig.java
        │  │          │      MyBatisConfig.java
        │  │          │      MyBatisMetaObjectHandler.java
        │  │          │
        │  │          └─quartz
        │  │              ├─domain
        │  │              │      SysJob.java
        │  │              │      SysJobLog.java
        │  │              │
        │  │              ├─exception
        │  │              │      TaskException.java
        │  │              │
        │  │              ├─mapper
        │  │              │      SysJobLogMapper.java
        │  │              │      SysJobMapper.java
        │  │              │
        │  │              ├─service
        │  │              │  │  ISysJobLogService.java
        │  │              │  │  ISysJobService.java
        │  │              │  │
        │  │              │  └─impl
        │  │              │          SysJobLogServiceImpl.java
        │  │              │          SysJobServiceImpl.java
        │  │              │
        │  │              ├─task
        │  │              │      RyTask.java
        │  │              │
        │  │              └─util
        │  │                      AbstractQuartzJob.java
        │  │                      CronUtils.java
        │  │                      JobInvokeUtil.java
        │  │                      QuartzDisallowConcurrentExecution.java
        │  │                      QuartzJobExecution.java
        │  │                      ScheduleConstants.java
        │  │                      ScheduleUtils.java
        │  │
        │  └─resources
        │      │  application-dev.yml
        │      │  application.yml
        │      │
        │      └─mapper
        │          └─quartz
        │                  SysJobLogMapper.xml
        │                  SysJobMapper.xml
        │
        └─test
            └─java
                └─com
                    └─hzhh123
                            QuartzJdbcApplicationTest.java
    
    
    • 依赖
    <!-- 定时任务 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.0.32</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.2</version>
    </dependency>
    
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-extension</artifactId>
        <version>3.5.2</version>
    </dependency>
    
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-annotation</artifactId>
        <version>3.5.2</version>
    </dependency>
    <!-- mybatis-plus 多数据源 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.11</version>
    </dependency>
    <!--常用工具类 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>
    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.2.5.Final</version>
    </dependency>
    <!-- 阿里数据库连接池 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.14</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    
    • quartz配置
    spring:
      quartz:
        job-store-type: jdbc 
        properties:
          org:
            quartz:
              jobStore:
                class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
                clusterCheckinInterval: 10000
                driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
                isClustered: true
                tablePrefix: QRTZ_
                useProperties: false
              scheduler:
                instanceId: AUTO
                instanceName: clusteredScheduler
              threadPool:
                class: org.quartz.simpl.SimpleThreadPool
                threadCount: 10
                threadPriority: 5
                threadsInheritContextClassLoaderOfInitializingThread: true
    

    配置说明:

    - org.quartz.scheduler.instanceName属性可为任何值,用在JDBC JobStore中来唯一标识实例,但是所有集群节点中必须相同。
    - org.quartz.scheduler.instanceId属性为AUTO即可,基于主机名和时间戳来产生实例ID。
    - org.quartz.jobStore.class属性为JobStoreTX,将任务持久化到数据中。因为集群中节点依赖于数据库来传播 Scheduler 实例的状态,你只能在使用 JDBC JobStore 时应用 Quartz 集群。这意味着你必须使用JobStoreTX或是JobStoreCMT作为Job存储;你不能在集群中使用RAMJobStore。
    - org.quartz.jobStore.isClustered属性为true,你就告诉了Scheduler实例要它参与到一个集群当中。这一属性会贯穿于调度框架的始终,用于修改集群环境中操作的默认行为。
    - org.quartz.jobStore.clusterCheckinInterval属性定义了Scheduler实例检入到数据库中的频率(单位:毫秒)。Scheduler检查是否其他的实例到了它们应当检入的时候未检入;这能指出一个失败的Scheduler实例,且当前Scheduler会以此来接管任何执行失败并可恢复的Job。通过检入操作,Scheduler也会更新自身的状态记录。clusterCheckinInterval越小,Scheduler节点检查失败的Scheduler实例就越频繁。默认值是15000(即15秒)
    
    • mybatis-plus配置
    #mybatis-plus配置
    mybatis-plus:
      # 如果是放在src/main/java目录下 classpath:/com/yourpackage/*/mapper/*Mapper.xml
      # 如果是放在resource目录 classpath:/mapper/*Mapper.xml
      mapperLocations: classpath*:mapper/**/*Mapper.xml
      #实体扫描,多个package用逗号或者分号分隔,该项目中多模块不采用扫描实体包
      typeAliasesPackage: com.hzhh123.domain,com.hzhh123.*.domain
      # 针对 typeAliasesPackage,如果配置了该属性,则仅仅会扫描路径下以该类作为父类的域对象
      #typeAliasesSuperType: Class<?>
      # 如果配置了该属性,SqlSessionFactoryBean 会把该包下面的类注册为对应的 TypeHandler
      #typeHandlersPackage: null
      # 如果配置了该属性,会将路径下的枚举类进行注入,让实体类字段能够简单快捷的使用枚举属性
      #typeEnumsPackage: null
      # 启动时是否检查 MyBatis XML 文件的存在,默认不检查
      checkConfigLocation: false
      # 通过该属性可指定 MyBatis 的执行器,MyBatis 的执行器总共有三种:
      # SIMPLE:该执行器类型不做特殊的事情,为每个语句的执行创建一个新的预处理语句(PreparedStatement)
      # REUSE:该执行器类型会复用预处理语句(PreparedStatement)
      # BATCH:该执行器类型会批量执行所有的更新语句
      executorType: SIMPLE
      # 指定外部化 MyBatis Properties 配置,通过该配置可以抽离配置,实现不同环境的配置部署
      configurationProperties: null
      configuration:
        # 自动驼峰命名规则(camel case)映射
        # 如果您的数据库命名符合规则无需使用 @TableField 注解指定数据库字段名
        mapUnderscoreToCamelCase: true
        # 默认枚举处理类,如果配置了该属性,枚举将统一使用指定处理器进行处理
        # org.apache.ibatis.type.EnumTypeHandler : 存储枚举的名称
        # org.apache.ibatis.type.EnumOrdinalTypeHandler : 存储枚举的索引
        # com.baomidou.mybatisplus.extension.handlers.MybatisEnumTypeHandler : 枚举类需要实现IEnum接口或字段标记@EnumValue注解.
        defaultEnumTypeHandler: org.apache.ibatis.type.EnumTypeHandler
        # 当设置为 true 的时候,懒加载的对象可能被任何懒属性全部加载,否则,每个属性都按需加载。需要和 lazyLoadingEnabled 一起使用。
        aggressiveLazyLoading: true
        # MyBatis 自动映射策略
        # NONE:不启用自动映射
        # PARTIAL:只对非嵌套的 resultMap 进行自动映射
        # FULL:对所有的 resultMap 都进行自动映射
        autoMappingBehavior: PARTIAL
        # MyBatis 自动映射时未知列或未知属性处理策
        # NONE:不做任何处理 (默认值)
        # WARNING:以日志的形式打印相关警告信息
        # FAILING:当作映射失败处理,并抛出异常和详细信息
        autoMappingUnknownColumnBehavior: NONE
        # Mybatis一级缓存,默认为 SESSION
        # SESSION session级别缓存,同一个session相同查询语句不会再次查询数据库
        # STATEMENT 关闭一级缓存
        localCacheScope: SESSION
        # 开启Mybatis二级缓存,默认为 true
        cacheEnabled: true
      global-config:
        # 是否打印 Logo banner
        banner: true
        # 是否初始化 SqlRunner
        enableSqlRunner: false
        dbConfig:
          # 主键类型
          # AUTO 数据库ID自增
          # NONE 空
          # INPUT 用户输入ID
          # ASSIGN_ID 全局唯一ID
          # ASSIGN_UUID 全局唯一ID UUID
          idType: INPUT
          # 表名前缀
          tablePrefix: null
          # 字段 format,例: %s,(对主键无效)
          columnFormat: null
          # 表名是否使用驼峰转下划线命名,只对表名生效
          tableUnderline: true
          # 大写命名,对表名和字段名均生效
          capitalMode: false
          # 全局的entity的逻辑删除字段属性名
          logicDeleteField: null
          # 逻辑已删除值
          logicDeleteValue: 1
          # 逻辑未删除值
          logicNotDeleteValue: 0
          # 字段验证策略之 insert,在 insert 的时候的字段验证策略
          # IGNORED 忽略判断
          # NOT_NULL 非NULL判断
          # NOT_EMPTY 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)
          # DEFAULT 默认的,一般只用于注解里
          # NEVER 不加入 SQL
          insertStrategy: NOT_NULL
          # 字段验证策略之 update,在 update 的时候的字段验证策略
          updateStrategy: NOT_NULL
          # 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件
          where-strategy: NOT_NULL
    
    • 基于dynamic-datasource-spring-boot-starter配置数据库
    spring:
      datasource:
        dynamic:
          primary: master
          strict: false
          datasource:
            master:
              type: com.alibaba.druid.pool.DruidDataSource
              driver-class-name: com.mysql.cj.jdbc.Driver
              username: root
              password: 123456
              url: jdbc:mysql://localhost:3308/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
              druid:
                initial-size: 6 #连接池初始化大小
                min-idle: 10 #最小空闲连接数
                max-active: 20 #最大连接数
                filters: stat
                filter:
                  stat:
                    enabled: true
                    # 慢SQL记录
                    log-slow-sql: true
                    slow-sql-millis: 1000
                    merge-sql: true
        druid:
          web-stat-filter:
            exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" #不统计这些请求数据
          #开启druid的监控面板
          stat-view-servlet:
            enabled: true #为true就是开启
            login-username: root
            login-password: root
    
    • 新增一个druid去除广告配置类
    
    import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
    import com.alibaba.druid.util.Utils;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.servlet.*;
    import java.io.IOException;
    
    /**
     * druid 配置多数据源
     *
     */
    @Configuration
    public class DruidConfig {
    
    
        /**
         * 去除监控页面底部的广告
         */
        @SuppressWarnings({"rawtypes", "unchecked"})
        @Bean
        @ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true", matchIfMissing = true)
        public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) {
            // 获取web监控页面的参数
            DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
            // 提取common.js的配置路径
            String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
            String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
            final String filePath = "support/http/resources/js/common.js";
            // 创建filter进行过滤
            Filter filter = new Filter() {
                @Override
                public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
                }
    
                @Override
                public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                        throws IOException, ServletException {
                    chain.doFilter(request, response);
                    // 重置缓冲区,响应头不会被重置
                    response.resetBuffer();
                    // 获取common.js
                    String text = Utils.readFromResource(filePath);
                    // 正则替换banner, 除去底部的广告信息
                    text = text.replaceAll("<a.*?banner\"></a><br/>", "");
                    text = text.replaceAll("powered.*?shrek.wang</a>", "");
                    //去掉头部的菜单连接
                    //text+="var t =null; $(function(){ t=setInterval(function(){$(\"a.brand\").hide()}, 10 ); setTimeout(function(){clearInterval(t)}, 2000 );})";
                    response.getWriter().write(text);
                }
    
                @Override
                public void destroy() {
                }
            };
            FilterRegistrationBean registrationBean = new FilterRegistrationBean();
            registrationBean.setFilter(filter);
            registrationBean.addUrlPatterns(commonJsPattern);
            return registrationBean;
        }
    }
    
    
    • 新增一个Mybatis配置类
    package com.hzhh123.config;
    
    import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * Mybatis支持*匹配扫描包
     *
     */
    @Configuration
    public class MyBatisConfig {
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            // 分页插件
            interceptor.addInnerInterceptor(paginationInnerInterceptor());
            // 乐观锁插件
            interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
            // 阻断插件
            interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
            return interceptor;
        }
    
        /**
         * 分页插件,自动识别数据库类型
         * https://baomidou.com/guide/interceptor-pagination.html
         */
        public PaginationInnerInterceptor paginationInnerInterceptor() {
            PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
            //// 设置数据库类型为mysql
            //paginationInnerInterceptor.setDbType(DbType.MYSQL);
            //// 设置最大单页限制数量,默认 500 条,-1 不受限制
            //paginationInnerInterceptor.setMaxLimit(-1L);
            return paginationInnerInterceptor;
        }
    
        /**
         * 乐观锁插件
         * https://baomidou.com/guide/interceptor-optimistic-locker.html
         */
        public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
            return new OptimisticLockerInnerInterceptor();
        }
    
        /**
         * 如果是对全表的删除或更新操作,就会终止该操作
         * https://baomidou.com/guide/interceptor-block-attack.html
         */
        public BlockAttackInnerInterceptor blockAttackInnerInterceptor() {
            return new BlockAttackInnerInterceptor();
        }
    
    
    }
    
    
    • 新增一个mybatis自动填充类
    package com.hzhh123.config;
    
    import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.reflection.MetaObject;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    /**
     * hzhh123
     * 2019/4/2 9:51
     * mybatis-plus 设置自动填充全局bean属性
     */
    @Slf4j
    @Component
    public class MyBatisMetaObjectHandler implements MetaObjectHandler {
        /**
         * 新增时自动填充
         *
         * @param metaObject
         */
        @Override
        public void insertFill(MetaObject metaObject) {
            log.info("[mybatis-plus新增时自动填充]");
            this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
        }
    
        /**
         * 更新时自动填充
         *
         * @param metaObject
         */
        @Override
        public void updateFill(MetaObject metaObject) {
            log.info("[mybatis-plus更新时自动填充]");
            this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
        }
    }
    
    
    • 配置自动扫描Mapper类
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    /**
     * 程序注解配置
     *
     */
    @Configuration
    // 表示通过aop框架暴露该代理对象,AopContext能够访问
    @EnableAspectJAutoProxy(exposeProxy = true)
    @MapperScan(basePackages = {"com.hzhh123.*.mapper"})
    public class ApplicationConfig {
    
    }
    
    
    • 实体
      用户自定义实体用于定时任务的CURD和保存定时任务调度日志。
      SysJob
    
    import com.fasterxml.jackson.annotation.JsonFormat;
    import com.hzhh123.quartz.util.CronUtils;
    import com.hzhh123.quartz.util.ScheduleConstants;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.commons.lang3.builder.ToStringBuilder;
    import org.apache.commons.lang3.builder.ToStringStyle;
    
    import javax.validation.constraints.NotBlank;
    import javax.validation.constraints.Size;
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     * 定时任务调度表 sys_job
     *
     */
    public class SysJob  implements Serializable {
        private static final long serialVersionUID = 1L;
    
        /**
         * 任务ID
         */
        private String jobId;
    
        /**
         * 任务名称
         */
        private String jobName;
    
        /**
         * 任务组名
         */
        private String jobGroup;
    
        /**
         * 调用目标字符串
         */
        private String invokeTarget;
    
        /**
         * cron执行表达式
         */
        private String cronExpression;
    
        /**
         * cron计划策略
         */
        private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;
    
        /**
         * 是否并发执行(0允许 1禁止)
         */
        private String concurrent;
    
        /**
         * 任务状态(0正常 1暂停)
         */
        private String status;
        /**
         * 创建者
         */
        private String createBy;
    
        /**
         * 创建时间
         */
    //    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        private Date createTime;
    
        /**
         * 更新者
         */
        private String updateBy;
    
        /**
         * 更新时间
         */
    //    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        private Date updateTime;
    
        /**
         * 备注
         */
        private String remark;
    
        public String getJobId() {
            return jobId;
        }
    
        public void setJobId(String jobId) {
            this.jobId = jobId;
        }
    
        @NotBlank(message = "任务名称不能为空")
        public String getJobName() {
            return jobName;
        }
    
        public void setJobName(String jobName) {
            this.jobName = jobName;
        }
    
        public String getJobGroup() {
            return jobGroup;
        }
    
        public void setJobGroup(String jobGroup) {
            this.jobGroup = jobGroup;
        }
    
        @NotBlank(message = "调用目标字符串不能为空")
        @Size(min = 0, max = 1000, message = "调用目标字符串长度不能超过500个字符")
        public String getInvokeTarget() {
            return invokeTarget;
        }
    
        public void setInvokeTarget(String invokeTarget) {
            this.invokeTarget = invokeTarget;
        }
    
        @NotBlank(message = "Cron执行表达式不能为空")
        @Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符")
        public String getCronExpression() {
            return cronExpression;
        }
    
        public void setCronExpression(String cronExpression) {
            this.cronExpression = cronExpression;
        }
    
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        public Date getNextValidTime() {
            if (StringUtils.isNotEmpty(cronExpression)) {
                return CronUtils.getNextExecution(cronExpression);
            }
            return null;
        }
    
        public String getMisfirePolicy() {
            return misfirePolicy;
        }
    
        public void setMisfirePolicy(String misfirePolicy) {
            this.misfirePolicy = misfirePolicy;
        }
    
        public String getConcurrent() {
            return concurrent;
        }
    
        public void setConcurrent(String concurrent) {
            this.concurrent = concurrent;
        }
    
        public String getStatus() {
            return status;
        }
    
        public void setStatus(String status) {
            this.status = status;
        }
    
        public String getCreateBy() {
            return createBy;
        }
    
        public void setCreateBy(String createBy) {
            this.createBy = createBy;
        }
    
        public Date getCreateTime() {
            return createTime;
        }
    
        public void setCreateTime(Date createTime) {
            this.createTime = createTime;
        }
    
        public String getUpdateBy() {
            return updateBy;
        }
    
        public void setUpdateBy(String updateBy) {
            this.updateBy = updateBy;
        }
    
        public Date getUpdateTime() {
            return updateTime;
        }
    
        public void setUpdateTime(Date updateTime) {
            this.updateTime = updateTime;
        }
    
        public String getRemark() {
            return remark;
        }
    
        public void setRemark(String remark) {
            this.remark = remark;
        }
    
        @Override
        public String toString() {
            return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
                    .append("jobId", getJobId())
                    .append("jobName", getJobName())
                    .append("jobGroup", getJobGroup())
                    .append("cronExpression", getCronExpression())
                    .append("nextValidTime", getNextValidTime())
                    .append("misfirePolicy", getMisfirePolicy())
                    .append("concurrent", getConcurrent())
                    .append("status", getStatus())
                    .append("createBy", getCreateBy())
                    .append("createTime", getCreateTime())
                    .append("updateBy", getUpdateBy())
                    .append("updateTime", getUpdateTime())
                    .append("remark", getRemark())
                    .toString();
        }
    }
    
    

    SysJobLog

    
    import org.apache.commons.lang3.builder.ToStringBuilder;
    import org.apache.commons.lang3.builder.ToStringStyle;
    
    import java.util.Date;
    
    /**
     * 定时任务调度日志表 sys_job_log
     *
     * @author 
     */
    public class SysJobLog{
        private static final long serialVersionUID = 1L;
    
        /**
         * ID
         */
        private String jobLogId;
    
        /**
         * 任务名称
         */
        private String jobName;
    
        /**
         * 任务组名
         */
        private String jobGroup;
    
        /**
         * 调用目标字符串
         */
        private String invokeTarget;
    
        /**
         * 日志信息
         */
        private String jobMessage;
    
        /**
         * 执行状态(0正常 1失败)
         */
        private String status;
    
        /**
         * 异常信息
         */
        private String exceptionInfo;
    
        /**
         * 开始时间
         */
        private Date startTime;
    
        /**
         * 结束时间
         */
        private Date endTime;
        /**
         * 创建者
         */
        private String createBy;
    
        /**
         * 创建时间
         */
    //    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        private Date createTime;
    
        /**
         * 更新者
         */
        private String updateBy;
    
        /**
         * 更新时间
         */
    //    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        private Date updateTime;
    
        /**
         * 备注
         */
        private String remark;
    
        public String getJobLogId() {
            return jobLogId;
        }
    
        public void setJobLogId(String jobLogId) {
            this.jobLogId = jobLogId;
        }
    
        public String getJobName() {
            return jobName;
        }
    
        public void setJobName(String jobName) {
            this.jobName = jobName;
        }
    
        public String getJobGroup() {
            return jobGroup;
        }
    
        public void setJobGroup(String jobGroup) {
            this.jobGroup = jobGroup;
        }
    
        public String getInvokeTarget() {
            return invokeTarget;
        }
    
        public void setInvokeTarget(String invokeTarget) {
            this.invokeTarget = invokeTarget;
        }
    
        public String getJobMessage() {
            return jobMessage;
        }
    
        public void setJobMessage(String jobMessage) {
            this.jobMessage = jobMessage;
        }
    
        public String getStatus() {
            return status;
        }
    
        public void setStatus(String status) {
            this.status = status;
        }
    
        public String getExceptionInfo() {
            return exceptionInfo;
        }
    
        public void setExceptionInfo(String exceptionInfo) {
            this.exceptionInfo = exceptionInfo;
        }
    
        public Date getStartTime() {
            return startTime;
        }
    
        public void setStartTime(Date startTime) {
            this.startTime = startTime;
        }
    
        public Date getEndTime() {
            return endTime;
        }
    
        public void setEndTime(Date endTime) {
            this.endTime = endTime;
        }
    
        public String getCreateBy() {
            return createBy;
        }
    
        public void setCreateBy(String createBy) {
            this.createBy = createBy;
        }
    
        public Date getCreateTime() {
            return createTime;
        }
    
        public void setCreateTime(Date createTime) {
            this.createTime = createTime;
        }
    
        public String getUpdateBy() {
            return updateBy;
        }
    
        public void setUpdateBy(String updateBy) {
            this.updateBy = updateBy;
        }
    
        public Date getUpdateTime() {
            return updateTime;
        }
    
        public void setUpdateTime(Date updateTime) {
            this.updateTime = updateTime;
        }
    
        public String getRemark() {
            return remark;
        }
    
        public void setRemark(String remark) {
            this.remark = remark;
        }
    
        @Override
        public String toString() {
            return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
                    .append("jobLogId", getJobLogId())
                    .append("jobName", getJobName())
                    .append("jobGroup", getJobGroup())
                    .append("jobMessage", getJobMessage())
                    .append("status", getStatus())
                    .append("exceptionInfo", getExceptionInfo())
                    .append("startTime", getStartTime())
                    .append("endTime", getEndTime())
                    .toString();
        }
    }
    
    
    • Mapper
      SysJobMapper
    package com.hzhh123.quartz.mapper;
    
    import com.hzhh123.quartz.domain.SysJob;
    
    import java.util.List;
    
    /**
     * 调度任务信息 数据层
     *
     */
    public interface SysJobMapper {
        /**
         * 查询调度任务日志集合
         *
         * @param job 调度信息
         * @return 操作日志集合
         */
        public List<SysJob> selectJobList(SysJob job);
    
        /**
         * 查询所有调度任务
         *
         * @return 调度任务列表
         */
        public List<SysJob> selectJobAll();
    
        /**
         * 通过调度ID查询调度任务信息
         *
         * @param jobId 调度ID
         * @return 角色对象信息
         */
        public SysJob selectJobById(String jobId);
    
        /**
         * 通过调度ID删除调度任务信息
         *
         * @param jobId 调度ID
         * @return 结果
         */
        public int deleteJobById(String jobId);
    
        /**
         * 批量删除调度任务信息
         *
         * @param ids 需要删除的数据ID
         * @return 结果
         */
        public int deleteJobByIds(String[] ids);
    
        /**
         * 修改调度任务信息
         *
         * @param job 调度任务信息
         * @return 结果
         */
        public int updateJob(SysJob job);
    
        /**
         * 新增调度任务信息
         *
         * @param job 调度任务信息
         * @return 结果
         */
        public int insertJob(SysJob job);
    }
    
    

    SysJobLogMapper

    package com.hzhh123.quartz.mapper;
    
    import com.hzhh123.quartz.domain.SysJobLog;
    
    import java.util.List;
    
    /**
     * 调度任务日志信息 数据层
     *
     */
    public interface SysJobLogMapper {
        /**
         * 获取quartz调度器日志的计划任务
         *
         * @param jobLog 调度日志信息
         * @return 调度任务日志集合
         */
        public List<SysJobLog> selectJobLogList(SysJobLog jobLog);
    
        /**
         * 查询所有调度任务日志
         *
         * @return 调度任务日志列表
         */
        public List<SysJobLog> selectJobLogAll();
    
        /**
         * 通过调度任务日志ID查询调度信息
         *
         * @param jobLogId 调度任务日志ID
         * @return 调度任务日志对象信息
         */
        public SysJobLog selectJobLogById(String jobLogId);
    
        /**
         * 新增任务日志
         *
         * @param jobLog 调度日志信息
         * @return 结果
         */
        public int insertJobLog(SysJobLog jobLog);
    
        /**
         * 批量删除调度日志信息
         *
         * @param logIds 需要删除的数据ID
         * @return 结果
         */
        public int deleteJobLogByIds(String[] logIds);
    
        /**
         * 删除任务日志
         *
         * @param jobId 调度日志ID
         * @return 结果
         */
        public int deleteJobLogById(String jobId);
    
        /**
         * 清空任务日志
         */
        public void cleanJobLog();
    }
    
    
    • Mapper XML文件
      SysJobMapper.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.hzhh123.quartz.mapper.SysJobMapper">
    
        <resultMap type="SysJob" id="SysJobResult">
            <id property="jobId" column="job_id"/>
            <result property="jobName" column="job_name"/>
            <result property="jobGroup" column="job_group"/>
            <result property="invokeTarget" column="invoke_target"/>
            <result property="cronExpression" column="cron_expression"/>
            <result property="misfirePolicy" column="misfire_policy"/>
            <result property="concurrent" column="concurrent"/>
            <result property="status" column="status"/>
            <result property="createBy" column="create_by"/>
            <result property="createTime" column="create_time"/>
            <result property="updateBy" column="update_by"/>
            <result property="updateTime" column="update_time"/>
            <result property="remark" column="remark"/>
        </resultMap>
    
        <sql id="selectJobVo">
            select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark
            from sys_job
        </sql>
    
        <select id="selectJobList" parameterType="SysJob" resultMap="SysJobResult">
            <include refid="selectJobVo"/>
            <where>
                <if test="jobName != null and jobName != ''">
                    AND job_name like concat('%', #{jobName}, '%')
                </if>
                <if test="jobGroup != null and jobGroup != ''">
                    AND job_group = #{jobGroup}
                </if>
                <if test="status != null and status != ''">
                    AND status = #{status}
                </if>
                <if test="invokeTarget != null and invokeTarget != ''">
                    AND invoke_target like concat('%', #{invokeTarget}, '%')
                </if>
            </where>
        </select>
    
        <select id="selectJobAll" resultMap="SysJobResult">
            <include refid="selectJobVo"/>
        </select>
    
        <select id="selectJobById" parameterType="String" resultMap="SysJobResult">
            <include refid="selectJobVo"/>
            where job_id = #{jobId}
        </select>
    
        <delete id="deleteJobById" parameterType="String">
            delete from sys_job where job_id = #{jobId}
        </delete>
    
        <delete id="deleteJobByIds" parameterType="String">
            delete from sys_job where job_id in
            <foreach collection="array" item="jobId" open="(" separator="," close=")">
                #{jobId}
            </foreach>
        </delete>
    
        <update id="updateJob" parameterType="SysJob">
            update sys_job
            <set>
                <if test="jobName != null and jobName != ''">job_name = #{jobName},</if>
                <if test="jobGroup != null and jobGroup != ''">job_group = #{jobGroup},</if>
                <if test="invokeTarget != null and invokeTarget != ''">invoke_target = #{invokeTarget},</if>
                <if test="cronExpression != null and cronExpression != ''">cron_expression = #{cronExpression},</if>
                <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy = #{misfirePolicy},</if>
                <if test="concurrent != null and concurrent != ''">concurrent = #{concurrent},</if>
                <if test="status !=null">status = #{status},</if>
                <if test="remark != null and remark != ''">remark = #{remark},</if>
                <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
                update_time = sysdate()
            </set>
            where job_id = #{jobId}
        </update>
    
        <insert id="insertJob" parameterType="SysJob">
            insert into sys_job(
            <if test="jobId != null and jobId != ''">job_id,</if>
            <if test="jobName != null and jobName != ''">job_name,</if>
            <if test="jobGroup != null and jobGroup != ''">job_group,</if>
            <if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if>
            <if test="cronExpression != null and cronExpression != ''">cron_expression,</if>
            <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy,</if>
            <if test="concurrent != null and concurrent != ''">concurrent,</if>
            <if test="status != null and status != ''">status,</if>
            <if test="remark != null and remark != ''">remark,</if>
            <if test="createBy != null and createBy != ''">create_by,</if>
            create_time
            )values(
            <if test="jobId != null and jobId != ''">#{jobId},</if>
            <if test="jobName != null and jobName != ''">#{jobName},</if>
            <if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if>
            <if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if>
            <if test="cronExpression != null and cronExpression != ''">#{cronExpression},</if>
            <if test="misfirePolicy != null and misfirePolicy != ''">#{misfirePolicy},</if>
            <if test="concurrent != null and concurrent != ''">#{concurrent},</if>
            <if test="status != null and status != ''">#{status},</if>
            <if test="remark != null and remark != ''">#{remark},</if>
            <if test="createBy != null and createBy != ''">#{createBy},</if>
            sysdate()
            )
        </insert>
    
    </mapper>
    
    

    SysJobLogMapper.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.hzhh123.quartz.mapper.SysJobLogMapper">
    
        <resultMap type="SysJobLog" id="SysJobLogResult">
            <id property="jobLogId" column="job_log_id"/>
            <result property="jobName" column="job_name"/>
            <result property="jobGroup" column="job_group"/>
            <result property="invokeTarget" column="invoke_target"/>
            <result property="jobMessage" column="job_message"/>
            <result property="status" column="status"/>
            <result property="exceptionInfo" column="exception_info"/>
            <result property="createTime" column="create_time"/>
        </resultMap>
    
        <sql id="selectJobLogVo">
            select job_log_id, job_name, job_group, invoke_target, job_message, status, exception_info, create_time
            from sys_job_log
        </sql>
    
        <select id="selectJobLogList" parameterType="SysJobLog" resultMap="SysJobLogResult">
            <include refid="selectJobLogVo"/>
            <where>
                <if test="jobName != null and jobName != ''">
                    AND job_name like concat('%', #{jobName}, '%')
                </if>
                <if test="jobGroup != null and jobGroup != ''">
                    AND job_group = #{jobGroup}
                </if>
                <if test="status != null and status != ''">
                    AND status = #{status}
                </if>
                <if test="invokeTarget != null and invokeTarget != ''">
                    AND invoke_target like concat('%', #{invokeTarget}, '%')
                </if>
                <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
                    and date_format(create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
                </if>
                <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
                    and date_format(create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
                </if>
            </where>
        </select>
    
        <select id="selectJobLogAll" resultMap="SysJobLogResult">
            <include refid="selectJobLogVo"/>
        </select>
    
        <select id="selectJobLogById" parameterType="String" resultMap="SysJobLogResult">
            <include refid="selectJobLogVo"/>
            where job_log_id = #{jobLogId}
        </select>
    
        <delete id="deleteJobLogById" parameterType="String">
            delete from sys_job_log where job_log_id = #{jobLogId}
        </delete>
    
        <delete id="deleteJobLogByIds" parameterType="String">
            delete from sys_job_log where job_log_id in
            <foreach collection="array" item="jobLogId" open="(" separator="," close=")">
                #{jobLogId}
            </foreach>
        </delete>
    
        <update id="cleanJobLog">
            truncate table sys_job_log
        </update>
    
        <insert id="insertJobLog" parameterType="SysJobLog">
            insert into sys_job_log(
            <if test="jobLogId != null and jobLogId != ''">job_log_id,</if>
            <if test="jobName != null and jobName != ''">job_name,</if>
            <if test="jobGroup != null and jobGroup != ''">job_group,</if>
            <if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if>
            <if test="jobMessage != null and jobMessage != ''">job_message,</if>
            <if test="status != null and status != ''">status,</if>
            <if test="exceptionInfo != null and exceptionInfo != ''">exception_info,</if>
            create_time
            )values(
            <if test="jobLogId != null and jobLogId != ''">#{jobLogId},</if>
            <if test="jobName != null and jobName != ''">#{jobName},</if>
            <if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if>
            <if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if>
            <if test="jobMessage != null and jobMessage != ''">#{jobMessage},</if>
            <if test="status != null and status != ''">#{status},</if>
            <if test="exceptionInfo != null and exceptionInfo != ''">#{exceptionInfo},</if>
            sysdate()
            )
        </insert>
    
    </mapper>
    
    
    • Service
      ISysJobService
    package com.hzhh123.quartz.service;
    
    import com.hzhh123.quartz.domain.SysJob;
    import com.hzhh123.quartz.exception.TaskException;
    import org.quartz.SchedulerException;
    
    import java.util.List;
    
    /**
     * 定时任务调度信息信息 服务层
     *
     * @author 
     */
    public interface ISysJobService {
        /**
         * 获取quartz调度器的计划任务
         *
         * @param job 调度信息
         * @return 调度任务集合
         */
        public List<SysJob> selectJobList(SysJob job);
    
        /**
         * 通过调度任务ID查询调度信息
         *
         * @param jobId 调度任务ID
         * @return 调度任务对象信息
         */
        public SysJob selectJobById(String jobId);
    
        /**
         * 暂停任务
         *
         * @param job 调度信息
         * @return 结果
         */
        public int pauseJob(SysJob job) throws SchedulerException;
    
        /**
         * 恢复任务
         *
         * @param job 调度信息
         * @return 结果
         */
        public int resumeJob(SysJob job) throws SchedulerException;
    
        /**
         * 删除任务后,所对应的trigger也将被删除
         *
         * @param job 调度信息
         * @return 结果
         */
        public int deleteJob(SysJob job) throws SchedulerException;
    
        /**
         * 批量删除调度信息
         *
         * @param jobIds 需要删除的任务ID
         * @return 结果
         */
        public void deleteJobByIds(String[] jobIds) throws SchedulerException;
    
        /**
         * 任务调度状态修改
         *
         * @param job 调度信息
         * @return 结果
         */
        public int changeStatus(SysJob job) throws SchedulerException;
    
        /**
         * 立即运行任务
         *
         * @param job 调度信息
         * @return 结果
         */
        public void run(SysJob job) throws SchedulerException;
    
        /**
         * 新增任务
         *
         * @param job 调度信息
         * @return 结果
         */
        public int insertJob(SysJob job) throws SchedulerException, TaskException;
    
        /**
         * 更新任务
         *
         * @param job 调度信息
         * @return 结果
         */
        public int updateJob(SysJob job) throws SchedulerException, TaskException;
    
        /**
         * 校验cron表达式是否有效
         *
         * @param cronExpression 表达式
         * @return 结果
         */
        public boolean checkCronExpressionIsValid(String cronExpression);
    }
    
    

    ISysJobLogService

    package com.hzhh123.quartz.service;
    
    import com.hzhh123.quartz.domain.SysJobLog;
    
    import java.util.List;
    
    /**
     * 定时任务调度日志信息信息 服务层
     *
     * @author 
     */
    public interface ISysJobLogService {
        /**
         * 获取quartz调度器日志的计划任务
         *
         * @param jobLog 调度日志信息
         * @return 调度任务日志集合
         */
        public List<SysJobLog> selectJobLogList(SysJobLog jobLog);
    
        /**
         * 通过调度任务日志ID查询调度信息
         *
         * @param jobLogId 调度任务日志ID
         * @return 调度任务日志对象信息
         */
        public SysJobLog selectJobLogById(String jobLogId);
    
        /**
         * 新增任务日志
         *
         * @param jobLog 调度日志信息
         */
        public void addJobLog(SysJobLog jobLog);
    
        /**
         * 批量删除调度日志信息
         *
         * @param logIds 需要删除的日志ID
         * @return 结果
         */
        public int deleteJobLogByIds(String[] logIds);
    
        /**
         * 删除任务日志
         *
         * @param jobId 调度日志ID
         * @return 结果
         */
        public int deleteJobLogById(String jobId);
    
        /**
         * 清空任务日志
         */
        public void cleanJobLog();
    }
    
    
    • ServiceImpl
      SysJobServiceImpl
    package com.hzhh123.quartz.service.impl;
    
    import cn.hutool.core.util.IdUtil;
    import com.hzhh123.quartz.domain.SysJob;
    import com.hzhh123.quartz.exception.TaskException;
    import com.hzhh123.quartz.mapper.SysJobMapper;
    import com.hzhh123.quartz.service.ISysJobService;
    import com.hzhh123.quartz.util.CronUtils;
    import com.hzhh123.quartz.util.ScheduleConstants;
    import com.hzhh123.quartz.util.ScheduleUtils;
    import org.quartz.JobDataMap;
    import org.quartz.JobKey;
    import org.quartz.Scheduler;
    import org.quartz.SchedulerException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import javax.annotation.PostConstruct;
    import java.util.List;
    
    /**
     * 定时任务调度信息 服务层
     *
     */
    @Service
    public class SysJobServiceImpl implements ISysJobService {
        @Autowired
        private Scheduler scheduler;
    
        @Autowired
        private SysJobMapper jobMapper;
    
        /**
         * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据)
         */
        @PostConstruct
        public void init() throws SchedulerException, TaskException {
            scheduler.clear();
            List<SysJob> jobList = jobMapper.selectJobAll();
            for (SysJob job : jobList) {
                ScheduleUtils.createScheduleJob(scheduler, job);
            }
        }
    
        /**
         * 获取quartz调度器的计划任务列表
         *
         * @param job 调度信息
         * @return
         */
        @Override
        public List<SysJob> selectJobList(SysJob job) {
            return jobMapper.selectJobList(job);
        }
    
        /**
         * 通过调度任务ID查询调度信息
         *
         * @param jobId 调度任务ID
         * @return 调度任务对象信息
         */
        @Override
        public SysJob selectJobById(String jobId) {
            return jobMapper.selectJobById(jobId);
        }
    
        /**
         * 暂停任务
         *
         * @param job 调度信息
         */
        @Override
        @Transactional
        public int pauseJob(SysJob job) throws SchedulerException {
            String jobId = job.getJobId();
            String jobGroup = job.getJobGroup();
            job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
            int rows = jobMapper.updateJob(job);
            if (rows > 0) {
                scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
            }
            return rows;
        }
    
        /**
         * 恢复任务
         *
         * @param job 调度信息
         */
        @Override
        @Transactional
        public int resumeJob(SysJob job) throws SchedulerException {
            String jobId = job.getJobId();
            String jobGroup = job.getJobGroup();
            job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
            int rows = jobMapper.updateJob(job);
            if (rows > 0) {
                scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
            }
            return rows;
        }
    
        /**
         * 删除任务后,所对应的trigger也将被删除
         *
         * @param job 调度信息
         */
        @Override
        @Transactional
        public int deleteJob(SysJob job) throws SchedulerException {
            String jobId = job.getJobId();
            String jobGroup = job.getJobGroup();
            int rows = jobMapper.deleteJobById(jobId);
            if (rows > 0) {
                scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
            }
            return rows;
        }
    
        /**
         * 批量删除调度信息
         *
         * @param jobIds 需要删除的任务ID
         * @return 结果
         */
        @Override
        @Transactional
        public void deleteJobByIds(String[] jobIds) throws SchedulerException {
            for (String jobId : jobIds) {
                SysJob job = jobMapper.selectJobById(jobId);
                deleteJob(job);
            }
        }
    
        /**
         * 任务调度状态修改
         *
         * @param job 调度信息
         */
        @Override
        @Transactional
        public int changeStatus(SysJob job) throws SchedulerException {
            int rows = 0;
            String status = job.getStatus();
            if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) {
                rows = resumeJob(job);
            } else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) {
                rows = pauseJob(job);
            }
            return rows;
        }
    
        /**
         * 立即运行任务
         *
         * @param job 调度信息
         */
        @Override
        @Transactional
        public void run(SysJob job) throws SchedulerException {
            String jobId = job.getJobId();
            String jobGroup = job.getJobGroup();
            SysJob properties = selectJobById(job.getJobId());
            // 参数
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
            scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap);
        }
    
        /**
         * 新增任务
         *
         * @param job 调度信息 调度信息
         */
        @Override
        @Transactional
        public int insertJob(SysJob job) throws SchedulerException, TaskException {
            job.setJobId(IdUtil.objectId());
            job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
            int rows = jobMapper.insertJob(job);
            if (rows > 0) {
                ScheduleUtils.createScheduleJob(scheduler, job);
            }
            return rows;
        }
    
        /**
         * 更新任务的时间表达式
         *
         * @param job 调度信息
         */
        @Override
        @Transactional
        public int updateJob(SysJob job) throws SchedulerException, TaskException {
            SysJob properties = selectJobById(job.getJobId());
            int rows = jobMapper.updateJob(job);
            if (rows > 0) {
                updateSchedulerJob(job, properties.getJobGroup());
            }
            return rows;
        }
    
        /**
         * 更新任务
         *
         * @param job      任务对象
         * @param jobGroup 任务组名
         */
        public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException {
            String jobId = job.getJobId();
            // 判断是否存在
            JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
            if (scheduler.checkExists(jobKey)) {
                // 防止创建时存在数据问题 先移除,然后在执行创建操作
                scheduler.deleteJob(jobKey);
            }
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
    
        /**
         * 校验cron表达式是否有效
         *
         * @param cronExpression 表达式
         * @return 结果
         */
        @Override
        public boolean checkCronExpressionIsValid(String cronExpression) {
            return CronUtils.isValid(cronExpression);
        }
    }
    
    

    SysJobLogServiceImpl

    package com.hzhh123.quartz.service.impl;
    
    import cn.hutool.core.util.IdUtil;
    import com.hzhh123.quartz.domain.SysJobLog;
    import com.hzhh123.quartz.mapper.SysJobLogMapper;
    import com.hzhh123.quartz.service.ISysJobLogService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    /**
     * 定时任务调度日志信息 服务层
     *
     * @author 
     */
    @Service
    public class SysJobLogServiceImpl implements ISysJobLogService {
        @Autowired
        private SysJobLogMapper jobLogMapper;
    
        /**
         * 获取quartz调度器日志的计划任务
         *
         * @param jobLog 调度日志信息
         * @return 调度任务日志集合
         */
        @Override
        public List<SysJobLog> selectJobLogList(SysJobLog jobLog) {
            return jobLogMapper.selectJobLogList(jobLog);
        }
    
        /**
         * 通过调度任务日志ID查询调度信息
         *
         * @param jobLogId 调度任务日志ID
         * @return 调度任务日志对象信息
         */
        @Override
        public SysJobLog selectJobLogById(String jobLogId) {
            return jobLogMapper.selectJobLogById(jobLogId);
        }
    
        /**
         * 新增任务日志
         *
         * @param jobLog 调度日志信息
         */
        @Override
        public void addJobLog(SysJobLog jobLog) {
            jobLog.setJobLogId(IdUtil.objectId());
            jobLogMapper.insertJobLog(jobLog);
        }
    
        /**
         * 批量删除调度日志信息
         *
         * @param logIds 需要删除的数据ID
         * @return 结果
         */
        @Override
        public int deleteJobLogByIds(String[] logIds) {
            return jobLogMapper.deleteJobLogByIds(logIds);
        }
    
        /**
         * 删除任务日志
         *
         * @param jobId 调度日志ID
         */
        @Override
        public int deleteJobLogById(String jobId) {
            return jobLogMapper.deleteJobLogById(jobId);
        }
    
        /**
         * 清空任务日志
         */
        @Override
        public void cleanJobLog() {
            jobLogMapper.cleanJobLog();
        }
    }
    
    
    • 异常类TaskException
    public class TaskException extends Exception {
        private static final long serialVersionUID = 1L;
    
        private Code code;
    
        public TaskException(String msg, Code code) {
            this(msg, code, null);
        }
    
        public TaskException(String msg, Code code, Exception nestedEx) {
            super(msg, nestedEx);
            this.code = code;
        }
    
        public Code getCode() {
            return code;
        }
    
        public enum Code {
            TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE
        }
    }
    
    • 定时任务工具类
      AbstractQuartzJob
    package com.hzhh123.quartz.util;
    
    import cn.hutool.core.util.StrUtil;
    import cn.hutool.extra.spring.SpringUtil;
    import com.hzhh123.quartz.domain.SysJob;
    import com.hzhh123.quartz.domain.SysJobLog;
    import com.hzhh123.quartz.service.ISysJobLogService;
    import org.quartz.Job;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.BeanUtils;
    
    import java.util.Date;
    
    /**
     *
     * @author 
     */
    public abstract class AbstractQuartzJob implements Job {
        private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);
        /**
         * 通用成功标识
         */
        public static final String SUCCESS = "0";
    
        /**
         * 通用失败标识
         */
        public static final String FAIL = "1";
        /**
         * 线程本地变量
         */
        private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();
    
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            SysJob sysJob = new SysJob();
            BeanUtils.copyProperties(context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES),sysJob);
            try {
                before(context, sysJob);
                if (sysJob != null) {
                    doExecute(context, sysJob);
                }
                after(context, sysJob, null);
            } catch (Exception e) {
                log.error("任务执行异常  - :", e);
                after(context, sysJob, e);
            }
        }
    
        /**
         * 执行前
         *
         * @param context 工作执行上下文对象
         * @param sysJob  系统计划任务
         */
        protected void before(JobExecutionContext context, SysJob sysJob) {
            threadLocal.set(new Date());
        }
    
        /**
         * 执行后
         *
         * @param context 工作执行上下文对象
         * @param sysJob  系统计划任务
         */
        protected void after(JobExecutionContext context, SysJob sysJob, Exception e) {
            Date startTime = threadLocal.get();
            threadLocal.remove();
    
            final SysJobLog sysJobLog = new SysJobLog();
            sysJobLog.setJobName(sysJob.getJobName());
            sysJobLog.setJobGroup(sysJob.getJobGroup());
            sysJobLog.setInvokeTarget(sysJob.getInvokeTarget());
            sysJobLog.setStartTime(startTime);
            sysJobLog.setEndTime(new Date());
            long runMs = sysJobLog.getEndTime().getTime() - sysJobLog.getStartTime().getTime();
            sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒");
            if (e != null) {
                sysJobLog.setStatus(FAIL);
                String errorMsg = e.getMessage();
                if(StrUtil.isNotBlank(errorMsg)){
                    if(errorMsg.length()>2000) {
                        errorMsg = errorMsg.substring(0, 2000);
                    }
                }
                sysJobLog.setExceptionInfo(errorMsg);
            } else {
                sysJobLog.setStatus(SUCCESS);
            }
    
            // 写入数据库当中
            SpringUtil.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
        }
    
        /**
         * 执行方法,由子类重载
         *
         * @param context 工作执行上下文对象
         * @param sysJob  系统计划任务
         * @throws Exception 执行过程中的异常
         */
        protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
    }
    
    

    CronUtils

    package com.hzhh123.quartz.util;
    
    import org.quartz.CronExpression;
    
    import java.text.ParseException;
    import java.util.Date;
    
    /**
     * cron表达式工具类
     *
     */
    public class CronUtils {
        /**
         * 返回一个布尔值代表一个给定的Cron表达式的有效性
         *
         * @param cronExpression Cron表达式
         * @return boolean 表达式是否有效
         */
        public static boolean isValid(String cronExpression) {
            return CronExpression.isValidExpression(cronExpression);
        }
    
        /**
         * 返回一个字符串值,表示该消息无效Cron表达式给出有效性
         *
         * @param cronExpression Cron表达式
         * @return String 无效时返回表达式错误描述,如果有效返回null
         */
        public static String getInvalidMessage(String cronExpression) {
            try {
                new CronExpression(cronExpression);
                return null;
            } catch (ParseException pe) {
                return pe.getMessage();
            }
        }
    
        /**
         * 返回下一个执行时间根据给定的Cron表达式
         *
         * @param cronExpression Cron表达式
         * @return Date 下次Cron表达式执行时间
         */
        public static Date getNextExecution(String cronExpression) {
            try {
                CronExpression cron = new CronExpression(cronExpression);
                return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
            } catch (ParseException e) {
                throw new IllegalArgumentException(e.getMessage());
            }
        }
    }
    
    

    JobInvokeUtil

    package com.hzhh123.quartz.util;
    
    import cn.hutool.extra.spring.SpringUtil;
    import com.hzhh123.quartz.domain.SysJob;
    import org.apache.commons.lang3.StringUtils;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.LinkedList;
    import java.util.List;
    
    /**
     * 任务执行工具
     *
     */
    public class JobInvokeUtil {
        /**
         * 执行方法
         *
         * @param sysJob 系统任务
         */
        public static void invokeMethod(SysJob sysJob) throws Exception {
            String invokeTarget = sysJob.getInvokeTarget();
            String beanName = getBeanName(invokeTarget);
            String methodName = getMethodName(invokeTarget);
            List<Object[]> methodParams = getMethodParams(invokeTarget);
    
            if (!isValidClassName(beanName)) {
                Object bean = SpringUtil.getBean(beanName);
                invokeMethod(bean, methodName, methodParams);
            } else {
                Object bean = Class.forName(beanName).newInstance();
                invokeMethod(bean, methodName, methodParams);
            }
        }
    
        /**
         * 调用任务方法
         *
         * @param bean         目标对象
         * @param methodName   方法名称
         * @param methodParams 方法参数
         */
        private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams)
                throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
                InvocationTargetException {
            if (methodParams!=null && methodParams.size() > 0) {
                Method method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
                method.invoke(bean, getMethodParamsValue(methodParams));
            } else {
                Method method = bean.getClass().getDeclaredMethod(methodName);
                method.invoke(bean);
            }
        }
    
        /**
         * 校验是否为为class包名
         *
         * @param invokeTarget 名称
         * @return true是 false否
         */
        public static boolean isValidClassName(String invokeTarget) {
            return StringUtils.countMatches(invokeTarget, ".") > 1;
        }
    
        /**
         * 获取bean名称
         *
         * @param invokeTarget 目标字符串
         * @return bean名称
         */
        public static String getBeanName(String invokeTarget) {
            String beanName = StringUtils.substringBefore(invokeTarget, "(");
            return StringUtils.substringBeforeLast(beanName, ".");
        }
    
        /**
         * 获取bean方法
         *
         * @param invokeTarget 目标字符串
         * @return method方法
         */
        public static String getMethodName(String invokeTarget) {
            String methodName = StringUtils.substringBefore(invokeTarget, "(");
            return StringUtils.substringAfterLast(methodName, ".");
        }
    
        /**
         * 获取method方法参数相关列表
         *
         * @param invokeTarget 目标字符串
         * @return method方法相关参数列表
         */
        public static List<Object[]> getMethodParams(String invokeTarget) {
            String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
            if (StringUtils.isEmpty(methodStr)) {
                return null;
            }
            String[] methodParams = methodStr.split(",");
            List<Object[]> classs = new LinkedList<>();
            for (int i = 0; i < methodParams.length; i++) {
                String str = StringUtils.trimToEmpty(methodParams[i]);
                // String字符串类型,包含'
                if (StringUtils.contains(str, "'")) {
                    classs.add(new Object[]{StringUtils.replace(str, "'", ""), String.class});
                }
                // boolean布尔类型,等于true或者false
                else if (StringUtils.equals(str, "true") || StringUtils.equalsIgnoreCase(str, "false")) {
                    classs.add(new Object[]{Boolean.valueOf(str), Boolean.class});
                }
                // long长整形,包含L
                else if (StringUtils.containsIgnoreCase(str, "L")) {
                    classs.add(new Object[]{Long.valueOf(StringUtils.replaceIgnoreCase(str, "L", "")), Long.class});
                }
                // double浮点类型,包含D
                else if (StringUtils.containsIgnoreCase(str, "D")) {
                    classs.add(new Object[]{Double.valueOf(StringUtils.replaceIgnoreCase(str, "D", "")), Double.class});
                }
                // 其他类型归类为整形
                else {
                    classs.add(new Object[]{Integer.valueOf(str), Integer.class});
                }
            }
            return classs;
        }
    
        /**
         * 获取参数类型
         *
         * @param methodParams 参数相关列表
         * @return 参数类型列表
         */
        public static Class<?>[] getMethodParamsType(List<Object[]> methodParams) {
            Class<?>[] classs = new Class<?>[methodParams.size()];
            int index = 0;
            for (Object[] os : methodParams) {
                classs[index] = (Class<?>) os[1];
                index++;
            }
            return classs;
        }
    
        /**
         * 获取参数值
         *
         * @param methodParams 参数相关列表
         * @return 参数值列表
         */
        public static Object[] getMethodParamsValue(List<Object[]> methodParams) {
            Object[] classs = new Object[methodParams.size()];
            int index = 0;
            for (Object[] os : methodParams) {
                classs[index] = (Object) os[0];
                index++;
            }
            return classs;
        }
    }
    
    

    QuartzDisallowConcurrentExecution

    package com.hzhh123.quartz.util;
    
    import com.hzhh123.quartz.domain.SysJob;
    import org.quartz.DisallowConcurrentExecution;
    import org.quartz.JobExecutionContext;
    
    /**
     * 定时任务处理(禁止并发执行)
     *
     * @author 
     */
    @DisallowConcurrentExecution
    public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob {
        @Override
        protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
            JobInvokeUtil.invokeMethod(sysJob);
        }
    }
    
    

    QuartzJobExecution

    package com.hzhh123.quartz.util;
    
    import com.hzhh123.quartz.domain.SysJob;
    import org.quartz.JobExecutionContext;
    
    /**
     * 定时任务处理(允许并发执行)
     *
     * @author 
     */
    public class QuartzJobExecution extends AbstractQuartzJob {
        @Override
        protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
            JobInvokeUtil.invokeMethod(sysJob);
        }
    }
    
    

    ScheduleConstants

    package com.hzhh123.quartz.util;
    
    /**
     * 任务调度通用常量
     *
     */
    public interface ScheduleConstants {
        public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";
    
        /**
         * 执行目标key
         */
        public static final String TASK_PROPERTIES = "TASK_PROPERTIES";
    
        /**
         * 默认
         */
        public static final String MISFIRE_DEFAULT = "0";
    
        /**
         * 立即触发执行
         */
        public static final String MISFIRE_IGNORE_MISFIRES = "1";
    
        /**
         * 触发一次执行
         */
        public static final String MISFIRE_FIRE_AND_PROCEED = "2";
    
        /**
         * 不触发立即执行
         */
        public static final String MISFIRE_DO_NOTHING = "3";
    
        public enum Status {
            /**
             * 正常
             */
            NORMAL("0"),
            /**
             * 暂停
             */
            PAUSE("1");
    
            private String value;
    
            private Status(String value) {
                this.value = value;
            }
    
            public String getValue() {
                return value;
            }
        }
    }
    
    

    ScheduleUtils

    package com.hzhh123.quartz.util;
    
    import com.hzhh123.quartz.domain.SysJob;
    import com.hzhh123.quartz.exception.TaskException;
    import org.quartz.*;
    
    /**
     * 定时任务工具类
     *
     */
    public class ScheduleUtils {
        /**
         * 得到quartz任务类
         *
         * @param sysJob 执行计划
         * @return 具体执行任务类
         */
        private static Class<? extends Job> getQuartzJobClass(SysJob sysJob) {
            boolean isConcurrent = "0".equals(sysJob.getConcurrent());
            return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
        }
    
        /**
         * 构建任务触发对象
         */
        public static TriggerKey getTriggerKey(String jobId, String jobGroup) {
            return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
        }
    
        /**
         * 构建任务键对象
         */
        public static JobKey getJobKey(String jobId, String jobGroup) {
            return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
        }
    
        /**
         * 创建定时任务
         */
        public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException {
            Class<? extends Job> jobClass = getQuartzJobClass(job);
            // 构建job信息
            String jobId = job.getJobId();
            String jobGroup = job.getJobGroup();
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();
    
            // 表达式调度构建器
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
            cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
    
            // 按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup))
                    .withSchedule(cronScheduleBuilder).build();
    
            // 放入参数,运行时的方法可以获取
            jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
    
            // 判断是否存在
            if (scheduler.checkExists(getJobKey(jobId, jobGroup))) {
                // 防止创建时存在数据问题 先移除,然后在执行创建操作
                scheduler.deleteJob(getJobKey(jobId, jobGroup));
            }
    
            scheduler.scheduleJob(jobDetail, trigger);
    
            // 暂停任务
            if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) {
                scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
            }
        }
    
        /**
         * 设置定时任务策略
         */
        public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb)
                throws TaskException {
            switch (job.getMisfirePolicy()) {
                case ScheduleConstants.MISFIRE_DEFAULT:
                    return cb;
                case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
                    return cb.withMisfireHandlingInstructionIgnoreMisfires();
                case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
                    return cb.withMisfireHandlingInstructionFireAndProceed();
                case ScheduleConstants.MISFIRE_DO_NOTHING:
                    return cb.withMisfireHandlingInstructionDoNothing();
                default:
                    throw new TaskException("The task misfire policy '" + job.getMisfirePolicy()
                            + "' cannot be used in cron schedule tasks", TaskException.Code.CONFIG_ERROR);
            }
        }
    }
    
    
    • 任务调度测试类
    import org.springframework.stereotype.Component;
    
    /**
     * 定时任务调度测试
     *
     */
    @Component("ryTask")
    public class RyTask {
        public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) {
            System.out.println(String.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i));
        }
    
        public void ryParams(String params) {
            System.out.println("执行有参方法:" + params);
        }
    
        public void ryNoParams() {
            System.out.println("执行无参方法");
        }
    
    
        public void ryIntParams(Integer i) {
            System.out.println("执行结果" + i);
        }
    
        public static void main(String[] args) {
            new RyTask().ryMultipleParams("a",true,1L,1.0,1);
        }
    }
    
    

    测试

    /**
     * 添加任务
     * @throws TaskException
     * @throws SchedulerException
     */
    @Test
    public void test1() throws TaskException, SchedulerException {
        SysJob sysJob=new SysJob();
        sysJob.setJobName("测试无参");
        sysJob.setJobGroup("DEFAULT");
        sysJob.setInvokeTarget("ryTask.ryNoParams");
        sysJob.setCronExpression("0/20 * * * * ?");
        sysJob.setConcurrent("1");
        sysJob.setCreateTime(new Date());
        sysJobService.insertJob(sysJob);
    }
    
    • 开启sql日志
    logging:
      level:
        com.hzhh123: debug
        org.springframework: warn
    
    • 启动任务执行
    
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::                (v2.6.7)
    
    2023-03-09 11:42:24.043  INFO 1788 --- [           main] com.hzhh123.QuartzJdbcApplication        : Starting QuartzJdbcApplication using Java 11.0.8 on LAPTOP-S6B6C46G with PID 1788 (E:\work\java\study\java-learn\springboot\springboot-quartz-demo\springboot-quartz-jdbc\target\classes started by hzhh123 in E:\work\java\study\java-learn\springboot\springboot-quartz-demo)
    2023-03-09 11:42:24.046 DEBUG 1788 --- [           main] com.hzhh123.QuartzJdbcApplication        : Running with Spring Boot v2.6.7, Spring v5.3.19
    2023-03-09 11:42:24.046  INFO 1788 --- [           main] com.hzhh123.QuartzJdbcApplication        : The following 1 profile is active: "dev"
    2023-03-09 11:42:25.110  INFO 1788 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
    2023-03-09 11:42:25.110  INFO 1788 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.62]
    2023-03-09 11:42:25.201  INFO 1788 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
    2023-03-09 11:42:25.652  INFO 1788 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1,master} inited
    2023-03-09 11:42:25.653  INFO 1788 --- [           main] c.b.d.d.DynamicRoutingDataSource         : dynamic-datasource - add a datasource named [master] success
    2023-03-09 11:42:25.653  INFO 1788 --- [           main] c.b.d.d.DynamicRoutingDataSource         : dynamic-datasource initial loaded [1] datasource,primary datasource named [master]
     _ _   |_  _ _|_. ___ _ |    _ 
    | | |\/|_)(_| | |_\  |_)||_|_\ 
         /               |         
                            3.5.2 
    2023-03-09 11:42:26.348  INFO 1788 --- [           main] org.quartz.impl.StdSchedulerFactory      : Using default implementation for ThreadExecutor
    2023-03-09 11:42:26.349  INFO 1788 --- [           main] org.quartz.simpl.SimpleThreadPool        : Job execution threads will use class loader of thread: main
    2023-03-09 11:42:26.355  INFO 1788 --- [           main] org.quartz.core.SchedulerSignalerImpl    : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
    2023-03-09 11:42:26.355  INFO 1788 --- [           main] org.quartz.core.QuartzScheduler          : Quartz Scheduler v.2.3.2 created.
    2023-03-09 11:42:26.359  INFO 1788 --- [           main] org.quartz.core.QuartzScheduler          : Scheduler meta-data: Quartz Scheduler (v2.3.2) 'clusteredScheduler' with instanceId 'LAPTOP-S6B6C46G1678333346348'
      Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
      NOT STARTED.
      Currently in standby mode.
      Number of jobs executed: 0
      Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
      Using job-store 'org.springframework.scheduling.quartz.LocalDataSourceJobStore' - which supports persistence. and is clustered.
    
    2023-03-09 11:42:26.359  INFO 1788 --- [           main] org.quartz.impl.StdSchedulerFactory      : Quartz scheduler 'clusteredScheduler' initialized from an externally provided properties instance.
    2023-03-09 11:42:26.359  INFO 1788 --- [           main] org.quartz.impl.StdSchedulerFactory      : Quartz scheduler version: 2.3.2
    2023-03-09 11:42:26.360  INFO 1788 --- [           main] org.quartz.core.QuartzScheduler          : JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@375084c9
    2023-03-09 11:42:26.420 DEBUG 1788 --- [           main] c.h.q.mapper.SysJobMapper.selectJobAll   : ==>  Preparing: select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark from sys_job
    2023-03-09 11:42:26.427 DEBUG 1788 --- [           main] c.h.q.mapper.SysJobMapper.selectJobAll   : ==> Parameters: 
    2023-03-09 11:42:26.438 DEBUG 1788 --- [           main] c.h.q.mapper.SysJobMapper.selectJobAll   : <==      Total: 5
    2023-03-09 11:42:26.867  INFO 1788 --- [           main] org.quartz.core.QuartzScheduler          : Scheduler clusteredScheduler_$_LAPTOP-S6B6C46G1678333346348 started.
    2023-03-09 11:42:26.877  INFO 1788 --- [           main] com.hzhh123.QuartzJdbcApplication        : Started QuartzJdbcApplication in 3.359 seconds (JVM running for 4.57)
    执行有参方法:ry
    2023-03-09 11:42:30.365 DEBUG 1788 --- [eduler_Worker-1] c.h.q.m.SysJobLogMapper.insertJobLog     : ==>  Preparing: insert into sys_job_log( job_log_id, job_name, job_group, invoke_target, job_message, status, create_time )values( ?, ?, ?, ?, ?, ?, sysdate() )
    2023-03-09 11:42:30.366 DEBUG 1788 --- [eduler_Worker-1] c.h.q.m.SysJobLogMapper.insertJobLog     : ==> Parameters: 640955a611ce1465da54ed42(String), 测试有参(String), DEFAULT(String), ryTask.ryParams('ry')(String), 测试有参 总共耗时:8毫秒(String), 0(String)
    2023-03-09 11:42:30.371 DEBUG 1788 --- [eduler_Worker-1] c.h.q.m.SysJobLogMapper.insertJobLog     : <==    Updates: 1
    
    • sql
      sys_job和sys_job_log
    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    -- Table structure for sys_job
    -- ----------------------------
    DROP TABLE IF EXISTS `sys_job`;
    CREATE TABLE `sys_job`  (
      `job_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务ID',
      `job_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '任务名称',
      `job_group` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'DEFAULT' COMMENT '任务组名',
      `invoke_target` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调用目标字符串',
      `cron_expression` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT 'cron执行表达式',
      `misfire_policy` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '3' COMMENT '计划执行错误策略(1立即执行 2执行一次 3放弃执行)',
      `concurrent` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '1' COMMENT '是否并发执行(0允许 1禁止)',
      `status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '状态(0正常 1暂停)',
      `create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '创建者',
      `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
      `update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '更新者',
      `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
      `remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '备注信息',
      PRIMARY KEY (`job_id`, `job_name`, `job_group`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '定时任务调度表' ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Table structure for sys_job_log
    -- ----------------------------
    DROP TABLE IF EXISTS `sys_job_log`;
    CREATE TABLE `sys_job_log`  (
      `job_log_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务日志ID',
      `job_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务名称',
      `job_group` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务组名',
      `invoke_target` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调用目标字符串',
      `job_message` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '日志信息',
      `status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '执行状态(0正常 1失败)',
      `exception_info` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '异常信息',
      `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
      PRIMARY KEY (`job_log_id`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '定时任务调度日志表' ROW_FORMAT = Dynamic;
    
    SET FOREIGN_KEY_CHECKS = 1;
    
    

    quartz.sql

    #
    # In your Quartz properties file, you'll need to set
    # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
    #
    #
    # By: Ron Cordell - roncordell
    #  I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM.
    
    DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
    DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
    DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
    DROP TABLE IF EXISTS QRTZ_LOCKS;
    DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
    DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
    DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
    DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
    DROP TABLE IF EXISTS QRTZ_TRIGGERS;
    DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
    DROP TABLE IF EXISTS QRTZ_CALENDARS;
    
    CREATE TABLE QRTZ_JOB_DETAILS(
    SCHED_NAME VARCHAR(120) NOT NULL,
    JOB_NAME VARCHAR(190) NOT NULL,
    JOB_GROUP VARCHAR(190) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    JOB_CLASS_NAME VARCHAR(250) NOT NULL,
    IS_DURABLE VARCHAR(1) NOT NULL,
    IS_NONCONCURRENT VARCHAR(1) NOT NULL,
    IS_UPDATE_DATA VARCHAR(1) NOT NULL,
    REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_TRIGGERS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    JOB_NAME VARCHAR(190) NOT NULL,
    JOB_GROUP VARCHAR(190) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    NEXT_FIRE_TIME BIGINT(13) NULL,
    PREV_FIRE_TIME BIGINT(13) NULL,
    PRIORITY INTEGER NULL,
    TRIGGER_STATE VARCHAR(16) NOT NULL,
    TRIGGER_TYPE VARCHAR(8) NOT NULL,
    START_TIME BIGINT(13) NOT NULL,
    END_TIME BIGINT(13) NULL,
    CALENDAR_NAME VARCHAR(190) NULL,
    MISFIRE_INSTR SMALLINT(2) NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
    REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    REPEAT_COUNT BIGINT(7) NOT NULL,
    REPEAT_INTERVAL BIGINT(12) NOT NULL,
    TIMES_TRIGGERED BIGINT(10) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_CRON_TRIGGERS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    CRON_EXPRESSION VARCHAR(120) NOT NULL,
    TIME_ZONE_ID VARCHAR(80),
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_SIMPROP_TRIGGERS
      (
        SCHED_NAME VARCHAR(120) NOT NULL,
        TRIGGER_NAME VARCHAR(190) NOT NULL,
        TRIGGER_GROUP VARCHAR(190) NOT NULL,
        STR_PROP_1 VARCHAR(512) NULL,
        STR_PROP_2 VARCHAR(512) NULL,
        STR_PROP_3 VARCHAR(512) NULL,
        INT_PROP_1 INT NULL,
        INT_PROP_2 INT NULL,
        LONG_PROP_1 BIGINT NULL,
        LONG_PROP_2 BIGINT NULL,
        DEC_PROP_1 NUMERIC(13,4) NULL,
        DEC_PROP_2 NUMERIC(13,4) NULL,
        BOOL_PROP_1 VARCHAR(1) NULL,
        BOOL_PROP_2 VARCHAR(1) NULL,
        PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
        FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_BLOB_TRIGGERS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    BLOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_CALENDARS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    CALENDAR_NAME VARCHAR(190) NOT NULL,
    CALENDAR BLOB NOT NULL,
    PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_FIRED_TRIGGERS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    ENTRY_ID VARCHAR(95) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    INSTANCE_NAME VARCHAR(190) NOT NULL,
    FIRED_TIME BIGINT(13) NOT NULL,
    SCHED_TIME BIGINT(13) NOT NULL,
    PRIORITY INTEGER NOT NULL,
    STATE VARCHAR(16) NOT NULL,
    JOB_NAME VARCHAR(190) NULL,
    JOB_GROUP VARCHAR(190) NULL,
    IS_NONCONCURRENT VARCHAR(1) NULL,
    REQUESTS_RECOVERY VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,ENTRY_ID))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_SCHEDULER_STATE (
    SCHED_NAME VARCHAR(120) NOT NULL,
    INSTANCE_NAME VARCHAR(190) NOT NULL,
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,
    PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
    ENGINE=InnoDB;
    
    CREATE TABLE QRTZ_LOCKS (
    SCHED_NAME VARCHAR(120) NOT NULL,
    LOCK_NAME VARCHAR(40) NOT NULL,
    PRIMARY KEY (SCHED_NAME,LOCK_NAME))
    ENGINE=InnoDB;
    
    CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
    CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);
    
    CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
    CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
    CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
    CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
    CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
    CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
    CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
    CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
    CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
    CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
    CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
    CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
    
    CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
    CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
    CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
    CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
    CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
    CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
    
    commit;
    
    

    本文由mdnice多平台发布

    相关文章

      网友评论

          本文标题:springboot整合quartz应用

          本文链接:https://www.haomeiwen.com/subject/pgmaldtx.html