博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring整合quartz并持久化
阅读量:4455 次
发布时间:2019-06-07

本文共 12147 字,大约阅读时间需要 40 分钟。

spring整合quartz有两种方式:

一.常见是使用配置文件,将定时任务保存到内存中

简单示例:

overdueRecall
0 0 17 * * ?

这中方式配置简单,但是存在问题。

1.定时任务信息都报错在内存中,服务器重启会丢失信息

2.每个定时任务都是一串配置,定时任务多了不好管理

3.任务时间修改后要重新发布项目

二.quartz持久化(quartz1.6,最新版本的quartz稍有变动)

研究了一下quartz的相关文档,quartz本身就提供将任务和触发器持久化到数据库中的功能

详见一书

要实现很简单:

1.安装 Quartz 数据库表

Quartz 包括了所有被支持的数据库平台的 SQL 脚本。你能在 <quartz_home>/docs/dbTables 目录下找到那些 SQL 脚本,这里的 <quartz_home> 是解压Quartz 分发包后的目录。

2.配置 JobStoreTX

要告诉 Quartz 运行环境你想使用一个别的 JobStore 而不是默认的 RAMJobStore,你必须配置几个属性。配置它们的顺序无关紧要,只要保证在第一次运行程序之前都做了设置。

在你的classpath下加入一个配置文件quartz.properties参数如下:

表 6.3. 可用于设置 JobStoreTX 的配置属性

属性 默认值
org.quartz.jobStore.driverDelegateClass  
描述:能理解不同数据库系统中某一特定方言的驱动代理
org.quartz.jobStore.dataSource  
描述:用于 quartz.properties 中数据源的名称
org.quartz.jobStore.tablePrefix QRTZ_
描述:指定用于 Scheduler 的一套数据库表名的前缀。假如有不同的前缀,Scheduler 就能在同一数据库中使用不同的表。
org.quartz.jobStore.userProperties False
描述:"use properties" 标记指示着持久性 JobStore 所有在 JobDataMap 中的值都是字符串,因此能以 名-值 对的形式存储,而不用让更复杂的对象以序列化的形式存入 BLOB 列中。这样会更方便,因为让你避免了发生于序列化你的非字符串的类到 BLOB 时的有关类版本的问题。
org.quartz.jobStore.misfireThreshold 60000
描述:在 Trigger 被认为是错过触发之前,Scheduler 还容许 Trigger 通过它的下次触发时间的毫秒数(译者注:据原文翻译,真的不好理解,实际效果可参看: 我在评论中的实验)。默认值(假如你未在配置中存在这一属性条目) 是 60000(60 秒)。这个不仅限于JDBC-JobStore;它也可作为 RAMJobStore 的参数
org.quartz.jobStore.isClustered False
描述:设置为 true 打开集群特性。如果你有多个 Quartz 实例在用同一套数据库时,这个属性就必须设置为 true。
org.quartz.jobStore.clusterCheckinInterval 15000
描述:设置一个频度(毫秒),用于实例报告给集群中的其他实例。这会影响到侦测失败实例的敏捷度。它只用于设置了 isClustered 为 true 的时候。
org.quartz.jobStore.maxMisfiresToHandleAtATime 20
描述:这是 JobStore 能处理的错过触发的 Trigger 的最大数量。处理太多(超过两打) 很快会导致数据库表被锁定够长的时间,这样就妨碍了触发别的(还未错过触发) trigger 执行的性能。
org.quartz.jobStore.dontSetAutoCommitFalse False
描述:设置这个参数为 true 会告诉 Quartz 从数据源获取的连接后不要调用它的setAutoCommit(false) 方法。这在少些情况下是有帮助的,比如假如你有这样一个驱动,它会抱怨本来就是关闭的又来调用这个方法。这个属性默认值是 false,因为大多数的驱动都要求调用 setAutoCommit(false)。
org.quartz.jobStore.selectWithLockSQL SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE
描述:这必须是一个从 LOCKS 表查询一行并对这行记录加锁的 SQL 语句。假如未设置,默认值就是 SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE,这能在大部分数据库上工作。{0} 会在运行期间被前面你配置的TABLE_PREFIX 所替换。
org.quartz.jobStore.txIsolationLevelSerializable False
描述:值为 true 时告知 Quartz(当使用 JobStoreTX 或 CMT) 调用 JDBC 连接的setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE) 方法。这有助于阻止某些数据库在高负载和长时间事物时锁的超时。

示例,使用的是sqlserver数据库:

#org.quartz.scheduler.instanceName = Mscheduler    org.quartz.scheduler.instanceId = AUTO        #============================================================================    # Configure ThreadPool      #============================================================================        orgorg.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool    org.quartz.threadPool.threadCount = 3    org.quartz.threadPool.threadPriority = 5        #============================================================================    # Configure JobStore      #============================================================================        #orgorg.quartz.jobStore.class = org.quartz.simpl.RAMJobStore    orgorg.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX    orgorg.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.MSSQLDelegate    org.quartz.jobStore.useProperties = true    org.quartz.jobStore.dataSource = myDS    org.quartz.jobStore.tablePrefix = QRTZ_    org.quartz.jobStore.isClustered = false    org.quartz.jobStore.maxMisfiresToHandleAtATime=1    #============================================================================    # Configure Datasources      #============================================================================        org.quartz.dataSource.myDS.driver =  net.sourceforge.jtds.jdbc.Driver   org.quartz.dataSource.myDS.URL =  jdbc\:jtds\:sqlserver\://ip/dbk   org.quartz.dataSource.myDS.user = db2    org.quartz.dataSource.myDS.password = db2    org.quartz.dataSource.myDS.maxConnections = 5          #============================================================================    # Configure Plugins     #============================================================================        #orgorg.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingJobHistoryPlugin        #orgorg.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin    #org.quartz.plugin.jobInitializer.fileNames = jobs.xml    #org.quartz.plugin.jobInitializer.overWriteExistingJobs = true    #org.quartz.plugin.jobInitializer.failOnFileNotFound = true    #org.quartz.plugin.jobInitializer.scanInterval = 10    #org.quartz.plugin.jobInitializer.wrapInUserTransaction = false

然后可直接运行以下测试类:

package com.sursen.test.service;    import java.text.ParseException;    import org.apache.commons.lang.StringUtils;  import org.quartz.CronTrigger;  import org.quartz.Job;  import org.quartz.JobDetail;  import org.quartz.Scheduler;  import org.quartz.SchedulerException;  import org.quartz.SchedulerFactory;  import org.quartz.Trigger;  import org.quartz.impl.StdSchedulerFactory;      public class QuartzTest  {          private static SchedulerFactory sf = new StdSchedulerFactory();           private static String JOB_GROUP_NAME = "ddlib";          private static String TRIGGER_GROUP_NAME = "ddlibTrigger";                    /**添加一个定时任务,使用默认的任务组名,触发器名,触发器组名*/         public static void addJob(String jobName,Job job,String cronExpression)                                     throws SchedulerException, ParseException{             addJob(jobName,null,jobName,null,job,cronExpression);         }                 /**          * 添加一个定时任务         * @param jobName 任务名         * @param jobGroupName 任务组名         * @param triggerName 触发器名         * @param triggerGroupName 触发器组名         * @param job     任务         * @param cronExpression    时间设置,参考quartz说明文档         */         public static void addJob(String jobName,String jobGroupName,                                   String triggerName,String triggerGroupName,Job job,String cronExpression)                                     throws SchedulerException, ParseException{             if(StringUtils.isBlank(jobGroupName)){                 jobGroupName = JOB_GROUP_NAME;             }             if(StringUtils.isBlank(triggerGroupName)){                 triggerGroupName = TRIGGER_GROUP_NAME;             }             Scheduler sched = sf.getScheduler();             JobDetail jobDetail = new JobDetail(jobName, jobGroupName, job.getClass());//任务名,任务组,任务执行类             CronTrigger  trigger = new CronTrigger(jobName,triggerGroupName,cronExpression);//触发器名,触发器组,cron表达式             sched.scheduleJob(jobDetail,trigger);             //启动             if(!sched.isShutdown()){                 sched.start();             }         }                 /**          * 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名)         */         public static void modifyJobTime(String jobName,String cronExpression)                                        throws SchedulerException, ParseException{             modifyJobTime(jobName, null, cronExpression);         }                 /**         * 修改一个任务的触发时间         */         public static void modifyJobTime(String triggerName,String triggerGroupName,                                          String cronExpression)throws SchedulerException, ParseException{             if(StringUtils.isBlank(triggerGroupName)){                 triggerGroupName = TRIGGER_GROUP_NAME;             }             Scheduler sched = sf.getScheduler();             Trigger trigger = sched.getTrigger(triggerName,triggerGroupName);             if(trigger != null){                 CronTrigger ct = (CronTrigger)trigger;                 //修改时间                 ct.setCronExpression(cronExpression);                 //重启触发器                 sched.resumeTrigger(triggerName,triggerGroupName);             }         }                 /**移除一个任务和触发器(使用默认的任务组名,触发器名,触发器组名)*/         public static void removeJob(String jobName,String triggerName)                                     throws SchedulerException{             removeJob(jobName, null, triggerName, null);         }                 /**移除一个任务和触发器 */         public static void removeJob(String jobName,String jobGroupName,                                      String triggerName,String triggerGroupName)                                     throws SchedulerException{             if(StringUtils.isBlank(jobGroupName)){                 jobGroupName = JOB_GROUP_NAME;             }             if(StringUtils.isBlank(triggerGroupName)){                 triggerGroupName = TRIGGER_GROUP_NAME;             }             Scheduler sched = sf.getScheduler();             sched.pauseTrigger(triggerName,triggerGroupName);//停止触发器             sched.unscheduleJob(triggerName,triggerGroupName);//移除触发器             sched.deleteJob(jobName,jobGroupName);//删除任务         }                  public static void main(String[] args) throws SchedulerException, ParseException {  //      addJob("test", new TestJob(), "*/5 * * * * ?");  //      addJob("zht", new TestJob(), "*/10 * * * * ?");  //      removeJob("myJob","myJobGroup", "myTrigger","myTriggerGroup");          removeJob("test","test");          removeJob("zht","zht");                }  }

三.spring下quartz的持久化

在spring配置文件中加入以下代码

之后将

注入到所需要的service中即可.

其中

为注入的数据源,

如果使用这种方式那配置文件中的相关配置要注释掉.

或者将配置文件中信息写入到spring配置文件中:

quartzScheduler
AUTO
org.quartz.simpl.SimpleThreadPool
3
5
60000
org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.impl.jdbcjobstore.MSSQLDelegate
SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?
QRTZ_
true
20000

期间遇到的问题留作记录:

1.持久化时遇到报字段不正确或表不存在的问题

原因: 我使用的是quartz 2.1建的表而jar包是1.6的

解决方法: 将建表语句和jar包统一版本即可

2.报dataSource name不存在的问题

原因: quartz.properties中的 org.quartz.jobStore.dataSource = myDS 

忘记打开或者与数据源配置没有保持一致

解决方法: 打开保持一致即可

3.使用注入dataSource时报

Failure obtaining db row lock: 第 1 行: FOR UPDATE 子句仅允许用于 DECLARE CU...

改为数据源写在quartz.properties中解决

还不清楚为什么注入不行(解决)

org.quartz.jobStore.selectWithLockSQL SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE
描述:这必须是一个从 LOCKS 表查询一行并对这行记录加锁的 SQL 语句。假如未设置,默认值就是 SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE,这能在大部分数据库上工作。{0} 会在运行期间被前面你配置的TABLE_PREFIX 所替换。

 这条sql在sqlserver2000中不能运行修改为:

org.quartz.jobStore.selectWithLockSQL=SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?

问题解决

这种方式配置quartz.properties文件需要放在classpath下,而我们项目中都是放在Web-info下

可直接修改为:

4.在spring配置文件中

以这种方式将scheduler即StdScheduler scheduler注入到相关service中报找不到该property的错误
而service中是肯定存在的 : private StdScheduler scheduler;
改为spring的注解形式注入
@Autowired  private StdScheduler  scheduler;

也不行,再改为

@Resource  private StdScheduler  scheduler;

 或者

@Qualifier("scheduler")  private StdScheduler  scheduler;

则没有问题,具体原因还在查找

本文转自:http://haiziwoainixx.iteye.com/blog/1838055

转载于:https://www.cnblogs.com/dreammyle/p/4088475.html

你可能感兴趣的文章
NSBundle,UIImage,UIButton的使用
查看>>
vue-cli3 中console.log报错
查看>>
GridView 中Item项居中显示
查看>>
UML类图五种关系与代码的对应关系
查看>>
如何理解作用域
查看>>
从无到满意offer,你需要知道的那些事
查看>>
P1516 青蛙的约会 洛谷
查看>>
SDOI2011 染色
查看>>
JQuery EasyUI combobox动态添加option
查看>>
面向连接的TCP概述
查看>>
前端快捷方式 [记录]
查看>>
亲测可用,解决端口被占用的指令!!
查看>>
MySQL--视图、触发器、事务、存储过程、内置函数、流程控制、索引
查看>>
Django--数据库查询操作
查看>>
自定义配置文件的使用
查看>>
js-20170609-运算符
查看>>
算法笔记_065:分治法求逆序对(Java)
查看>>
MSP430FLASH小结
查看>>
STM32 ADC转换时间
查看>>
结合实际业务场景聊一聊MVP模式的应用
查看>>