文章目录

  • 概述
  • 操作步骤
    • 执行脚本建立对应的表
      • quartz数据表解释
    • 配置quartz.properties
  • 示例
  • 总结
  • 示例源码

概述

在默认情况下,Quartz将任务调度的运行信息保存在内存中。 这种方法提供了最佳的性能,因为在内存中的数据访问速度最快;不足之处就是却反数据的持久性,当程序中途停止或者系统崩溃时,所有运行的信息都会丢失。

比如我们希望安排一个执行100次的任务,如果执行到50次时系统崩溃了,系统重启时任务的执行计数器将从0开始。在大多数实际的应用中,我们往往并不需要保存任务调度的现场数据,因为很少需要规划一个指定执行次数的任务。对于仅执行一次的任务来说,其执行条件信息本身应该是已经持久化的业务数据,当执行完成后,条件信息也会相应改变。当然调度现场信息不仅仅是记录运行次数,还包括调度规则、JobDataMap中的数据等等。

如果确实需要持久化任务调度信息,Quartz允许你通过调整其属性文件,将这些信息保存到数据库中。使用数据库保存任务调度信息后,即使系统崩溃后重新启动,任务的调度信息将得到恢复。如前面所说的例子,执行50次崩溃后重新运行,计数器将从51开始计数。使用了数据库保存信息的任务称为持久化任务。


操作步骤

执行脚本建立对应的表

方式一: 从下载的压缩包中找 quartz-2.2.3\docs\dbTables中

方式二:https://github.com/quartz-scheduler/quartz/tree/quartz-1.8.x/docs/dbTables

这里我们使用Oracle数据库,我们执行tables_oracle.sql即可,共计11个表。

quartz数据表解释


配置quartz.properties

首先,我们需要在我们的属性文件中表明使用JobStoreTX:

org.quartz.jobStore.class = org.quartz.ompl.jdbcjobstore.JobStoreTX

然后我们需要配置能理解不同数据库系统中某一特定方言的驱动代理,选择选择对应数据库的代理类。

这些代理类在org.quar.impl.jdbcjobstore package或者其子下。
包括

  • DB2v6Delegate (for DB2 version 6 and earlier)
  • HSQLDBDelegate (for HSQLDB)
  • MSSQLDelegate (for Microsoft SQLServer)
  • PostgreSQLDelegate (for PostgreSQL)
  • WeblogicDelegate (for using JDBC drivers made by WebLogic)
  • OracleDelegate (for using Oracle)等

然后确定应用程序需要哪种类型的事务。 如果不需要将调度命令(例如添加和删除触发器)绑定到其他事务,那么可以通过使用JobStoreTX作为JobStore来管理事务(这是最常见的选择)。

如果需要Quartz与其他事务(即在J2EE应用程序服务器中)一起工作,那么您应该使用JobStoreCMT - 在这种情况下,Quartz将让应用程序服务器容器管理事务。

最后设置一个DataSource,JDBCJobStore可以从中获取与数据库的连接。 DataSources在Quartz属性中使用几种不同的方法之一进行定义。

  • 一种方法是让Quartz创建和管理DataSource本身 - 通过提供数据库的所有连接信息。
  • 另一种方法是让Quartz使用由Quartz正在运行的应用程序服务器管理的DataSource,通过提供JDBCJobStore DataSource的JNDI名称

要使用JDBCJobStore(并假定使用的是StdSchedulerFactory),首先需要将Quartz配置的JobStore类属性设置为org.quartz.impl.jdbcjobstore.JobStoreTXorg.quartz.impl.jdbcjobstore.JobStoreCMT

org.quartz.jobStore.useProperties配置参数设置为“true”(默认为false),以表示JDBCJobStore将JobDataMaps中的所有值都作为字符串,因此可以作为名称 - 值对存储 而不是在BLOB列中以其序列化形式存储更多复杂的对象。 从长远来看,这是更安全的,因为避免了将非String类序列化为BLOB的类版本问题。


示例

配置文件:

#============================================================================
# Configure Main Scheduler Properties
#============================================================================org.quartz.scheduler.instanceName: ArtisanScheduler
org.quartz.scheduler.instanceId: AUTO
org.quartz.scheduler.skipUpdateCheck: trueorg.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false#============================================================================
# Configure ThreadPool
#============================================================================org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 3
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true#============================================================================
# Configure JobStore
# (choose the right driverDelegateClass, for oracle:OracleDelegate ,for mysql:StdJDBCDelegate)
#============================================================================org.quartz.jobStore.misfireThreshold: 60000org.quartz.jobStore.class : org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass : org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.dataSource : qzDS
org.quartz.jobStore.tablePrefix : QRTZ_
org.quartz.jobStore.useProperties : true
org.quartz.jobStore.isClustered: false#============================================================================
# Configure Datasource  ORACLE
#============================================================================org.quartz.dataSource.qzDS.connectionProvider.class : com.xgj.quartz.quartzItself.saveInfoInDB.MyPoolingconnectionProviderorg.quartz.dataSource.qzDS.driverClassName : oracle.jdbc.driver.OracleDriver
org.quartz.dataSource.qzDS.url : jdbc:oracle:thin:@172.25.246.11:1521:testbed
org.quartz.dataSource.qzDS.username : cc
org.quartz.dataSource.qzDS.password : xxxx

Job

package com.xgj.quartz.quartzItself.saveInfoInDB;import java.text.SimpleDateFormat;
import java.util.Date;import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;public class MyPersistenceJob implements Job {@Overridepublic void execute(JobExecutionContext context)throws JobExecutionException {JobKey jobKey = context.getJobDetail().getKey();System.out.println("\n任务key "+ jobKey+ "执行时间:"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));}}

数据源连接

package com.xgj.quartz.quartzItself.saveInfoInDB;import java.sql.Connection;
import java.sql.SQLException;import org.apache.commons.dbcp.BasicDataSource;
import org.quartz.utils.ConnectionProvider;/*** * * @ClassName: MyPoolingconnectionProvider* * @Description: MyPoolingconnectionProvider数据源连接和quartz.properties配置文件。* *               数据源是自己定义的类,实现了quartz自带的ConnectionProvider类,如果不想使用它,*               你也可以选择其他数据源,*               比如Tomcat的DataSource,Spring的SimpleDriverDataSource等,自行选择.* *               DBCP数据源连接池的属性,这里仅仅使用了必须的配置,其他配置也显式设置,也可使用默认值,根据需要执行调整。* * @author: Mr.Yang* * @date: 2017年10月10日 下午9:49:07*/
public class MyPoolingconnectionProvider implements ConnectionProvider {/*** 使用apache dbcp数据源连接池*/private BasicDataSource datasource = new BasicDataSource();private String driverClassName;private String url;private String username;private String password;@Overridepublic Connection getConnection() throws SQLException {System.out.println("getConnection");return datasource.getConnection();}@Overridepublic void shutdown() throws SQLException {System.out.println("connection pool shutdown");datasource.close();}@Overridepublic void initialize() throws SQLException {try {System.out.println("inti connection");datasource.setDriverClassName(driverClassName);datasource.setUrl(url);datasource.setUsername(username);datasource.setPassword(password);} catch (Exception e) {e.printStackTrace();}}public void setDatasource(BasicDataSource datasource) {this.datasource = datasource;}public void setDriverClassName(String driverClassName) {this.driverClassName = driverClassName;}public void setUrl(String url) {this.url = url;}public void setUsername(String username) {this.username = username;}public void setPassword(String password) {this.password = password;}}

测试类

package com.xgj.quartz.quartzItself.saveInfoInDB;import java.text.SimpleDateFormat;
import java.util.Date;import org.quartz.DateBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.triggers.SimpleTriggerImpl;public class QuartzPersistenceTest {public static void main(String[] args) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");try {System.out.println("------- 初始化 ----------------------");// 通过调度器工厂获取调度器,初始化工程时须指定其使用我们自己的配置文件SchedulerFactory sf = new StdSchedulerFactory("quartz/quartz.properties");Scheduler sched = sf.getScheduler();// clear一下,因为使用数据库储存方式时,shutdown的时候没有清除,第二次运行会报Job is already// existsched.clear();System.out.println("------- 初始化完成 -----------");// 下一分钟开始执行Date runTime = DateBuilder.evenMinuteDate(new Date());System.out.println("------- Scheduling Job  -------------------");// 任务详情JobDetail job = JobBuilder.newJob(MyPersistenceJob.class).withIdentity("artisanJob", "artisanGroup").build();// 触发器 重复20+1次 间隔2秒SimpleTriggerImpl trigger = (SimpleTriggerImpl) TriggerBuilder.newTrigger().withIdentity("artisanTrigger", "artisanGroup").startAt(runTime).build();trigger.setRepeatCount(20);trigger.setRepeatInterval(2000);System.out.println("------- 当前时间:" + sdf.format(new Date())+ " -----------------");// 调度器、触发器、任务,三者关联sched.scheduleJob(job, trigger);System.out.println(job.getKey() + " 开始job运行时间:"+ sdf.format(runTime));// 调度启动sched.start();System.out.println("------- 开始调度器 Scheduler -----------------");System.out.println("------- 等待10秒-------------");try {Thread.sleep(1 * 10000L);} catch (Exception e) {}System.out.println("------- 关闭调度器 模拟异常退出---------------------");sched.shutdown(true);System.out.println("------- 异常退出 -----------------");SchedulerMetaData metaData = sched.getMetaData();System.out.println("目前执行了 "+ metaData.getNumberOfJobsExecuted() + " 个 jobs.");} catch (Exception e) {e.printStackTrace();}}}

通过关闭quartz模拟异常中断的场景

第一次运行日志:

------- 初始化 ----------------------
inti connection
INFO  StdSchedulerFactory - Using default implementation for ThreadExecutor
INFO  SimpleThreadPool - Job execution threads will use class loader of thread: main
INFO  SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
INFO  QuartzScheduler - Quartz Scheduler v.2.2.3 created.
INFO  JobStoreTX - Using thread monitor-based data access locking (synchronization).
INFO  JobStoreTX - JobStoreTX initialized.
INFO  QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.2.3) 'ArtisanScheduler' with instanceId 'NON_CLUSTERED'Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.NOT STARTED.Currently in standby mode.Number of jobs executed: 0Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 3 threads.Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is not clustered.INFO  StdSchedulerFactory - Quartz scheduler 'ArtisanScheduler' initialized from the specified file : 'quartz/quartz.properties' from the class resource path.
INFO  StdSchedulerFactory - Quartz scheduler version: 2.2.3
getConnection
------- 初始化完成 -----------
------- Scheduling Job  -------------------
------- 当前时间:2017-10-12 17:35:53 -----------------
getConnection
artisanGroup.artisanJob 开始job运行时间:2017-10-12 17:36:00
getConnection
INFO  JobStoreTX - Freed 0 triggers from 'acquired' / 'blocked' state.
INFO  JobStoreTX - Recovering 0 jobs that were in-progress at the time of the last shut-down.
INFO  JobStoreTX - Recovery complete.
INFO  JobStoreTX - Removed 0 'complete' triggers.
INFO  JobStoreTX - Removed 0 stale fired job entries.
INFO  QuartzScheduler - Scheduler ArtisanScheduler_$_NON_CLUSTERED started.
getConnection
------- 开始调度器 Scheduler -----------------
------- 等待10秒-------------
getConnection
getConnection
getConnection任务key artisanGroup.artisanJob执行时间:2017-10-12 17:36:00
getConnection
getConnection
getConnection任务key artisanGroup.artisanJob执行时间:2017-10-12 17:36:02
getConnection
------- 关闭调度器 模拟异常退出---------------------
INFO  QuartzScheduler - Scheduler ArtisanScheduler_$_NON_CLUSTERED shutting down.
INFO  QuartzScheduler - Scheduler ArtisanScheduler_$_NON_CLUSTERED paused.
connection pool shutdown
INFO  QuartzScheduler - Scheduler ArtisanScheduler_$_NON_CLUSTERED shutdown complete.
------- 异常退出 -----------------
目前执行了 2 个 jobs.

数据库表qrtz_simple_triggers数据:

将测试类中的下列代码屏蔽,使其正常运行

System.out.println("------- 等待10秒-------------");try {Thread.sleep(1 * 10000L);} catch (Exception e) {}System.out.println("------- 关闭调度器 模拟异常退出---------------------");sched.shutdown(true);System.out.println("------- 异常退出 -----------------");

再次运行

qrtz_simple_triggers 中的 times_triggered字段重0开始计算, 执行完成后,该表的数据为空。


总结

简单的10个字概括就是:未执行,插入; 执行过,删除。

示例源码

代码已托管到Github—> https://github.com/yangshangwei/SpringMaster

Quartz-任务调度信息持久化到DB中相关推荐

  1. (转)Quartz任务调度(1)概念例析快速入门

    http://blog.csdn.net/qwe6112071/article/details/50991563 Quartz框架需求引入 在现实开发中,我们常常会遇到需要系统在特定时刻完成特定任务的 ...

  2. java任务调度定时器,从零开始学 Java - Spring 使用 Quartz 任务调度定时器

    生活的味道 睁开眼看一看窗外的阳光,伸一个懒腰,拿起放在床一旁的水白开水,甜甜的味道,晃着尾巴东张西望的猫猫,在窗台上舞蹈.你向生活微笑,生活也向你微笑. 请你不要询问我的未来,这有些可笑.你问我你是 ...

  3. Quartz任务调度框架

    Quartz 基本概念及原理 作为一个优秀的开源调度框架,Quartz 具有以下特点: 1.强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求: 2. 灵活的应用方式,例如支持任务 ...

  4. Quartz-2.2.1 任务调度框架在Java项目中的使用实例

    < Quartz-2.2.1 任务调度框架在Java项目中的使用实例 > 本实例是基于Quartz 2.2.1 版本为中心,也是目前最新的Quartz任务调度框架. 目前在 J2EE 项目 ...

  5. python基金筛选_Python爬取基金的排名信息,写入excel中方便挑选基金

    原标题:Python爬取基金的排名信息,写入excel中方便挑选基金 基金是一种很好的理财方式,利用pyhton根据以往的跌幅情况进行基金选择,是一种很可靠的选择方式.本文以债券基金(稳定且风险较低) ...

  6. 在DB中存储图像-是或否?

    因此,我正在使用一个将图像大量存储在数据库中的应用程序. 您对此有何看法? 我更喜欢将位置存储在文件系统中,而不是直接将其存储在数据库中. 您认为优点/缺点是什么? #1楼 我尚未见任何人提及的一件事 ...

  7. 从零开始学 Java - Spring 使用 Quartz 任务调度定时器

    生活的味道 睁开眼看一看窗外的阳光,伸一个懒腰,拿起放在床一旁的水白开水,甜甜的味道,晃着尾巴东张西望的猫猫,在窗台上舞蹈.你向生活微笑,生活也向你微笑. 请你不要询问我的未来,这有些可笑.你问我你是 ...

  8. udt java_Java DB中的Java用户定义类型(UDT)

    udt java Java DB是基于Java编程语言和SQL的关系数据库管理系统. 这是Apache软件基金会的开源Derby项目的Oracle版本. Java SE 7 SDK中包含Java DB ...

  9. Java DB中的Java用户定义类型(UDT)

    Java DB是基于Java编程语言和SQL的关系数据库管理系统. 这是Apache软件基金会的开源Derby项目的Oracle版本. Java SE 7 SDK中包含Java DB. 用户定义类型( ...

最新文章

  1. .NET 线程问题汇总
  2. python连接服务器失败_python-查询期间失去与MySQL服务器的连接
  3. Mysql错误问题:ERROR 1005 (HY000): Can't create table 'crm_1.tbl_client' (errno: 150)
  4. 72 页 PPT,带你梳理神经网络完整架构(含 PyTorch 代码)
  5. 爬虫总结(四)-- 分布式爬虫
  6. Qt Creator管理工作区
  7. 《c语言从入门到精通》看书笔记——第3章 数据类型
  8. 在Grails 2.0中使用Servlet 3.0异步功能
  9. (90)AXI突发式读写时序和过程
  10. python高阶函数_python_bomb----高阶函数
  11. [转]35岁前程序员要规划好的四件事,健康居首位
  12. SQL SERVER 跟踪调优书籍
  13. 一次http请求中的信息
  14. [问题]apparmor 问题导致mysql切换datadir目录失败
  15. 《图解TCPIP》知识学习(1.3):协议
  16. win764位和32位有什么区别_win10系统32位和64位区别详解
  17. java字符串下标替换_java字符串下标替换
  18. github上这个项目有点意思,适合有女朋友的程序员
  19. 阿里云服务器安全组宝塔端口8888开放教程
  20. 如何给自己的照片制作水印

热门文章

  1. 如何用python画一个小房子?
  2. 8行代码求解非线性方程
  3. python为什么用号做注释符_Python为什么用#号作注释符?
  4. NLP-基础知识-005(专家系统)
  5. NTU生活:NTU景点
  6. MATLAB三维曲面绘图你不得不知道的...
  7. tableau必知必会之拖拽功能失效是怎么回事
  8. MySQL关系数据库
  9. 接口测试用例_【学习】接口测试用例编写和测试关注点
  10. Python/WSGI 应用快速入门--转