Quartz与SpringMVC的整合

简介

Quartz是一个完全由java编写的开源作业调度框架,为在Java应用程序中进行作业调度提供了简单却强大的机制。Quartz允许开发人员根据时间间隔来调度作业。它实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。这篇文章介绍了Quartz与SSM框架的整合,包括了持久化的方法和对于任务的一些简单操作。本文包括一个简单的由vuejs和ElementUI开发的前端任务管理页面,对于vuejs和ElementUI的用法,在我的另一篇文章Vue2.0+ElementUI+PageHelper实现的表格分页中进行了详细的介绍,并且有完整的代码可供参考,这里不再赘述。

正文

Quartz的引入

在pom.xml中加入如下代码:

  1. <dependency>
  2. <groupId>org.quartz-scheduler</groupId>
  3. <artifactId>quartz</artifactId>
  4. <version>2.2.</version>
  5. </dependency>
  6.  
  7. <dependency>
  8. <groupId>org.quartz-scheduler</groupId>
  9. <artifactId>quartz-jobs</artifactId>
  10. <version>2.2.</version>
  11. </dependency>

在web.xml中,加入如下代码:

  1. <context-param>
  2. <param-name>quartz:config-file</param-name>
  3. <param-value>scan-quartz.properties</param-value>
  4. </context-param>
  5. <context-param>
  6. <param-name>quartz:shutdown-on-unload</param-name>
  7. <param-value>true</param-value>
  8. </context-param>
  9. <context-param>
  10. <param-name>quartz:wait-on-shutdown</param-name>
  11. <param-value>false</param-value>
  12. </context-param>
  13. <context-param>
  14. <param-name>quartz:start-scheduler-on-load</param-name>
  15. <param-value>true</param-value>
  16. </context-param>
  17. <listener>
  18. <listener-class>
  19. org.quartz.ee.servlet.QuartzInitializerListener
  20. </listener-class>
  21. </listener>

scan-quartz.properties是quartz的配置文件,我们需要在resouces目录下加入新建一个名字为scan-quartz.properties的文件,然后在里面加入

  1. # 固定前缀org.quartz
  2. # 主要分为scheduler、threadPool、jobStore、plugin等部分
  3. #
  4. #
  5. org.quartz.scheduler.instanceName = DefaultQuartzScheduler
  6. org.quartz.scheduler.rmi.export = false
  7. org.quartz.scheduler.rmi.proxy = false
  8. org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
  9.  
  10. # 实例化ThreadPool时,使用的线程类为SimpleThreadPool
  11. org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
  12.  
  13. # threadCount和threadPriority将以setter的形式注入ThreadPool实例
  14. # 并发个数
  15. org.quartz.threadPool.threadCount =
  16. # 优先级
  17. org.quartz.threadPool.threadPriority =
  18. org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
  19.  
  20. org.quartz.jobStore.misfireThreshold =
  21.  
  22. # 默认存储在内存中
  23. org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

上面的最后一句话的作用是把任务内容存储在内存中。我们的程序是要做持久化任务,所以把上面的最后一句话注释掉,然后在下面加上

  1. #持久化
  2. org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
  3.  
  4. org.quartz.jobStore.tablePrefix = QRTZ_
  5.  
  6. org.quartz.jobStore.dataSource = qzDS
  7.  
  8. org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
  9.  
  10. org.quartz.dataSource.qzDS.URL = jdbc:mysql://190.0.1.88:3306/hello_test?useUnicode=true&characterEncoding=UTF-8
  11.  
  12. org.quartz.dataSource.qzDS.user = root
  13.  
  14. org.quartz.dataSource.qzDS.password = root
  15.  
  16. org.quartz.dataSource.qzDS.maxConnections =

上面代码主要是做了一些数据库的连接配置,如果大家用过mybatis,这些应该都能看懂。不过,事先要在数据库里创建一些表。具体的做法是打开数据库客户端,连接到某个数据库,然后新建一个查询。如果你用到的是mysql数据库,那么执行以下代码:

  1. #
  2. # Quartz seems to work best with the driver mm.mysql-2.0.-bin.jar
  3. #
  4. # PLEASE consider using mysql with innodb tables to avoid locking issues
  5. #
  6. # In your Quartz properties file, you'll need to set
  7. # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  8. #
  9.  
  10. DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
  11. DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
  12. DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
  13. DROP TABLE IF EXISTS QRTZ_LOCKS;
  14. DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
  15. DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
  16. DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
  17. DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
  18. DROP TABLE IF EXISTS QRTZ_TRIGGERS;
  19. DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
  20. DROP TABLE IF EXISTS QRTZ_CALENDARS;
  21.  
  22. CREATE TABLE QRTZ_JOB_DETAILS
  23. (
  24. SCHED_NAME VARCHAR() NOT NULL,
  25. JOB_NAME VARCHAR() NOT NULL,
  26. JOB_GROUP VARCHAR() NOT NULL,
  27. DESCRIPTION VARCHAR() NULL,
  28. JOB_CLASS_NAME VARCHAR() NOT NULL,
  29. IS_DURABLE VARCHAR() NOT NULL,
  30. IS_NONCONCURRENT VARCHAR() NOT NULL,
  31. IS_UPDATE_DATA VARCHAR() NOT NULL,
  32. REQUESTS_RECOVERY VARCHAR() NOT NULL,
  33. JOB_DATA BLOB NULL,
  34. PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
  35. );
  36.  
  37. CREATE TABLE QRTZ_TRIGGERS
  38. (
  39. SCHED_NAME VARCHAR() NOT NULL,
  40. TRIGGER_NAME VARCHAR() NOT NULL,
  41. TRIGGER_GROUP VARCHAR() NOT NULL,
  42. JOB_NAME VARCHAR() NOT NULL,
  43. JOB_GROUP VARCHAR() NOT NULL,
  44. DESCRIPTION VARCHAR() NULL,
  45. NEXT_FIRE_TIME BIGINT() NULL,
  46. PREV_FIRE_TIME BIGINT() NULL,
  47. PRIORITY INTEGER NULL,
  48. TRIGGER_STATE VARCHAR() NOT NULL,
  49. TRIGGER_TYPE VARCHAR() NOT NULL,
  50. START_TIME BIGINT() NOT NULL,
  51. END_TIME BIGINT() NULL,
  52. CALENDAR_NAME VARCHAR() NULL,
  53. MISFIRE_INSTR SMALLINT() NULL,
  54. JOB_DATA BLOB NULL,
  55. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  56. FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
  57. REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
  58. );
  59.  
  60. CREATE TABLE QRTZ_SIMPLE_TRIGGERS
  61. (
  62. SCHED_NAME VARCHAR() NOT NULL,
  63. TRIGGER_NAME VARCHAR() NOT NULL,
  64. TRIGGER_GROUP VARCHAR() NOT NULL,
  65. REPEAT_COUNT BIGINT() NOT NULL,
  66. REPEAT_INTERVAL BIGINT() NOT NULL,
  67. TIMES_TRIGGERED BIGINT() NOT NULL,
  68. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  69. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  70. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  71. );
  72.  
  73. CREATE TABLE QRTZ_CRON_TRIGGERS
  74. (
  75. SCHED_NAME VARCHAR() NOT NULL,
  76. TRIGGER_NAME VARCHAR() NOT NULL,
  77. TRIGGER_GROUP VARCHAR() NOT NULL,
  78. CRON_EXPRESSION VARCHAR() NOT NULL,
  79. TIME_ZONE_ID VARCHAR(),
  80. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  81. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  82. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  83. );
  84.  
  85. CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  86. (
  87. SCHED_NAME VARCHAR() NOT NULL,
  88. TRIGGER_NAME VARCHAR() NOT NULL,
  89. TRIGGER_GROUP VARCHAR() NOT NULL,
  90. STR_PROP_1 VARCHAR() NULL,
  91. STR_PROP_2 VARCHAR() NULL,
  92. STR_PROP_3 VARCHAR() NULL,
  93. INT_PROP_1 INT NULL,
  94. INT_PROP_2 INT NULL,
  95. LONG_PROP_1 BIGINT NULL,
  96. LONG_PROP_2 BIGINT NULL,
  97. DEC_PROP_1 NUMERIC(,) NULL,
  98. DEC_PROP_2 NUMERIC(,) NULL,
  99. BOOL_PROP_1 VARCHAR() NULL,
  100. BOOL_PROP_2 VARCHAR() NULL,
  101. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  102. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  103. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  104. );
  105.  
  106. CREATE TABLE QRTZ_BLOB_TRIGGERS
  107. (
  108. SCHED_NAME VARCHAR() NOT NULL,
  109. TRIGGER_NAME VARCHAR() NOT NULL,
  110. TRIGGER_GROUP VARCHAR() NOT NULL,
  111. BLOB_DATA BLOB NULL,
  112. PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  113. FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  114. REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  115. );
  116.  
  117. CREATE TABLE QRTZ_CALENDARS
  118. (
  119. SCHED_NAME VARCHAR() NOT NULL,
  120. CALENDAR_NAME VARCHAR() NOT NULL,
  121. CALENDAR BLOB NOT NULL,
  122. PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
  123. );
  124.  
  125. CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
  126. (
  127. SCHED_NAME VARCHAR() NOT NULL,
  128. TRIGGER_GROUP VARCHAR() NOT NULL,
  129. PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
  130. );
  131.  
  132. CREATE TABLE QRTZ_FIRED_TRIGGERS
  133. (
  134. SCHED_NAME VARCHAR() NOT NULL,
  135. ENTRY_ID VARCHAR() NOT NULL,
  136. TRIGGER_NAME VARCHAR() NOT NULL,
  137. TRIGGER_GROUP VARCHAR() NOT NULL,
  138. INSTANCE_NAME VARCHAR() NOT NULL,
  139. FIRED_TIME BIGINT() NOT NULL,
  140. SCHED_TIME BIGINT() NOT NULL,
  141. PRIORITY INTEGER NOT NULL,
  142. STATE VARCHAR() NOT NULL,
  143. JOB_NAME VARCHAR() NULL,
  144. JOB_GROUP VARCHAR() NULL,
  145. IS_NONCONCURRENT VARCHAR() NULL,
  146. REQUESTS_RECOVERY VARCHAR() NULL,
  147. PRIMARY KEY (SCHED_NAME,ENTRY_ID)
  148. );
  149.  
  150. CREATE TABLE QRTZ_SCHEDULER_STATE
  151. (
  152. SCHED_NAME VARCHAR() NOT NULL,
  153. INSTANCE_NAME VARCHAR() NOT NULL,
  154. LAST_CHECKIN_TIME BIGINT() NOT NULL,
  155. CHECKIN_INTERVAL BIGINT() NOT NULL,
  156. PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
  157. );
  158.  
  159. CREATE TABLE QRTZ_LOCKS
  160. (
  161. SCHED_NAME VARCHAR() NOT NULL,
  162. LOCK_NAME VARCHAR() NOT NULL,
  163. PRIMARY KEY (SCHED_NAME,LOCK_NAME)
  164. );
  165.  
  166. commit;

如果是其他的数据库,可以从quartz官网下载完整的文档,然后在docs目录下的dbTables文件夹里找到对应的创建表的方法。

后台代码

先创建两个任务类HelloJob和NewJob

  1. import java.util.Date;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import org.quartz.Job;
  5. import org.quartz.JobExecutionContext;
  6. import org.quartz.JobExecutionException;
  7.  
  8. public class HelloJob implements Job {
  9.  
  10. private static Logger _log = LoggerFactory.getLogger(HelloJob.class);
  11.  
  12. public HelloJob() {
  13.  
  14. }
  15.  
  16. public void execute(JobExecutionContext context)
  17. throws JobExecutionException {
  18. _log.error("Hello Job执行时间: " + new Date());
  19.  
  20. }
  21. }
  1. import java.util.Date;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import org.quartz.Job;
  5. import org.quartz.JobExecutionContext;
  6. import org.quartz.JobExecutionException;
  7.  
  8. public class NewJob implements Job {
  9.  
  10. private static Logger _log = LoggerFactory.getLogger(HelloJob.class);
  11.  
  12. public NewJob() {
  13.  
  14. }
  15.  
  16. public void execute(JobExecutionContext context)
  17. throws JobExecutionException {
  18. _log.error("New Job执行时间: " + new Date());
  19.  
  20. }
  21. }

这两个任务类的执行都是简单的打印执行时间。 
接下来是Controller,首先是添加任务

  1. @ResponseBody
  2. @RequestMapping(value="/addjob", method = RequestMethod.POST)
  3. public void addjob(@RequestParam(value="jobClassName")String jobClassName) throws Exception
  4. {
  5. setJob(jobClassName);
  6. }
  7.  
  8. public static void setJob(String jobClassName) throws Exception
  9. {
  10. // 通过SchedulerFactory获取一个调度器实例
  11. SchedulerFactory sf = new StdSchedulerFactory();
  12.  
  13. Scheduler sched = sf.getScheduler();
  14.  
  15. // 启动调度器
  16. sched.start();
  17.  
  18. switch (jobClassName)
  19. {
  20. case "HelloJob":
  21. JobDetail job = newJob(HelloJob.class).withIdentity("HelloJob", "group1").build();
  22. Trigger trigger = newTrigger().withIdentity("HelloJob", "group1").startNow().withSchedule(simpleSchedule()
  23. .withIntervalInSeconds()
  24. .repeatForever()).build();
  25. sched.scheduleJob(job, trigger);
  26. break;
  27.  
  28. case "NewJob":
  29. JobDetail job2 = newJob(NewJob.class).withIdentity("NewJob", "group1").build();
  30. Trigger trigger2 = newTrigger().withIdentity("NewJob", "group1").startNow().withSchedule(simpleSchedule()
  31. .withIntervalInSeconds()
  32. .repeatForever()).build();
  33. sched.scheduleJob(job2, trigger2);
  34. break;
  35.  
  36. default:
  37. break;
  38. }
  39. }

从前端接收一个名称参数,然后根据这个名称创建对应的任务实例。每个任务都有个触发器,这里两个任务都设置为每10秒钟运行一次,永久循环。每个任务实例都需要一个名称和组,对于两个任务实例来说,名称和组不能全部相同,因为它们在寻找任务实例中起到key的作用。

接下来是暂停任务。

  1. @ResponseBody
  2. @RequestMapping(value="/pausejob", method = RequestMethod.POST)
  3. public void pausejob(@RequestParam(value="jobClassName")String jobClassName) throws Exception
  4. {
  5. jobPause(jobClassName);
  6. }
  7.  
  8. public static void jobPause(String jobClassName) throws Exception
  9. {
  10. // 通过SchedulerFactory获取一个调度器实例
  11. SchedulerFactory sf = new StdSchedulerFactory();
  12. Scheduler sched = sf.getScheduler();
  13. switch (jobClassName) {
  14. case "HelloJob":
  15. sched.pauseJob(JobKey.jobKey("HelloJob", "group1"));
  16. break;
  17.  
  18. case "NewJob":
  19. sched.pauseJob(JobKey.jobKey("NewJob", "group1"));
  20. break;
  21.  
  22. default:
  23. break;
  24. }
  25. }

可以看到上文通过jobkey找到了这个任务的实例,然后进行暂停操作。 
接下来恢复。

  1. @ResponseBody
  2. @RequestMapping(value="/resumejob", method = RequestMethod.POST)
  3. public void resumejob(@RequestParam(value="jobClassName")String jobClassName) throws Exception
  4. {
  5. jobresume(jobClassName);
  6. }
  7.  
  8. public static void jobresume(String jobClassName) throws Exception
  9. {
  10. // 通过SchedulerFactory获取一个调度器实例
  11. SchedulerFactory sf = new StdSchedulerFactory();
  12. Scheduler sched = sf.getScheduler();
  13. if(sched != null){
  14. switch (jobClassName) {
  15. case "HelloJob":
  16. sched.resumeJob(JobKey.jobKey("HelloJob", "group1"));
  17. break;
  18.  
  19. case "NewJob":
  20. sched.resumeJob(JobKey.jobKey("NewJob", "group1"));
  21. break;
  22.  
  23. default:
  24. break;
  25. }
  26. }
  27. }

然后是删除操作。

  1. @ResponseBody
  2. @RequestMapping(value="/deletejob", method = RequestMethod.POST)
  3. public void deletejob(@RequestParam(value="jobClassName")String jobClassName) throws Exception
  4. {
  5. jobdelete(jobClassName);
  6. }
  7.  
  8. public static void jobdelete(String jobClassName) throws Exception
  9. {
  10. // 通过SchedulerFactory获取一个调度器实例
  11. SchedulerFactory sf = new StdSchedulerFactory();
  12. Scheduler sched = sf.getScheduler();
  13. switch (jobClassName) {
  14. case "HelloJob":
  15. sched.pauseTrigger(TriggerKey.triggerKey("HelloJob", "group1"));
  16. sched.unscheduleJob(TriggerKey.triggerKey("HelloJob", "group1"));
  17. sched.deleteJob(JobKey.jobKey("HelloJob", "group1"));
  18. break;
  19.  
  20. case "NewJob":
  21. sched.pauseTrigger(TriggerKey.triggerKey("NewJob", "group1"));
  22. sched.unscheduleJob(TriggerKey.triggerKey("NewJob", "group1"));
  23. sched.deleteJob(JobKey.jobKey("NewJob", "group1"));
  24. break;
  25.  
  26. default:
  27. break;
  28. }
  29.  
  30. }

删除操作前应该暂停该任务的触发器,并且停止该任务的执行。

最后是查询,这里用到了pageHelper插件,具体的也是写在了Vue2.0+ElementUI+PageHelper实现的表格分页中。

  1. @ResponseBody
  2. @RequestMapping(value="/queryjob")
  3. public Map<String, Object> queryjob(@RequestParam(value="pageNum")Integer pageNum, @RequestParam(value="pageSize")Integer pageSize)
  4. {
  5. Page<JobAndTrigger> jobAndTrigger = iJobAndTriggerService.getJobAndTriggerDetails(pageNum, pageSize);
  6. Map<String, Object> map = new HashMap<String, Object>();
  7. map.put("JobAndTrigger", jobAndTrigger);
  8. map.put("number", jobAndTrigger.getTotal());
  9. return map;
  10. }

这里主要的作用是将数据库里存储的关于任务和触发器的部分信息查询出来在前端显示。附上mybatis中的sql代码

  1. <select id="getJobAndTriggerDetails" resultType="com.hdnav.core.entity.JobAndTrigger">
  2. SELECT
  3. qrtz_job_details.JOB_NAME,
  4. qrtz_job_details.JOB_GROUP,
  5. qrtz_job_details.JOB_CLASS_NAME,
  6. qrtz_triggers.TRIGGER_NAME,
  7. qrtz_triggers.TRIGGER_GROUP,
  8. qrtz_simple_triggers.REPEAT_INTERVAL,
  9. qrtz_simple_triggers.TIMES_TRIGGERED
  10. FROM
  11. qrtz_job_details
  12. JOIN qrtz_triggers
  13. JOIN qrtz_simple_triggers ON qrtz_job_details.JOB_NAME = qrtz_triggers.JOB_NAME
  14. AND qrtz_triggers.TRIGGER_NAME = qrtz_simple_triggers.TRIGGER_NAME
  15. AND qrtz_triggers.TRIGGER_GROUP = qrtz_simple_triggers.TRIGGER_GROUP
  16. </select>

最后显示的效果如图所示

右键图片在新标签页打开图片可以看得更清晰。 
下面是控制台输出

  1. [com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
  2. [com.hdnav.core.HelloJob] - Hello Job执行时间: Thu Apr :: CST
  3. [com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
  4. [com.hdnav.core.HelloJob] - Hello Job执行时间: Thu Apr :: CST
  5. [com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
  6. [com.hdnav.core.HelloJob] - Hello Job执行时间: Thu Apr :: CST
  7. [com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
  8. [com.hdnav.core.HelloJob] - Hello Job执行时间: Thu Apr :: CST
  9. [com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
  10. [com.hdnav.core.HelloJob] - Hello Job执行时间: Thu Apr :: CST

可以看到两个任务都是每隔10秒执行一次。 
若点击暂停,则该任务暂停执行。点击恢复则会恢复执行。 
若执行删除操作,则数据表内不再有该任务的数据,只有重新添加任务才会显示,不过所有的数据都会重置。

  1. [com.hdnav.core.HelloJob] - Hello Job执行时间: Thu Apr :: CST
  2. [com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
  3. [com.hdnav.core.HelloJob] - Hello Job执行时间: Thu Apr :: CST
  4. [com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
  5. [com.hdnav.core.dao.JobAndTriggerMapper.getJobAndTriggerDetails] - ==> Preparing: SELECT qrtz_job_details.JOB_NAME, qrtz_job_details.JOB_GROUP, qrtz_job_details.JOB_CLASS_NAME, qrtz_triggers.TRIGGER_NAME, qrtz_triggers.TRIGGER_GROUP, qrtz_simple_triggers.REPEAT_INTERVAL, qrtz_simple_triggers.TIMES_TRIGGERED FROM qrtz_job_details JOIN qrtz_triggers JOIN qrtz_simple_triggers ON qrtz_job_details.JOB_NAME = qrtz_triggers.JOB_NAME AND qrtz_triggers.TRIGGER_NAME = qrtz_simple_triggers.TRIGGER_NAME AND qrtz_triggers.TRIGGER_GROUP = qrtz_simple_triggers.TRIGGER_GROUP LIMIT
  6. [com.hdnav.core.dao.JobAndTriggerMapper.getJobAndTriggerDetails] - ==> Parameters:
  7. [com.hdnav.core.dao.JobAndTriggerMapper.getJobAndTriggerDetails] - <== Total:
  8. [com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
  9. [com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST
  10. [com.hdnav.core.HelloJob] - New Job执行时间: Thu Apr :: CST

可以看到当执行删除HelloJob操作之后,控制台不再有指令。此时数据表中该条数据也被删除。

前端代码

前端代码比较简单,这里不再做过多的解释。

  1. <html>
  2. <head>
  3. <meta charset="UTF-8">
  4. <title>QuartzDemo</title>
  5. <link rel="stylesheet" href="/core/element-ui/lib/theme-default/index.css">
  6. <script src="./js/jquery-3.1.1.min.js"></script>
  7. <script src="./js/json2.js"></script>
  8. <script src="./js/vue.min.js"></script>
  9. <script src="./js/vue-resource.js"></script>
  10. <script src="./element-ui/lib/index.js"></script>
  11.  
  12. <style>
  13. #top {
  14. background:#20A0FF;
  15. padding:5px;
  16. overflow:hidden
  17. }
  18. </style>
  19.  
  20. </head>
  21. <body>
  22. <div id="test">
  23.  
  24. <div id="top">
  25. <el-button type="text" @click="search" style="color:white">查询</el-button>
  26. <el-button type="text" @click="add" style="color:white">添加</el-button>
  27. </span>
  28. </div>
  29.  
  30. <br/>
  31.  
  32. <div style="margin-top:15px">
  33.  
  34. <el-table
  35. ref="testTable"
  36. :data="tableData"
  37. style="width:100%"
  38. border
  39. >
  40. <el-table-column
  41. prop="job_NAME"
  42. label="任务名称"
  43. sortable
  44. show-overflow-tooltip>
  45. </el-table-column>
  46.  
  47. <el-table-column
  48. prop="job_GROUP"
  49. label="任务所在组"
  50. sortable>
  51. </el-table-column>
  52.  
  53. <el-table-column
  54. prop="job_CLASS_NAME"
  55. label="任务类名"
  56. sortable>
  57. </el-table-column>
  58.  
  59. <el-table-column
  60. prop="trigger_NAME"
  61. label="触发器名称"
  62. sortable>
  63. </el-table-column>
  64.  
  65. <el-table-column
  66. prop="trigger_GROUP"
  67. label="触发器所在组"
  68. sortable>
  69. </el-table-column>
  70.  
  71. <el-table-column
  72. prop="repeat_INTERVAL"
  73. label="触发间隔(毫秒)"
  74. sortable>
  75. </el-table-column>
  76.  
  77. <el-table-column
  78. prop="times_TRIGGERED"
  79. label="已触发次数"
  80. sortable>
  81. </el-table-column>
  82.  
  83. <el-table-column label="操作">
  84. <template scope="scope">
  85. <el-button
  86. size="small"
  87. type="warning"
  88. @click="handlePause(scope.$index, scope.row)">暂停</el-button>
  89.  
  90. <el-button
  91. size="small"
  92. type="info"
  93. @click="handleResume(scope.$index, scope.row)">恢复</el-button>
  94.  
  95. <el-button
  96. size="small"
  97. type="danger"
  98. @click="handleDelete(scope.$index, scope.row)">删除</el-button>
  99. </template>
  100. </el-table-column>
  101. </el-table>
  102.  
  103. <div align="center">
  104. <el-pagination
  105. @size-change="handleSizeChange"
  106. @current-change="handleCurrentChange"
  107. :current-page="currentPage"
  108. :page-sizes="[10, 20, 30, 40]"
  109. :page-size="pagesize"
  110. layout="total, sizes, prev, pager, next, jumper"
  111. :total="totalCount">
  112. </el-pagination>
  113. </div>
  114. </div>
  115. </div>
  116.  
  117. <footer align="center">
  118. <p>&copy; Quartz 任务管理</p>
  119. </footer>
  120.  
  121. <script>
  122. var vue = new Vue({
  123. el:"#test",
  124. data: {
  125. //表格当前页数据
  126. tableData: [],
  127.  
  128. //请求的URL
  129. url:'job/queryjob',
  130.  
  131. //默认每页数据量
  132. pagesize: ,
  133.  
  134. //当前页码
  135. currentPage: ,
  136.  
  137. //查询的页码
  138. start: ,
  139.  
  140. //默认数据总数
  141. totalCount: ,
  142. },
  143.  
  144. methods: {
  145.  
  146. //从服务器读取数据
  147. loadData: function(pageNum, pageSize){
  148. this.$http.get(this.url,{pageNum:pageNum, pageSize:pageSize}).then(function(res){
  149. this.tableData = res.data.JobAndTrigger;
  150. this.totalCount = res.data.number;
  151. },function(){
  152. console.log('failed');
  153. });
  154. },
  155.  
  156. //单行删除
  157. handleDelete: function(index, row) {
  158. this.$http.post('job/deletejob',{"jobClassName":row.job_NAME},{emulateJSON: true}).then(function(res){
  159. this.loadData( this.currentPage, this.pagesize);
  160. },function(){
  161. console.log('failed');
  162. });
  163. },
  164.  
  165. handlePause: function(index, row){
  166. this.$http.post('job/pausejob',{"jobClassName":row.job_NAME},{emulateJSON: true}).then(function(res){
  167. this.loadData( this.currentPage, this.pagesize);
  168. },function(){
  169. console.log('failed');
  170. });
  171. },
  172.  
  173. handleResume: function(index, row){
  174. this.$http.post('job/resumejob',{"jobClassName":row.job_NAME},{emulateJSON: true}).then(function(res){
  175. this.loadData( this.currentPage, this.pagesize);
  176. },function(){
  177. console.log('failed');
  178. });
  179. },
  180.  
  181. //搜索
  182. search: function(){
  183. this.loadData(this.currentPage, this.pagesize);
  184. },
  185.  
  186. //添加
  187. add: function(){
  188. this.$prompt('请输入名称', '提示', {
  189. confirmButtonText: '确定',
  190. cancelButtonText: '取消',
  191. }).then(({ value }) => {
  192. if(value==''||value==null)
  193. return;
  194. this.$http.post('job/addjob',{"jobClassName":value},{emulateJSON: true}).then(function(res){
  195. this.loadData(this.currentPage, this.pagesize);
  196. },function(){
  197. console.log('failed');
  198. });
  199. }).catch(() => {
  200.  
  201. });
  202.  
  203. },
  204.  
  205. //每页显示数据量变更
  206. handleSizeChange: function(val) {
  207. this.pagesize = val;
  208. this.loadData(this.currentPage, this.pagesize);
  209. },
  210.  
  211. //页码变更
  212. handleCurrentChange: function(val) {
  213. this.currentPage = val;
  214. this.loadData(this.currentPage, this.pagesize);
  215. },
  216.  
  217. },
  218.  
  219. });
  220.  
  221. //载入数据
  222. vue.loadData(vue.currentPage, vue.pagesize);
  223. </script>
  224.  
  225. </body>
  226. </html>

至此,一个简单的SSM框架与Quartz的整合工程便做好了。虽然是一个很简单的Demo,但是功能还算完整。对于人物的修改,后台的代码如下:

  1. @ResponseBody
  2. @RequestMapping(value="/reschedulejob", method = RequestMethod.POST)
  3. public void rescheduleJob(@RequestParam(value="jobClassName")String jobClassName) throws Exception
  4. {
  5. jobreschedule(jobClassName);
  6. }
  7.  
  8. public static void jobreschedule(String jobClassName) throws Exception
  9. {
  10. SchedulerFactory schedulerFactory = new StdSchedulerFactory();
  11. Scheduler scheduler = schedulerFactory.getScheduler();
  12. TriggerKey triggerKey = TriggerKey.triggerKey(jobClassName, "group1");
  13. Trigger newTrigger = scheduler.getTrigger(triggerKey);
  14. scheduler.rescheduleJob(triggerKey, newTrigger);
  15. scheduler.start();
  16. }

当然这里的trigger可以重新自定义,达到修改任务的效果。不过和前端结合相对来说比较麻烦,所以没有写前端的代码。做这个Demo的主要目的是为了更直观的感受到Quartz的可持久化特性,并且与SpringMVC相结合,可以从一个简单的前端界面直接去操作和管理这些任务。

结束语

这个小小的Demo所展现出的功能只是quartz茫茫多的功能的冰山一角,不过quartz是一个比较简单易懂的开源框架,文档相对来说很全面,在企业级的web程序开发中也完全可以胜任。相对于这个小小的Demo,还有很多更强大的功能等待着我们去探索。

Spring+SpringMVC+mybatis+Quartz整合的更多相关文章

  1. Spring+SpringMVC+MyBatis+easyUI整合基础篇(六)maven整合SSM

    写在前面的话   承接前文<Spring+SpringMVC+MyBatis+easyUI整合基础篇(五)讲一下maven>,本篇所讲述的是如何使用maven与原ssm项目整合,使得一个普 ...

  2. Spring+SpringMVC+MyBatis+easyUI整合基础篇(八)mysql中文查询bug修复

    写在前面的话 在测试搜索时出现的问题,mysql通过中文查询条件搜索不出数据,但是英文和数字可以搜索到记录,中文无返回记录.本文就是写一下发现问题的过程及解决方法.此bug在第一个项目中点这里还存在, ...

  3. Spring+SpringMVC+MyBatis+easyUI整合基础篇(十一)SVN服务器进阶

    日常啰嗦 上一篇文章<Spring+SpringMVC+MyBatis+easyUI整合基础篇(十)SVN搭建>简单的讲了一下SVN服务器的搭建,并没有详细的介绍配置文件及一些复杂的功能, ...

  4. Spring+SpringMVC+MyBatis+easyUI整合基础篇(十二)阶段总结

    不知不觉,已经到了基础篇的收尾阶段了,看着前面的十几篇文章,真的有点不敢相信,自己竟然真的坚持了下来,虽然过程中也有过懒散和焦虑,不过结果还是自己所希望的,克服了很多的问题,将自己的作品展现出来,也发 ...

  5. Spring+SpringMVC+MyBatis+easyUI整合优化篇(二)Log4j讲解与整合

    日常啰嗦 上一篇文章主要讲述了一下syso和Log间的一些区别与比较,重点是在项目的日志功能上,因此,承接前文<Spring+SpringMVC+MyBatis+easyUI整合优化篇(一)Sy ...

  6. Spring+SpringMVC+MyBatis+easyUI整合优化篇(四)单元测试实例

    日常啰嗦 前一篇文章<Spring+SpringMVC+MyBatis+easyUI整合优化篇(三)代码测试>讲了不为和不能两个状态,针对不为,只能自己调整心态了,而对于不能,本文会结合一 ...

  7. Spring+SpringMVC+MyBatis+easyUI整合优化篇(五)结合MockMvc进行服务端的单元测试

    日常啰嗦 承接前一篇文章<Spring+SpringMVC+MyBatis+easyUI整合优化篇(四)单元测试实例>,已经讲解了dao层和service层的单元测试,还有控制器这层也不能 ...

  8. Spring+SpringMVC+MyBatis+easyUI整合优化篇(七)图片上传功能

    日常啰嗦 前一篇文章<Spring+SpringMVC+MyBatis+easyUI整合优化篇(六)easyUI与富文本编辑器UEditor整合>讲了富文本编辑器UEditor的整合与使用 ...

  9. Spring+SpringMVC+MyBatis+easyUI整合优化篇(十三)数据层优化-表规范、索引优化

    本文提要 最近写的几篇文章都是关于数据层优化方面的,这几天也在想还有哪些地方可以优化改进,结合日志和项目代码发现,关于数据层的优化,还是有几个方面可以继续修改的,代码方面,整合了druid数据源也开启 ...

随机推荐

  1. php使用tcpdf实现在线PDF功能

    今天看书,发现有个例子就是实现php生成pdf格式文件的例子,所以扩展了下百度了下 找了个tcpdf Git上有地址,如果感冒自行下载 https://github.com/tecnickcom/tc ...

  2. Apache Maven 入门篇

    2017-11-09注释:IntelliJ IDEA 2017.2.5 x64 等新版本会安装maven,为了有好的体验 建议在安装目录找到IntelliJ IDEA 2017.2.5\plugins ...

  3. PHP 程序员学数据结构与算法之《栈》

    “要成高手,必练此功”.   要成为优秀的程序员,数据结构和算法是必修的内容.而现在的Web程序员使用传统算法和数据结构都比较少,因为很多算法都是包装好的,不用我们去操心具体的实现细节,如PHP的取栈 ...

  4. xe7 c++builder 日期时间头文件函数大全 date

    c++builde r时间日期函数大全,在头文件System.DateUtils.hpp,不过没有IncMonth,因为这个函数定义在System.SysUtils.hpp里头了,唉 date,dat ...

  5. ABAP-年月期间搜索帮助

    selection-screen begin of block block1 with frame title text-. parameters:p_mon1 like s031-spmon, p_ ...

  6. c++ cout cin, 命名空间

    cout<<a<<endl; cout<<a; 返回值其实就是一个输出流,(cout就是输出流) 上述语句等价于(cout<<a)<<end ...

  7. focusin 事件| focusout事件

    focusin 定义和用法 当元素(或在其内的任意元素)获得焦点时发生 focusin 事件. 当在元素或在其内的任意元素上发生 focus 事件时,focusin() 方法添加要运行的函数. 与 f ...

  8. mysqldump之不老将

    –add-drop-database 每个数据库创建之前添加drop数据库语句.mysqldump -uroot -p –all-databases –add-drop-database –add-d ...

  9. form表单中的encType属性

    enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码,它有三个值 1.application/x-www-form-urlencoded:表单数据被编码为名称/值对.这是默认的编码方式 ...

  10. JAVA中request.getParameterMap()用法笔记

    一. 根据Java规范:request.getParameterMap()返回的是一个Map类型的值,该返回值记录着前端(如jsp页面)所提交请求中的请求参数和请求参数值的映射关系.这个返回值有个特别 ...