一、说明
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') >= date_format(#{params.beginTime},'%y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
and date_format(create_time,'%y%m%d') <= 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多平台发布
网友评论