基本環境搭建
導入quartz所需要的內置表
quartz 如果要將定時任務的數據持久化到數據庫就需要導入quartz的內置表
進入quartz的官網http://www.quartz-scheduler.org/,點擊Downloads,下載後在目錄\docs\dbTables下有常用數據庫創建quartz表的腳本,例如:“tables_mysql.sql
,你是啥數據庫就導入啥對應的 腳本就好了
ps: table_mysql_innodb.sql 這個是針對 mysql中innodb 這個引擎的,如果你使用的是這個引擎就導入這個吧
quartz 內置表結構:
自定義的業務表
-- 注意:job_name存放的任務類的全路徑,在quartz中通過jobName和jobGroup來確定trigger的唯一性,所以這兩列爲聯合唯一索引
create table t_schedule_trigger
(
id int primary key auto_increment, -- ID
cron varchar(200) not null, -- 時間表達式
status char(1) not null, -- 使用狀態 0:禁用 1:啓用
job_name varchar(200) not null, -- 任務名稱
job_group varchar(200) not null, -- 任務分組
unique index(job_name,job_group)
);
-- 額外添加到任務中的參數
create table t_schedule_trigger_param
(
param_id int primary key auto_increment, -- ID
name varchar(200) not null, -- 參數名
value varchar(512), -- 參數值
schedule_trigger_id int not null, -- 外鍵:引用t_schedule_trigger(id)
foreign key(schedule_trigger_id) references t_schedule_trigger(id)
);
案例中需要觀察表數據變化的表
-- 自定義的業務表
SELECT * FROM t_schedule_trigger;
SELECT * FROM t_schedule_trigger_param;
-- quartz調度框架自帶的表
SELECT * FROM qrtz_scheduler_state;
SELECT * FROM qrtz_cron_triggers;
SELECT * FROM qrtz_simple_triggers
SELECT * FROM qrtz_triggers;
SELECT * FROM qrtz_job_details;
創建springboot項目
這是此處項目所要用到的啓動器
maven中還需添加:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- aop切面-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
pom.xml所有代碼:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cpc</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- aop切面-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
修改quartz數據源爲 Druid
說明:
quartz 需要用到數據庫連接池,Quartz 2.0 以前默認使用 DBCP。Quartz 2.0 以後 默認C3P0(包含2.0)。下面我們要將數據源修改爲 Druid
基本步驟:
(1)導入依賴(上面已經導入了)
(2)導入DruidConnectionProvider.java
(3)修改quartz.properties配置
導入DruidConnectionProvider.java
package com.cpc.util;
import com.alibaba.druid.pool.DruidDataSource;
import org.quartz.SchedulerException;
import org.quartz.utils.ConnectionProvider;
import java.sql.Connection;
import java.sql.SQLException;
/*
#============================================================================
# JDBC
#============================================================================
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties:false
org.quartz.jobStore.dataSource:qzDS
#org.quartz.dataSource.qzDS.connectionProvider.class:org.quartz.utils.PoolingConnectionProvider
org.quartz.dataSource.qzDS.connectionProvider.class:com.javaxl.q03.quartz.DruidConnectionProvider
org.quartz.dataSource.qzDS.driver:com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL:jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8
org.quartz.dataSource.qzDS.user:root
org.quartz.dataSource.qzDS.password:root
org.quartz.dataSource.qzDS.maxConnections:30
org.quartz.dataSource.qzDS.validationQuery: select 0
*/
/**
* [Druid連接池的Quartz擴展類]
*
* @ProjectName: []
* @Author: [xuguang]
* @CreateDate: [2015/11/10 17:58]
* @Update: [說明本次修改內容] BY[xuguang][2015/11/10]
* @Version: [v1.0]
*/
public class DruidConnectionProvider implements ConnectionProvider {
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 常量配置,與quartz.properties文件的key保持一致(去掉前綴),同時提供set方法,Quartz框架自動注入值。
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
//JDBC驅動
public String driver;
//JDBC連接串
public String URL;
//數據庫用戶名
public String user;
//數據庫用戶密碼
public String password;
//數據庫最大連接數
public int maxConnection;
//數據庫SQL查詢每次連接返回執行到連接池,以確保它仍然是有效的。
public String validationQuery;
private boolean validateOnCheckout;
private int idleConnectionValidationSeconds;
public String maxCachedStatementsPerConnection;
private String discardIdleConnectionsSeconds;
public static final int DEFAULT_DB_MAX_CONNECTIONS = 10;
public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120;
//Druid連接池
private DruidDataSource datasource;
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 接口實現
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public Connection getConnection() throws SQLException {
return datasource.getConnection();
}
public void shutdown() throws SQLException {
datasource.close();
}
public void initialize() throws SQLException{
if (this.URL == null) {
throw new SQLException("DBPool could not be created: DB URL cannot be null");
}
if (this.driver == null) {
throw new SQLException("DBPool driver could not be created: DB driver class name cannot be null!");
}
if (this.maxConnection < 0) {
throw new SQLException("DBPool maxConnectins could not be created: Max connections must be greater than zero!");
}
datasource = new DruidDataSource();
try{
datasource.setDriverClassName(this.driver);
} catch (Exception e) {
try {
throw new SchedulerException("Problem setting driver class name on datasource: " + e.getMessage(), e);
} catch (SchedulerException e1) {
}
}
datasource.setUrl(this.URL);
datasource.setUsername(this.user);
datasource.setPassword(this.password);
datasource.setMaxActive(this.maxConnection);
datasource.setMinIdle(1);
datasource.setMaxWait(0);
datasource.setMaxPoolPreparedStatementPerConnectionSize(this.DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION);
if (this.validationQuery != null) {
datasource.setValidationQuery(this.validationQuery);
if(!this.validateOnCheckout)
datasource.setTestOnReturn(true);
else
datasource.setTestOnBorrow(true);
datasource.setValidationQueryTimeout(this.idleConnectionValidationSeconds);
}
}
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 提供get set方法
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getURL() {
return URL;
}
public void setURL(String URL) {
this.URL = URL;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getMaxConnection() {
return maxConnection;
}
public void setMaxConnection(int maxConnection) {
this.maxConnection = maxConnection;
}
public String getValidationQuery() {
return validationQuery;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public boolean isValidateOnCheckout() {
return validateOnCheckout;
}
public void setValidateOnCheckout(boolean validateOnCheckout) {
this.validateOnCheckout = validateOnCheckout;
}
public int getIdleConnectionValidationSeconds() {
return idleConnectionValidationSeconds;
}
public void setIdleConnectionValidationSeconds(int idleConnectionValidationSeconds) {
this.idleConnectionValidationSeconds = idleConnectionValidationSeconds;
}
public DruidDataSource getDatasource() {
return datasource;
}
public void setDatasource(DruidDataSource datasource) {
this.datasource = datasource;
}
}
在項目中添加quartz.properties文件(這樣就不會加載自帶的properties文件),此文件的內容主要分爲:scheduler,ThreadPool,JobStore,plugin,Datasources等部分,覆蓋properties文件的目的是覆蓋默認的數據源,更換爲druid的數據配置
#
#============================================================================
# Configure Main Scheduler Properties 調度器屬性
#============================================================================
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount= 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
org.quartz.jobStore.misfireThreshold: 60000
#============================================================================
# Configure JobStore
#============================================================================
#存儲方式使用JobStoreTX,也就是數據庫
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#使用自己的配置文件
org.quartz.jobStore.useProperties:true
#數據庫中quartz表的表名前綴
org.quartz.jobStore.tablePrefix:qrtz_
org.quartz.jobStore.dataSource:qzDS
#是否使用集羣(如果項目只部署到 一臺服務器,就不用了)
org.quartz.jobStore.isClustered = true
#============================================================================
# Configure Datasources
#============================================================================
#配置數據庫源(org.quartz.dataSource.qzDS.maxConnections: c3p0配置的是有s的,druid數據源沒有s)
org.quartz.dataSource.qzDS.connectionProvider.class:com.cpc.util.DruidConnectionProvider
org.quartz.dataSource.qzDS.driver: com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL: jdbc:mysql://localhost:3306/cpc?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
org.quartz.dataSource.qzDS.user: root
org.quartz.dataSource.qzDS.password: root
org.quartz.dataSource.qzDS.maxConnection: 10
導入 MyJobFactory,讓job類實現bean主任的功能
package com.cpc.config;
import lombok.extern.slf4j.Slf4j;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class MyJobFactory extends AdaptableJobFactory {
//這個由Spring自動注入
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
//重寫Job任務對象的創建實例方法
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
//通過以下方式,解決Job任務無法使用Spring中的Bean問題
autowireCapableBeanFactory.autowireBean(jobInstance);
//這個時候 jobInstance 已經交給spring管理
return jobInstance;
}
}
QuartzConfiguration.java(quartz調度框架與spring框架整合的配置類,主要是要將org.quartz.Scheduler交給spring進行管理)
package com.cpc.config;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import java.io.IOException;
import java.util.Properties;
@Configuration
public class QuartzConfiguration {
@Autowired
private MyJobFactory myJobFactory;
//創建調度器工廠
@Bean
public SchedulerFactoryBean schedulerFactoryBean(){
//1.創建SchedulerFactoryBean
//2.加載自定義的quartz.properties配置文件
//3.設置MyJobFactory
SchedulerFactoryBean factoryBean=new SchedulerFactoryBean();
try {
factoryBean.setQuartzProperties(quartzProperties());
factoryBean.setJobFactory(myJobFactory);
return factoryBean;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean=new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
@Bean(name="scheduler")
public Scheduler scheduler(){
return schedulerFactoryBean().getScheduler();
}
}
application.yml
server:
servlet:
context-path: /
port: 80
spring:
datasource:
#1.JDBC
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/cpc?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
username: root
password: root
druid:
#2.連接池配置
#初始化連接池的連接數量 大小,最小,最大
initial-size: 5
min-idle: 5
max-active: 20
#配置獲取連接等待超時的時間
max-wait: 60000
#配置間隔多久才進行一次檢測,檢測需要關閉的空閒連接,單位是毫秒
time-between-eviction-runs-millis: 60000
# 配置一個連接在池中最小生存的時間,單位是毫秒
min-evictable-idle-time-millis: 30000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: true
test-on-return: false
# 是否緩存preparedStatement,也就是PSCache 官方建議MySQL下建議關閉 個人建議如果想用SQL防火牆 建議打開
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
# 配置監控統計攔截的filters,去掉後監控界面sql無法統計,'wall'用於防火牆
filter:
stat:
merge-sql: true
slow-sql-millis: 5000
#3.基礎監控配置
web-stat-filter:
enabled: true
url-pattern: /*
#設置不統計哪些URL
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
session-stat-enable: true
session-stat-max-count: 100
stat-view-servlet:
enabled: true
url-pattern: /druid/*
reset-enable: true
#設置監控頁面的登錄名和密碼
login-username: admin
login-password: admin
allow: 127.0.0.1
#deny: 192.168.1.100
thymeleaf:
cache: false
encoding: utf-8
#顯示日誌
logging:
level:
com.javaxl.quartz02.mapper: debug
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
mybatis:
mapper-locations: classpath:mappings/*.xml
啓動類:
package com.cpc;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@MapperScan("com.cpc.dao")
@EnableTransactionManagement
@EnableScheduling
@SpringBootApplication
public class CpcApplication {
public static void main(String[] args) {
SpringApplication.run(CpcApplication.class, args);
}
}
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/cpc?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
jdbc.username=root
jdbc.password=root
jdbc.initialSize=10
jdbc.maxTotal=100
jdbc.maxIdle=50
jdbc.minIdle=10
jdbc.maxWaitMillis=-1
案例
下面是我使用代碼生成工具生成的簡單對 t_schedule_trigger
和 t_schedule_trigger_param
表的 curd
在生成的curd上在加了點方法:
TScheduleTriggerMapper.java
java:
/**
* 查詢觸發器中包含的所有任務
* @return
*/
List<TScheduleTrigger> queryScheduleTriggerLst();
xml:
<select id="queryScheduleTriggerLst" resultMap="TScheduleTriggerMap">
select id, cron, status, job_name, job_group
from t_schedule_trigger
</select>
注:此處省略service 代碼
TScheduleTriggerParamMapper.java
java:
/**
* 查詢出當前任務類對應所需的參數
* @param triggerId
* @return
*/
List<TScheduleTriggerParam> queryScheduleParamLst(Integer triggerId);
xml:
<select id="queryScheduleParamLst" resultMap="TScheduleTriggerParamMap">
select param_id, name, value, schedule_trigger_id from t_schedule_trigger_param where schedule_trigger_id=#{triggerId}
</select>
注:此處省略service 代碼
spring自帶定時任務作業類SpringTaskByQaurtz .java
import com.cpc.dao.TScheduleTriggerMapper;
import com.cpc.entity.TScheduleTrigger;
import com.cpc.entity.TScheduleTriggerParam;
import com.cpc.service.TScheduleTriggerParamService;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.Resource;
import java.util.List;
/**
* @Description:
* @Author: cpc
* @Date: 2019-11-16 8:47
* @Version: V1.0
*/
@Component
public class SpringTaskByQaurtz {
@Resource
private TScheduleTriggerMapper tScheduleTriggerDao;
@Resource
private TScheduleTriggerParamService tScheduleTriggerParamService;
@Autowired
private Scheduler scheduler;
//這是每10秒中將調用一次這個方法
@Scheduled(cron = "0/10 * * * * ?")
public void refreshScheduler(){
try {
//這是查找出所有數據庫中的定時任務數據
List<TScheduleTrigger> scheduleTriggers =
tScheduleTriggerDao.queryScheduleTriggerLst();
if(null!=scheduleTriggers){
//循環開啓定時任務
for (TScheduleTrigger scheduleTrigger : scheduleTriggers) {
String cron = scheduleTrigger.getCron(); //表達式
String jobName = scheduleTrigger.getJobName(); //任務名稱
String jobGroup = scheduleTrigger.getJobGroup(); //任務分組
String status = scheduleTrigger.getStatus(); //任務狀態
//JobName+JobGroup=Primary Key
//根據jobName和jobGroup生成TriggerKey
TriggerKey triggerKey =
TriggerKey.triggerKey(jobName, jobGroup);
//根據TriggerKey到Scheduler調度器中獲取觸發器
CronTrigger cronTrigger = (CronTrigger)
scheduler.getTrigger(triggerKey);
if(null==cronTrigger){
//這是還沒創建的定時任務 也就是 cronTrigger 爲空的清空
if(status.equals("0"))
continue;
System.out.println("創建調度器");
//創建任務詳情
JobDetail jobDetail=
JobBuilder.newJob((Class<? extends Job>) Class.forName(jobName))
.withIdentity(jobName,jobGroup)
.build();
//往Job任務中傳遞參數
JobDataMap jobDataMap = jobDetail.getJobDataMap();
List<TScheduleTriggerParam> params =
tScheduleTriggerParamService.queryScheduleParamLst(scheduleTrigger.getId());
for (TScheduleTriggerParam param : params) {
jobDataMap.put(param.getName(),param.getValue());
}
//創建表達式調度器
CronScheduleBuilder cronSchedule =
CronScheduleBuilder.cronSchedule(cron);
//創建Trigger
cronTrigger=TriggerBuilder.newTrigger()
.withIdentity(jobName,jobGroup)
.withSchedule(cronSchedule)
.build();
//將jobDetail和Trigger注入到scheduler調度器中
scheduler.scheduleJob(jobDetail,cronTrigger);
}else{
//System.out.println("Quartz 調度任務中已存在該任務");
if(status.equals("0")){
//如果禁用就直接將定時任務給關了
JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
scheduler.deleteJob(jobKey);
continue;
}
//調度器中的表達式
String cronExpression =
cronTrigger.getCronExpression();
if(!cron.equals(cronExpression)){
//創建表達式調度器
CronScheduleBuilder cronSchedule =
CronScheduleBuilder.cronSchedule(cron);
//重構
cronTrigger=cronTrigger.getTriggerBuilder()
.withIdentity(triggerKey)
.withSchedule(cronSchedule)
.build();
//刷新調度器
scheduler.rescheduleJob(triggerKey,cronTrigger);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
讀取數據庫中表達式啓動定時任務
數據庫表t_schedule_trigger配置
MyJob.java
package com.cpc.job;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
@Slf4j
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("MyJob是一個空的任務計劃,時間:"+new Date().toLocaleString());
}
}
Spring自帶定時任務每10s執行一次,查詢自定義觸發器表,獲取到具體的作業類及任務表達式,quartz的任務爲每5s執行一次,所以打印如上
更改定時任務狀態
更改數據庫調度器表t_schedule_trigger的state狀態
應爲spring自帶的定時任務會沒10秒指向一次所有當我們對數據庫進行修改的時候到了對應的時間點就刷新了
定時任務中攜帶參數
t_schedule_trigger
t_schedule_trigger_param
MyJobParam
package com.cpc.job;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* @Description:
* @Author: cpc
* @Date: 2019-11-15 18:40
* @Version: V1.0
*/
@Component
@Slf4j
public class MyJobParam implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
JobDetail jobDetail =
jobExecutionContext.getJobDetail();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
System.out.println(new Date().toLocaleString()+"-->攜帶參數個數:"+jobDataMap.size());
}
}
程序執行spring自帶的定時任務,每10s執行一次查詢,查詢t_schedule_trigger表,調度器是需要攜帶參數的,攜帶的參數在t_schedule_trigger_param表中,所以一開始調度器啓動的時候需要查詢t_schedule_trigger_param表數據,最終打印語句如上。
更改定時任務規則
t_schedule_trigger
t_schedule_trigger_param
作業類
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* @Description:
* @Author: cpc
* @Date: 2019-11-15 18:49
* @Version: V1.0
*/
@Component
@Slf4j
public class MyJobParam2 implements Job {
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
JobDetail jobDetail =
jobExecutionContext.getJobDetail();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
System.out.println(new Date().toLocaleString()+"-->MyJobParam2參數傳遞name="+jobDataMap.get("name")+",score="+
jobDataMap.get("score"));
}
}
控制檯:
修改一下對應的cron 表達式
控制檯:
頁面版定時任務curd
目錄結構(使用layui做的頁面)
後臺
TScheduleTriggerParamServiceImpl
package com.cpc.service.impl;
import com.cpc.dao.TScheduleTriggerParamMapper;
import com.cpc.entity.TScheduleTriggerParam;
import com.cpc.service.TScheduleTriggerParamService;
import com.cpc.util.Query;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* (TScheduleTriggerParam)表服務實現類
*
* @author cpc
* @since 2019-11-15 16:47:41
*/
@Service("tScheduleTriggerParamService")
public class TScheduleTriggerParamServiceImpl implements TScheduleTriggerParamService {
@Resource
private TScheduleTriggerParamMapper tScheduleTriggerParamDao;
/**
* 通過ID查詢單條數據
*
* @param paramId 主鍵
* @return 實例對象
*/
@Override
public TScheduleTriggerParam queryById(Integer paramId) {
return this.tScheduleTriggerParamDao.queryById(paramId);
}
/**
* 通過query對象查詢
*
* @param query 分頁查詢對象
* @return 對象列表
*/
@Override
public List<TScheduleTriggerParam> queryPager(Query query) {
return this.tScheduleTriggerParamDao.queryPager(query);
}
@Override
public List<TScheduleTriggerParam> queryScheduleParamLst(Integer triggerId) {
return this.tScheduleTriggerParamDao.queryScheduleParamLst(triggerId);
}
/**
* 新增數據
*
* @param tScheduleTriggerParam 實例對象
* @return 實例對象
*/
@Override
public int insert(TScheduleTriggerParam tScheduleTriggerParam) {
return this.tScheduleTriggerParamDao.insert(tScheduleTriggerParam);
}
/**
* 修改數據
*
* @param tScheduleTriggerParam 實例對象
* @return 實例對象
*/
@Override
public int update(TScheduleTriggerParam tScheduleTriggerParam) {
return this.tScheduleTriggerParamDao.update(tScheduleTriggerParam);
}
/**
* 通過主鍵刪除數據
*
* @param paramId 主鍵
* @return 是否成功
*/
@Override
public int deleteById(Integer paramId) {
return this.tScheduleTriggerParamDao.deleteById(paramId);
}
@Override
public int deleteByScheduleTriggerId(Integer scheduleTriggerId) {
return this.tScheduleTriggerParamDao.deleteByScheduleTriggerId(scheduleTriggerId);
}
}
TScheduleTriggerController
package com.cpc.controller;
import com.cpc.entity.TScheduleTrigger;
import com.cpc.service.TScheduleTriggerService;
import com.cpc.util.PageUtils;
import com.cpc.util.Query;
import com.cpc.util.R;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
import java.util.Map;
/**
* (TScheduleTrigger)表控制層
*
* @author cpc
* @since 2019-11-15 16:47:41
*/
@RestController
@RequestMapping("tScheduleTrigger")
public class TScheduleTriggerController {
@Autowired
private TScheduleTriggerService tScheduleTriggerService;
/**
* 分頁查詢
*
* @param params 請求參數集
* @return 結果集封裝對象
*/
@GetMapping("queryPager")
public PageUtils queryPager(@RequestParam Map<String, Object> params) {
Query query = new Query(params);
List<TScheduleTrigger> list = tScheduleTriggerService.queryPager(query);
return new PageUtils(list, query.getTotal());
}
/**
* 新增數據
*
* @param tScheduleTrigger 實例對象
* @return R
*/
@PostMapping("add")
public R add(TScheduleTrigger tScheduleTrigger) {
R r = R.update(tScheduleTriggerService.insert(tScheduleTrigger));
//刷新定時任務
this.tScheduleTriggerService.refreshScheduler();
return r;
}
/**
* 修改數據
*
* @param tScheduleTrigger 實例對象
* @return R
*/
@PostMapping("update")
public R update(TScheduleTrigger tScheduleTrigger) {
R r = R.update(tScheduleTriggerService.update(tScheduleTrigger));
//刷新定時任務
this.tScheduleTriggerService.refreshScheduler();
return r;
}
/**
* 刪除數據
*
* @param id 主鍵
* @return R
*/
@PostMapping("del")
public R del(Integer id) throws SchedulerException {
R r = R.update(tScheduleTriggerService.deleteById(id));
//刷新定時任務
this.tScheduleTriggerService.refreshScheduler();
return r;
}
@RequestMapping("toQuarztManagement")
public ModelAndView toQuarztManagement(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("quarztManagement");
return modelAndView;
}
}
MyWebAppConfigurer
package com.cpc.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
/**
* 資源映射路徑
*/
@Configuration
public class MyWebAppConfigurer extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
super.addResourceHandlers(registry);
}
}
前端代碼
導入layui 到static下,當然你也可以去找cdn 這個隨便
quarztManagement.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" th:href="@{/static/layui/css/layui.css}">
<script type="text/javascript"
th:src="@{/static/layui/layui.js}"></script>
<style>
.layui-table-cell .layui-form-checkbox[lay-skin="primary"] {
top: 50%;
transform: translateY(-50%);
}
</style>
</head>
<body class="childrenBody" style="padding: 10px;">
<!--搜索維度-->
<div class="layui-form">
<form class="layui-row layui-form" onsubmit="return false">
<div class="layui-inline layui-col-xs3 layui-col-sm3 layui-col-md3">
<label class="layui-form-label" style="width: 80px;">所在組織</label>
<div class="layui-input-inline">
<input type="text" id='jobGroupSearch' name="jobGroup" placeholder="請輸入流程所在組織" autocomplete="true"
class="layui-input">
</div>
</div>
<div class="layui-inline layui-col-xs2 layui-col-sm2 layui-col-md2">
<button class="layui-btn" lay-submit lay-filter="search">查詢</button>
<button class="layui-btn layui-btn-normal" data-type="add">新建</button>
</div>
</form>
</div>
<!--顯示數據的表格-->
<div class="demoTable" style="margin-top: 15px">
<div class=layui-form>
<table class="layui-table" id="quarzTable" lay-filter="quarzTable"
lay-data="{id: 'quarzTable'}">
</table>
</div>
</div>
<!--按鈕菜單-->
<script type="text/html" id="quarzTableRowBar">
<a class="layui-btn layui-btn-xs" lay-event="edit">編輯</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">刪除</a>
</script>
<!--彈出層 增加|修改通用-->
<div class="site-text" style="margin: 5%; display: none"
id="quarzFormBox" target="test123">
<form class="layui-form" onsubmit="return false" id="quarzForm"
lay-filter="quarzForm">
<input type="hidden" id="id" name="id"> <input
type="hidden" id="oldName" name="oldName">
<div class="layui-row" style="margin-top: 15px">
<div
class="layui-inline layui-col-xs12 layui-col-sm12 layui-col-md12">
<label class="layui-form-label">所在組織</label>
<div class="layui-input-block">
<input type="text" class="layui-input text_add my_clear"
id="jobGroup" name="jobGroup" required lay-verify="required">
</div>
</div>
</div>
<div class="layui-row" style="margin-top: 15px">
<div
class="layui-inline layui-col-xs12 layui-col-sm12 layui-col-md12">
<label class="layui-form-label">cron</label>
<div class="layui-input-block">
<input type="text" class="layui-input my_clear"
id="cron" name="cron" required
lay-verify="required">
</div>
</div>
</div>
<div class="layui-row" style="margin-top: 15px">
<div
class="layui-inline layui-col-xs12 layui-col-sm12 layui-col-md12">
<label class="layui-form-label">狀態</label>
<div class="layui-input-block">
<input type="checkbox" name="status" id="status"
lay-skin="switch" lay-filter="status" checked>
</div>
</div>
</div>
<div class="layui-row" style="margin-top: 15px">
<div
class="layui-inline layui-col-xs12 layui-col-sm12 layui-col-md12">
<label class="layui-form-label">job類限定名</label>
<div class="layui-input-block">
<input type="text" class="layui-input my_clear"
id="jobName" name="jobName" required
lay-verify="required">
</div>
</div>
</div>
<div class="layui-row" style="margin-top: 60px">
<div
class="layui-inline layui-col-xs12 layui-col-sm12 layui-col-md12"
style="text-align: right">
<button class="layui-btn" lay-submit lay-filter="quarzFormSubmit">提交</button>
</div>
</div>
</form>
</div>
<script th:src="@{/static/js/quarztManagement.js}"></script>
</body>
</HTML>
quarztManagement.js
var path = 'http://localhost';
//當前調用方法
var methodName = null;
//當前彈出
var index = null;
//當前操作 的 定時任務id
var scheduleTriggerId = null;
// 定時任務管理
layui.use(['table', 'form', 'jquery'],
// , 'from'
function () {
var table = layui.table,
form = layui.form;
// 第一個實例
table.render({
elem : '#quarzTable',
url : path + '/tScheduleTrigger/queryPager',// 數據接口
page : true, // 開啓分頁
limit: 5,
loading:true,
limits : [ 5, 10, 15 ],
cellMinWidth : 80,
cols : [ [
{
field : 'id',
title : '編號',
sort : true
},{
field : 'jobGroup',
title : '所在組織'
},{
field : 'cron',
title : 'cron'
},{
field : 'status',
title : '狀態'
},{
field : 'jobName',
title : 'jobName'
},{
fixed : 'right',
title : '操作',
align : 'center',
toolbar : '#quarzTableRowBar'
}
] ],
id : 'quarzTableReload'
});
// 上方菜單
var $ = layui.$,
active = {
add: function () { // 添加
layer.open({
type: 1,
title: '添加定時任務',
maxmin: true,
shadeClose: true, // 點擊遮罩關閉層
area: ['60%', '73%'],
content: $('#quarzFormBox'),
success: function (layero, n) {// 彈出後執行的函數
methodName = 'add';
index = n;
},
cancel:function (index, layero) {
$(".my_clear").val("");
layer.close(index)
}
});
}
}
$('.layui-form .layui-btn').on('click', function () {
var type = $(this).data('type');
active[type] ? active[type].call(this) : '';
});
//監聽查詢提交事件
form.on('submit(search)', function(event){
var data = event.field;
// 執行重載
table.reload('quarzTableReload', {
page: {
curr: 1 // 重新從第 1 頁開始
},
where:data
});
return false;
});
//監聽提交 (添加|修改)
form.on('submit(quarzFormSubmit)', function (event) {
var data = event.field;
data.status = document.getElementById('status').checked? 1: 0;
if(methodName == 'add'){ //如果是添加
$.post(path + '/tScheduleTrigger/add',data, function (data) {
if (data.code == 0) {
layer.alert('創建成功', {
icon: 1,
title: '提示'
}, function (i) {
layer.close(i);
layer.close(index);// 關閉彈出層
$(".my_clear").val("");
table.reload('quarzTableReload', {// 重載表格
page: {
curr: 1 // 重新從第 1 頁開始
}
})
})
}
})
}else{ //這是修改
$.post(path + '/tScheduleTrigger/update',data, function (data) {
if (data.code == 0) {
layer.alert('修改', {
icon: 1,
title: '提示'
}, function (i) {
layer.close(i);
layer.close(index);// 關閉彈出層
table.reload('quarzTableReload', {// 重載表格
page: {
curr: 1 // 重新從第 1 頁開始
}
})
})
}
})
}
return false;
});
// table菜單
table.on('tool(quarzTable)', function (obj) {
var data = obj.data;// 獲得當前行數據
var layEvent = obj.event; // 獲得 lay-event 對應的值(也可以是表頭的event參數對應的值)
var tr = obj.tr; // 獲得當前行 tr 的DOM對象
if (layEvent == 'edit') {// 編輯
layer.open({
type: 1,
title: '修改',
maxmin: true,
shadeClose: true, // 點擊遮罩關閉層
area: ['60%', '73%'],
content: $('#quarzFormBox'),
success: function (layero, n) {// 彈出後執行的函數
document.getElementById("status").checked = data.status == 1;
form.render(); //重新渲染
$("#id").val(data.id);
$("#cron").val(data.cron);
$("#status").val(data.status);
$("#jobName").val(data.jobName);
$("#jobGroup").val(data.jobGroup);
methodName = 'edit';
index = n;
},
cancel:function (index, layero) {
$(".my_clear").val("");
layer.close(index)
}
});
} else if (layEvent == 'del') {// 刪除
layer.confirm('確定刪除嗎???', {icon: 3, title: '提示'}, function (index) {
$.post( path + '/tScheduleTrigger/del', {
id: data.id
}, function (data) {
layer.close(index);
table.reload('quarzTableReload', {
page: {
curr: 1 // 重新從第 1 頁開始
}
})
});
})
}
});
});
訪問 http://localhost/tScheduleTrigger/toQuarztManagement
over