spring整合quartz有两种方式:

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

简单示例:

<!-- 短信催还提醒任务调度 -->
<bean id="overdueRecall"
class="com.sursen.souba.ddlibserve.quartz.OverdueRecallTimerTask" /> <!--定义定时执行overdueRecallTimerTask 这个bean中的overdueRecall()方法-->
<bean id="overdueRecallTask"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject">
<ref bean="overdueRecall" />
</property>
<property name="targetMethod">
<value>overdueRecall</value>
</property>
</bean> <!--触发器的bean的设置,要触发的jobDetail是overdueRecallTask-->
<bean id="overdueRecallTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<ref bean="overdueRecallTask" />
</property>
<property name="cronExpression">
<!-- 每天17:00运行 -->
<value>0 0 17 * * ?</value>
<!--<value>0 05 18 * * ?</value>-->
</property>
</bean> <!--管理触发器的总设置,管理我们的触发器列表,可以在bean的list中放置多个触发器。-->
<bean autowire="no"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref local="overdueRecallTrigger" />
</list>
</property>
</bean>

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

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

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

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

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

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

详见<Quartz-Job-Scheduling-Framework>一书

要实现很简单:

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 通过它的下次触发时间的毫秒数(译者注:据原文翻译,真的不好理解,实际效果可参看:http://www.blogjava.net/Unmi/archive/2007/10/23/153413.html 我在评论中的实验)。默认值(假如你未在配置中存在这一属性条目) 是 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配置文件中加入以下代码

<!-- quartz持久化存储  -->
<bean id="scheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >
<!-- <property name="dataSource">
<ref bean="ddlibserveDataSource" />
</property> -->
<!-- <property name="schedulerName" value="Mscheduler" /> -->
<property name="configLocation" value="classpath:quartz.properties" />
<property name="applicationContextSchedulerContextKey"
value="applicationContextKey" />
<property name="autoStartup" value="true" />
</bean>

之后将

<bean id="scheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >

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

其中

<!--   <property name="dataSource">
<ref bean="ddlibserveDataSource" />
</property> -->

为注入的数据源,

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

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

<!-- quartz持久化存储  -->
<bean id="scheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >
<property name="dataSource">
<ref bean="ddlibserveDataSource" />
</property> <!--方式一 : quartz.properties配置文件是放在classpath下
<property name="configLocation" value="classpath:quartz.properties" /> --> <!--方式二 : quartz.properties配置文件是放在WEB-INF下
<property name="configLocation" value="/WEB-INF/quartz.properties" /> --> <!--方式三 : quartz.properties配置文件写入到配置XML中 -->
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.instanceName">quartzScheduler</prop>
<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
<prop key="org.quartz.threadPool.threadCount">3</prop>
<prop key="org.quartz.threadPool.threadPriority">5</prop>
<prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.MSSQLDelegate </prop>
<prop key="org.quartz.jobStore.selectWithLockSQL">SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?</prop>
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
<prop key="org.quartz.jobStore.isClustered">true</prop>
<prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop>
</props>
</property>
<property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
<property name="autoStartup" value="true" />
</bean>

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

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 = ?   

问题解决

 <property name="configLocation" value="classpath:quartz.properties" />

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

可直接修改为:

<property name="configLocation" value="/WEB-INF/quartz.properties" />  

4.在spring配置文件中

<bean id="quartzServiceImpl"
class="com.sursen.test.service.impl.QuartzServiceImpl">
<property name="scheduler">
<ref bean="scheduler" />
</property>
</bean>
以这种方式将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

spring整合quartz并持久化的更多相关文章

  1. Spring整合Quartz实现持久化、动态设定时间

    一.spring整合 网上一搜有很多整合的方式,这里我采用了其中的一种(暂时还没有对其他的方法研究过). 对于spring的整合其中的任务,spring提供了几个类.接口(这些类都实现了Job接口): ...

  2. Spring整合Quartz定时任务 在集群、分布式系统中的应用(Mysql数据库环境)

    Spring整合Quartz定时任务 在集群.分布式系统中的应用(Mysql数据库环境)   转载:http://www.cnblogs.com/jiafuwei/p/6145280.html 单个Q ...

  3. 使用Spring整合Quartz轻松完成定时任务

    一.背景 上次我们介绍了如何使用Spring Task进行完成定时任务的编写,这次我们使用Spring整合Quartz的方式来再一次实现定时任务的开发,以下奉上开发步骤及注意事项等. 二.开发环境及必 ...

  4. Spring整合Quartz定时任务执行2次,Spring定时任务执行2次

    Spring整合Quartz定时任务执行2次,Spring定时任务执行2次 >>>>>>>>>>>>>>>&g ...

  5. spring整合quartz时间任务调度框架

    spring整合quartz框架 1.创建maven工程 2.导入jar包(pom.xml) <dependencies> <dependency> <groupId&g ...

  6. Spring quartz Job不能依赖注入,Spring整合quartz Job任务不能注入

    Spring quartz Job不能依赖注入,Spring整合quartz Job任务不能注入 Spring4整合quartz2.2.3中Job任务使用@Autowired不能注入 >> ...

  7. 使用spring整合Quartz实现—定时器

    使用spring整合Quartz实现—定时器(Maven项目做演示) 不基于特定的基类的方法 一,开发环境以及依赖的jar包 Spring 4.2.6.RELEASE Maven 3.3.9 Jdk ...

  8. Spring整合Quartz分布式调度

    前言 为了保证应用的高可用和高并发性,一般都会部署多个节点:对于定时任务,如果每个节点都执行自己的定时任务,一方面耗费了系统资源,另一方面有些任务多次执行,可能引发应用逻辑问题,所以需要一个分布式的调 ...

  9. Spring整合Quartz分布式调度(山东数漫江湖)

    前言 为了保证应用的高可用和高并发性,一般都会部署多个节点:对于定时任务,如果每个节点都执行自己的定时任务,一方面耗费了系统资源,另一方面有些任务多次执行,可能引发应用逻辑问题,所以需要一个分布式的调 ...

随机推荐

  1. 《转》Visual Studio 2010 终极定制安装精简方法

    打开VS2010安装目录下的 Setup 文件夹,找到 baseline.dat 文件和 vs_setup.pdi 文件还有一个 locdata.ini 文件,是对应的. 这些都是文本文件,用记事本就 ...

  2. cocos2d-x 制作系统公告

    2013-12-15 21:57:33 下载地址:http://download.csdn.net/detail/jackyvincefu/6434549 (摘自:CSDN资源) CTestLayer ...

  3. Android studio 添加依赖

    以前添加依赖总是到github上下载源码,再添加源码到module的依赖当中,其实在studio中,应该使用maven库. 比如在github上看到了sliding-menu这个项目,就应该到mave ...

  4. 转载一篇关于ios copy的文章

    由于原文创作时间较早,一些内容不实用了,我对其进行了加工,去掉了一部分内容,添加了一点注释. 原文连接 http://www.cnblogs.com/ydhliphonedev/archive/201 ...

  5. mybatis随机生成可控制主键的方式

    mybatis生成的主键,一般都是用数据库的序列,可是还有不同的写法,比如: 一.NUMBER类型的主键 <insert id="insertPeriodical" para ...

  6. 【USACO】beads

    题目: You have a necklace of N red, white, or blue beads (3<=N<=350) some of which are red, othe ...

  7. Android Handler leak 分析及解决办法

    In Android, Handler classes should be static or leaks might occur, Messages enqueued on the applicat ...

  8. jdk

    mkdir java mv jdk1.7.0_71/ java/ [root@centos02 src]# java -version java version "1.7.0_71" ...

  9. MVC学习笔记---MVC导出excel(数据量大,非常耗时的,异步导出)

    要在ASP.NET MVC站点上做excel导出功能,但是要导出的excel文件比较大,有几十M,所以导出比较费时,为了不影响对界面的其它操作,我就采用异步的方式,后台开辟一个线程将excel导出到指 ...

  10. mysql中char,varchar,text区别总结

    具体对这三种类型的说明不做阐述可以查看mysql帮助文档. char的总结:      char最大长度是255字符,注意是字符数和字符集没关系.可以有默认值,尾部有空格会被截断.varchar的总结 ...