how2j.cn

下载区
文件名 文件大小
quartz.rar 2m
使用站长秘制下载工具
步骤 1 : JDBCStore 概念   
步骤 2 : 建表 sql   
步骤 3 : 先运行,看到效果,再学习   
步骤 4 : 模仿和排错   
步骤 5 : 配置文件   
步骤 6 : MailJob   
步骤 7 : TestQuartz   

默认情况,Quartz的触发器,调度,任务等信息都是放在内存中的,叫做 RAMJobStore。 好处是快速,坏处是一旦系统重启,那么信息就丢失了,就得全部从头来过。
所以Quartz还提供了另一个方式,可以把这些信息存放在数据库做,叫做 JobStoreTX。 好处是就算系统重启了,目前运行到第几次了这些信息都是存放在数据库中的,那么就可以继续原来的步伐把计划任务无缝地继续做下去。 坏处就是性能上比内存慢一些,毕竟数据库读取总是要慢一些的。
为了能够把相关信息存放进 mysql 数据库里,必须手动建立数据库和表,使用如下 脚本就行了。
注: 这里使用的数据库名称是 quartz
DROP DATABASE IF EXISTS quartz; CREATE DATABASE quartz DEFAULT CHARACTER SET utf8; USE quartz; 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(100) NOT NULL, JOB_GROUP VARCHAR(100) 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) ); CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(100) NOT NULL, TRIGGER_GROUP VARCHAR(100) NOT NULL, JOB_NAME VARCHAR(100) NOT NULL, JOB_GROUP VARCHAR(100) 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(100) 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) ); CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(100) NOT NULL, TRIGGER_GROUP VARCHAR(100) 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) ); CREATE TABLE QRTZ_CRON_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(100) NOT NULL, TRIGGER_GROUP VARCHAR(100) NOT NULL, CRON_EXPRESSION VARCHAR(100) 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) ); CREATE TABLE QRTZ_SIMPROP_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(100) NOT NULL, TRIGGER_GROUP VARCHAR(100) 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) ); CREATE TABLE QRTZ_BLOB_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(100) NOT NULL, TRIGGER_GROUP VARCHAR(100) NOT NULL, BLOB_DATA BLOB 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) ); CREATE TABLE QRTZ_CALENDARS ( SCHED_NAME VARCHAR(120) NOT NULL, CALENDAR_NAME VARCHAR(100) NOT NULL, CALENDAR BLOB NOT NULL, PRIMARY KEY (SCHED_NAME,CALENDAR_NAME) ); CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_GROUP VARCHAR(100) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_FIRED_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, ENTRY_ID VARCHAR(95) NOT NULL, TRIGGER_NAME VARCHAR(100) NOT NULL, TRIGGER_GROUP VARCHAR(100) NOT NULL, INSTANCE_NAME VARCHAR(100) 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(100) NULL, JOB_GROUP VARCHAR(100) NULL, IS_NONCONCURRENT VARCHAR(1) NULL, REQUESTS_RECOVERY VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,ENTRY_ID) ); CREATE TABLE QRTZ_SCHEDULER_STATE ( SCHED_NAME VARCHAR(120) NOT NULL, INSTANCE_NAME VARCHAR(100) NOT NULL, LAST_CHECKIN_TIME BIGINT(13) NOT NULL, CHECKIN_INTERVAL BIGINT(13) NOT NULL, PRIMARY KEY (SCHED_NAME,INSTANCE_NAME) ); CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(120) NOT NULL, LOCK_NAME VARCHAR(40) NOT NULL, PRIMARY KEY (SCHED_NAME,LOCK_NAME) ); commit;
步骤 3 :

先运行,看到效果,再学习

edit
老规矩,先下载右上角的可运行项目,配置运行起来,确认可用之后,再学习做了哪些步骤以达到这样的效果。
运行 TestQuartz,为了看到如图所示的效果,要按照如下步骤进行:
1. 先运行一次,是不会看到红色的,因为此时数据库里还没有任务,会顺利执行
2. 经过20秒,自动结束。此时任务还未运行完毕(任务设计是总共10次,间隔15秒一次)。
3. 不用修改代码,再次执行,就会看到红色的字样,表示试图向数据库添加任务,但是发现任务已经在数据库里已经存在了,那么这个时候调度就会自动运行数据库里的任务,从而达到系统重启后自动衔接的效果
注: 项目需要用到的mysql 连接 jar 包已经包含在可运行项目里了
先运行,看到效果,再学习
在确保可运行项目能够正确无误地运行之后,再严格照着教程的步骤,对代码模仿一遍。
模仿过程难免代码有出入,导致无法得到期望的运行结果,此时此刻通过比较正确答案 ( 可运行项目 ) 和自己的代码,来定位问题所在。
采用这种方式,学习有效果,排错有效率,可以较为明显地提升学习速度,跨过学习路上的各个槛。

推荐使用diffmerge软件,进行文件夹比较。把你自己做的项目文件夹,和我的可运行项目文件夹进行比较。
这个软件很牛逼的,可以知道文件夹里哪两个文件不对,并且很明显地标记出来
这里提供了绿色安装和使用教程:diffmerge 下载和使用教程
在src下新建 quartz.properties 配置文件,里面指定使用 JobStoreTX 方式管理任务。 并且指定链接数据库的账号密码等信息
配置文件
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.scheduler.instanceName = MyScheduler org.quartz.threadPool.threadCount = 3 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.jobStore.dataSource = mysqlDatabase org.quartz.dataSource.mysqlDatabase.driver = com.mysql.jdbc.Driver org.quartz.dataSource.mysqlDatabase.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8 org.quartz.dataSource.mysqlDatabase.user = root org.quartz.dataSource.mysqlDatabase.password = admin org.quartz.dataSource.mysqlDatabase.maxConnections = 5
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = mysqlDatabase

org.quartz.dataSource.mysqlDatabase.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.mysqlDatabase.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8
org.quartz.dataSource.mysqlDatabase.user = root
org.quartz.dataSource.mysqlDatabase.password = admin
org.quartz.dataSource.mysqlDatabase.maxConnections = 5
这个和以前的一样,没什么变化
package com.how2java; import java.text.SimpleDateFormat; import java.util.Date; import org.quartz.DisallowConcurrentExecution; import org.quartz.Job; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; @DisallowConcurrentExecution public class MailJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { JobDetail detail = context.getJobDetail(); String email = detail.getJobDataMap().getString("email"); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); String now = sdf.format(new Date()); System.out.printf("给邮件地址 %s 发出了一封定时邮件, 当前时间是: %s (%s)%n" ,email, now,context.isRecovering()); } }
新增加了一个resumeJobFromDatabase 方法,当使用原来的方式增加任务报异常的时候,就直接从数据库重跑任务。

任务的触发也调整成了15秒一次,总共11次。
package com.how2java; import static org.quartz.JobBuilder.newJob; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; import static org.quartz.TriggerBuilder.newTrigger; import org.quartz.JobDetail; import org.quartz.ObjectAlreadyExistsException; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.impl.StdSchedulerFactory; public class TestQuartz { public static void main(String[] args) throws Exception { try { assginNewJob(); } catch (ObjectAlreadyExistsException e) { System.err.println("发现任务已经在数据库存在了,直接从数据库里运行:"+ e.getMessage()); // TODO Auto-generated catch block resumeJobFromDatabase(); } } private static void resumeJobFromDatabase() throws Exception { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.start(); // 等待200秒,让前面的任务都执行完了之后,再关闭调度器 Thread.sleep(200000); scheduler.shutdown(true); } private static void assginNewJob() throws SchedulerException, InterruptedException { // 创建调度器 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 定义一个触发器 Trigger trigger = newTrigger().withIdentity("trigger1", "group1") // 定义名称和所属的租 .startNow() .withSchedule(simpleSchedule().withIntervalInSeconds(15) // 每隔15秒执行一次 .withRepeatCount(10)) // 总共执行11次(第一次执行不基数) .build(); // 定义一个JobDetail JobDetail job = newJob(MailJob.class) // 指定干活的类MailJob .withIdentity("mailjob1", "mailgroup") // 定义任务名称和分组 .usingJobData("email", "admin@10086.com") // 定义属性 .build(); // 调度加入这个job scheduler.scheduleJob(job, trigger); // 启动 scheduler.start(); // 等待20秒,让前面的任务都执行完了之后,再关闭调度器 Thread.sleep(20000); scheduler.shutdown(true); } }


HOW2J公众号,关注后实时获知最新的教程和优惠活动,谢谢。


问答区域    
2019-10-31 使用了数据库,但是完全不懂是哪里进行了数据的操作啊?
恋晨

如题:代码的话,和之前没有使用数据库的代码并没有什么新的代码,也不懂内部的调度机制,怎么就调到数据库了,在哪一步有把数据存到数据库了,完全没有理解啊。




1 个答案

文正
答案时间:2020-03-19
想了解原理自己去看scheduler的源码,你需要这个功能就加了一个配置文件,配置文件里面就有某个参数是开启了这个功能,具体的数据库代码源码给你封装好了,想知道内部机制只能看源码。



回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到




2019-08-14 为什么mysql驱动8版本的不行啊?
java迷途小菜鸡

如题







回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到




2019-08-05 第一次启动就直接报错,看着好像是数据库那块的,但是网上查了好久,一直无果
2018-10-30 创建了数据库表就行了
2018-03-29 项目运行起来直接报错 sql语句语法问题是怎么回事啊 站长大人




提问之前请登陆
提问已经提交成功,正在审核。 请于 我的提问 处查看提问记录,谢谢
关于 工具和中间件-quartz-JDBC Store 的提问

尽量提供截图代码异常信息,有助于分析和解决问题。 也可进本站QQ群交流: 745032160
提问尽量提供完整的代码,环境描述,越是有利于问题的重现,您的问题越能更快得到解答。
对教程中代码有疑问,请提供是哪个步骤,哪一行有疑问,这样便于快速定位问题,提高问题得到解答的速度
在已经存在的几千个提问里,有相当大的比例,是因为使用了和站长不同版本的开发环境导致的,比如 jdk, eclpise, idea, mysql,tomcat 等等软件的版本不一致。
请使用和站长一样的版本,可以节约自己大量的学习时间。 站长把教学中用的软件版本整理了,都统一放在了这里, 方便大家下载: https://how2j.cn/k/helloworld/helloworld-version/1718.html

上传截图