spring整合quartz并持久化
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>
@Autowired
private StdScheduler scheduler;
也不行,再改为
@Resource
private StdScheduler scheduler;
或者
@Qualifier("scheduler")
private StdScheduler scheduler;
则没有问题,具体原因还在查找
本文转自:http://haiziwoainixx.iteye.com/blog/1838055
spring整合quartz并持久化的更多相关文章
- Spring整合Quartz实现持久化、动态设定时间
一.spring整合 网上一搜有很多整合的方式,这里我采用了其中的一种(暂时还没有对其他的方法研究过). 对于spring的整合其中的任务,spring提供了几个类.接口(这些类都实现了Job接口): ...
- Spring整合Quartz定时任务 在集群、分布式系统中的应用(Mysql数据库环境)
Spring整合Quartz定时任务 在集群.分布式系统中的应用(Mysql数据库环境) 转载:http://www.cnblogs.com/jiafuwei/p/6145280.html 单个Q ...
- 使用Spring整合Quartz轻松完成定时任务
一.背景 上次我们介绍了如何使用Spring Task进行完成定时任务的编写,这次我们使用Spring整合Quartz的方式来再一次实现定时任务的开发,以下奉上开发步骤及注意事项等. 二.开发环境及必 ...
- Spring整合Quartz定时任务执行2次,Spring定时任务执行2次
Spring整合Quartz定时任务执行2次,Spring定时任务执行2次 >>>>>>>>>>>>>>>&g ...
- spring整合quartz时间任务调度框架
spring整合quartz框架 1.创建maven工程 2.导入jar包(pom.xml) <dependencies> <dependency> <groupId&g ...
- Spring quartz Job不能依赖注入,Spring整合quartz Job任务不能注入
Spring quartz Job不能依赖注入,Spring整合quartz Job任务不能注入 Spring4整合quartz2.2.3中Job任务使用@Autowired不能注入 >> ...
- 使用spring整合Quartz实现—定时器
使用spring整合Quartz实现—定时器(Maven项目做演示) 不基于特定的基类的方法 一,开发环境以及依赖的jar包 Spring 4.2.6.RELEASE Maven 3.3.9 Jdk ...
- Spring整合Quartz分布式调度
前言 为了保证应用的高可用和高并发性,一般都会部署多个节点:对于定时任务,如果每个节点都执行自己的定时任务,一方面耗费了系统资源,另一方面有些任务多次执行,可能引发应用逻辑问题,所以需要一个分布式的调 ...
- Spring整合Quartz分布式调度(山东数漫江湖)
前言 为了保证应用的高可用和高并发性,一般都会部署多个节点:对于定时任务,如果每个节点都执行自己的定时任务,一方面耗费了系统资源,另一方面有些任务多次执行,可能引发应用逻辑问题,所以需要一个分布式的调 ...
随机推荐
- wget批量下载
wget -i download.txt 这样就会把download.txt里面列出的每个URL都下载下来. wget -c http://the.url.of/incomplete/file 使用断 ...
- Java 7 的7个新特性
1.对集合类的语言支持:(??) 2.自动资源管理: 3.改进的通用实例创建类型推断:(??) 4.数字字面量下划线支持:(√) 5.switch中使用string:(√) 6.二进制字面量:(√) ...
- 66. 有序数组构造二叉搜索树[array to binary search tree]
[本文链接] http://www.cnblogs.com/hellogiser/p/array-to-binary-search-tree.html [题目] 编写一个程序,把一个有序整数数组放到二 ...
- 40.扑克牌的顺子[Continuous cards]
[题目] 从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的.2-10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字. [分析] 这题目很有意思,是一个典型 ...
- (转)SQL Server 的事务和锁(二)-Range S-S锁
在这篇随笔中,我们的主要关注点在 Key-Range Lock.Key-Range Lock有 S-S.S-U.I-N.X-X几种情况.我们一个一个来说,力求明白.遗憾的是,这里可能会比较冗长,那么死 ...
- 【python】lamda表达式,map
一个很好的博客:http://blog.csdn.net/mathboylinlin/article/details/9413551 博客不让转载,我只摘抄了里面几个例子,更多内容到博客里去看 lam ...
- [Android Pro] 横竖屏切换时,禁止activity重新创建,android:configChanges="keyboardHidden|orientation" 不起作用
referece to : http://blog.csdn.net/mybook1122/article/details/24978025 这个网上搜索,很多结果都是: AndroidManifes ...
- c# 正则表达式 匹配回车
1 "." 匹配除 "\n" 之外的任何单个字符,一般用".*?"匹配不包括回车的任意字符. 2 我们在用正则表达式分析html或者是xml ...
- 【读书笔记】读《JavaScript模式》 - 函数复用模式之类式继承模式
实现类式继承的目标是通过构造函数Child()获取来自于另外一个构造函数Parent()的属性,从而创建对象. 1.类式继承模式#1 —— 默认方式(原型指向父函数实例) function Paren ...
- Kafka学习笔记(二):Partition分发策略
kafka版本0.8.2.1 Java客户端版本0.9.0.0 为了更好的实现负载均衡和消息的顺序性,Kafka Producer可以通过分发策略发送给指定的Partition.Kafka保证在par ...