javascript
java quartz 数据库_SpringBoot+Quartz+数据库存储
Spring整合Quartz
a、quartz調度框架是有內置表的
進入quartz的官網http://www.quartz-scheduler.org/,點擊Downloads,
下載后在目錄\docs\dbTables下有常用數據庫創建quartz表的腳本,例如:“tables_mysql.sql”
table_mysql.sql
table_mysql_innodb.sql
org.quartz-scheduler
quartz-jobs
2.2.1
org.springframework.boot
spring-boot-starter-quartz
com.mchange
c3p0
0.9.5.2
更換成Druid連接池
A、 引入依賴
com.alibaba
druid-spring-boot-starter
1.1.10
B、 導入DruidConnectionProvider.java
C、修改quartz.properties配置
#配置數據庫源
org.quartz.dataSource.qzDS.connectionProvider.class:
com.lingerqi.項目名.util.DruidConnectionProvider
在項目中添加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.lingerqi.quartz02.utils.DruidConnectionProvider
org.quartz.dataSource.qzDS.driver: com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL: jdbc:mysql://localhost:3306/mybatis_ssm?useUnicode=true&characterEncoding=utf8
org.quartz.dataSource.qzDS.user: root
org.quartz.dataSource.qzDS.password: 123
org.quartz.dataSource.qzDS.maxConnection: 10
要搞清楚一個問題:從數據庫讀取任務信息動態生成定時任務,和把quartz持久化到數據庫是沒有關系的。前者是我們自己定義的業務表,而后者是quartz使用自己的表來存儲信息。持久化到數據庫后,就算服務器重啟或是多個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)
);
package com.lingerqi.quartz02.utils;
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.zking.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;
}
}
QuartzConfiguration.java(quartz調度框架與spring框架整合的配置類,主要是要將org.quartz.Scheduler交給spring進行管理)
package com.lingerqi.quartz02.config;
import com.lingerqi.quartz02.utils.MyJobFactory;
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();
}
}
案例中需要觀察表數據變化的表
-- 自定義的業務表
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項目
pom依賴
org.quartz-scheduler
quartz-jobs
2.2.1
com.alibaba
druid-spring-boot-starter
1.1.10
src/main/java
**/*.xml
src/main/resources
*.properties
*.xml
*.yml
org.mybatis.generator
mybatis-generator-maven-plugin
1.3.2
mysql
mysql-connector-java
${mysql.version}
true
配置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/mybatis_ssm?useUnicode=true&characterEncoding=utf8
username: root
password: 123
druid:
#2.\u8FDE\u63A5\u6C60\u914D\u7F6E
#\u521D\u59CB\u5316\u8FDE\u63A5\u6C60\u7684\u8FDE\u63A5\u6570\u91CF \u5927\u5C0F\uFF0C\u6700\u5C0F\uFF0C\u6700\u5927
initial-size: 5
min-idle: 5
max-active: 20
#\u914D\u7F6E\u83B7\u53D6\u8FDE\u63A5\u7B49\u5F85\u8D85\u65F6\u7684\u65F6\u95F4
max-wait: 60000
#\u914D\u7F6E\u95F4\u9694\u591A\u4E45\u624D\u8FDB\u884C\u4E00\u6B21\u68C0\u6D4B\uFF0C\u68C0\u6D4B\u9700\u8981\u5173\u95ED\u7684\u7A7A\u95F2\u8FDE\u63A5\uFF0C\u5355\u4F4D\u662F\u6BEB\u79D2
time-between-eviction-runs-millis: 60000
# \u914D\u7F6E\u4E00\u4E2A\u8FDE\u63A5\u5728\u6C60\u4E2D\u6700\u5C0F\u751F\u5B58\u7684\u65F6\u95F4\uFF0C\u5355\u4F4D\u662F\u6BEB\u79D2
min-evictable-idle-time-millis: 30000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: true
test-on-return: false
# \u662F\u5426\u7F13\u5B58preparedStatement\uFF0C\u4E5F\u5C31\u662FPSCache \u5B98\u65B9\u5EFA\u8BAEMySQL\u4E0B\u5EFA\u8BAE\u5173\u95ED \u4E2A\u4EBA\u5EFA\u8BAE\u5982\u679C\u60F3\u7528SQL\u9632\u706B\u5899 \u5EFA\u8BAE\u6253\u5F00
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
# \u914D\u7F6E\u76D1\u63A7\u7EDF\u8BA1\u62E6\u622A\u7684filters\uFF0C\u53BB\u6389\u540E\u76D1\u63A7\u754C\u9762sql\u65E0\u6CD5\u7EDF\u8BA1\uFF0C'wall'\u7528\u4E8E\u9632\u706B\u5899
filter:
stat:
merge-sql: true
slow-sql-millis: 5000
#3.\u57FA\u7840\u76D1\u63A7\u914D\u7F6E
web-stat-filter:
enabled: true
url-pattern: /*
#\u8BBE\u7F6E\u4E0D\u7EDF\u8BA1\u54EA\u4E9BURL
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
#\u8BBE\u7F6E\u76D1\u63A7\u9875\u9762\u7684\u767B\u5F55\u540D\u548C\u5BC6\u7801
login-username: admin
login-password: admin
allow: 127.0.0.1
#deny: 192.168.1.100
#\u663E\u793A\u65E5\u5FD7
logging:
level:
com.lingerqi.quartz02.mapper: debug
Quartz02Application.java前面加上以下注解:
@MapperScan("com.lingerqi.quartz02.mapper")
@EnableTransactionManagement
@EnableScheduling
@SpringBootApplication
generatorConfig.xml
/p>
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
connectionURL="${jdbc.url}" userId="${jdbc.username}" password="${jdbc.password}"/>
targetProject="src/main/java">
targetProject="src/main/java">
targetProject="src/main/java" type="XMLMAPPER">
enableCountByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" enableUpdateByExample="false">
enableCountByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" enableUpdateByExample="false">
ScheduleTriggerMapper.java
/**
* 查詢觸發器中包含的所有任務
* @return
*/
List queryScheduleTriggerLst();
ScheduleTriggerParamMapper.java
/**
* 查詢出當前任務類對應所需的參數
* @param triggerId
* @return
*/
List queryScheduleParamLst(Integer triggerId);
spring自帶定時任務作業類ScheduleTriggerServiceImpl.java
package com.lingerqi.quartz02.service.impl;
import com.lingerqi.quartz02.entity.ScheduleTrigger;
import com.lingerqi.quartz02.entity.ScheduleTriggerParam;
import com.lingerqi.quartz02.mapper.ScheduleTriggerMapper;
import com.lingerqi.quartz02.mapper.ScheduleTriggerParamMapper;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ScheduleTriggerServiceImpl {
@Autowired
private ScheduleTriggerMapper scheduleTriggerMapper;
@Autowired
private ScheduleTriggerParamMapper scheduleTriggerParamMapper;
@Autowired
private Scheduler scheduler;
@Scheduled(cron = "0/10 * * * * ?")
public void refreshScheduler(){
try {
List scheduleTriggers =
scheduleTriggerMapper.queryScheduleTriggerLst();
if(null!=scheduleTriggers){
for (ScheduleTrigger scheduleTrigger : scheduleTriggers) {
String cron = scheduleTrigger.getCron(); //表達式
String jobName = scheduleTrigger.getJob_name(); //任務名稱
String jobGroup = scheduleTrigger.getJob_group(); //任務分組
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){
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 params =
scheduleTriggerParamMapper.queryScheduleParamLst(scheduleTrigger.getId());
for (ScheduleTriggerParam 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.lingerqi.quartz02.quartz;
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自帶的定時任務,每10s執行一次查詢
定時任務中攜帶參數
MyJob1
@Component
@Slf4j
public class MyJob1 implements Job {
@Autowired
private ScheduleTriggerParamService scheduleTriggerParamService;
@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表,調度器表中有兩個調度器,都是每5s執行一次,其中一個調度器是需要攜帶參數的,攜帶的參數在t_schedule_trigger_param表中,所以一開始調度器啟動的時候需要查詢t_schedule_trigger_param表數據,最終打印語句如上。
更改定時任務規則
MyJob2.java
@Component
@Slf4j
public class MyJob2 implements Job{
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
JobDetail jobDetail =
jobExecutionContext.getJobDetail();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
System.out.println(new Date().toLocaleString()+"-->MyJob2參數傳遞name="+jobDataMap.get("name")+",score="+
jobDataMap.get("score"));
}
}
程序執行spring自帶的定時任務,每10s執行一次查詢,查詢t_schedule_trigger表,將任務3的表達式由0/3 * * * * ?改成0/20 * * * * ?,最終結果如上
@Component
@Slf4j
public class MyJob2 implements Job{
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
JobDetail jobDetail =
jobExecutionContext.getJobDetail();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
System.out.println(new Date().toLocaleString()+"-->MyJob2參數傳遞name="+jobDataMap.get("name")+",score="+
jobDataMap.get("score"));
}
}
總結
以上是生活随笔為你收集整理的java quartz 数据库_SpringBoot+Quartz+数据库存储的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: helpsvc.exe是什么进程 作用是
- 下一篇: json 潜逃 结构体_JSON还原为结