原文: https://howtodoinjava.com/spring-batch/quartz-h2-jdbcjobstore-example/

学习使用 Quartz 调度程序和 Quartz 用于记录作业和触发信息的持久性数据库存储记录来执行多个 Spring Batch 作业。 我在启用 Web 控制台的情况下使用H2数据库来查看数据库表中的数据。 您可以根据需要选择数据库。

项目结构

目标

在本教程中,我们将创建一个 Spring 应用程序并执行以下任务。

  1. 创建 2 个 Spring Batch 作业。 每个作业都有多个步骤。
  2. 创建 Quartz 作业和触发器,它们将运行在步骤 1 中创建的 SpringBatch。
  3. Quartz 作业和触发器将存储在文件系统中的持久性 H2 数据库中。
  4. 通过 H2 控制台验证存储在 DB 中的触发信息。
  5. 使用LoggingTriggerHistoryPlugin在日志中记录完整的触发器历史记录。

项目结构

Spring Batch   Quartz   H2 Jdbcjobstore 示例 - 图1

项目结构

Maven 依赖

我们需要具有以下依赖才能运行此项目。

pom.xml

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.howtodoinjava</groupId>
  6. <artifactId>App</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <packaging>jar</packaging>
  9. <name>App</name>
  10. <url>http://maven.apache.org</url>
  11. <parent>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-starter-parent</artifactId>
  14. <version>2.0.3.RELEASE</version>
  15. </parent>
  16. <properties>
  17. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-web</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.springframework.boot</groupId>
  26. <artifactId>spring-boot-starter-batch</artifactId>
  27. </dependency>
  28. <dependency>
  29. <groupId>org.springframework.boot</groupId>
  30. <artifactId>spring-boot-starter-quartz</artifactId>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework</groupId>
  34. <artifactId>spring-context-support</artifactId>
  35. </dependency>
  36. <dependency>
  37. <groupId>com.h2database</groupId>
  38. <artifactId>h2</artifactId>
  39. <scope>runtime</scope>
  40. </dependency>
  41. <dependency>
  42. <groupId>com.mchange</groupId>
  43. <artifactId>c3p0</artifactId>
  44. <version>0.9.5.2</version>
  45. </dependency>
  46. </dependencies>
  47. <build>
  48. <plugins>
  49. <plugin>
  50. <groupId>org.springframework.boot</groupId>
  51. <artifactId>spring-boot-maven-plugin</artifactId>
  52. </plugin>
  53. </plugins>
  54. </build>
  55. <repositories>
  56. <repository>
  57. <id>repository.spring.release</id>
  58. <name>Spring GA Repository</name>
  59. <url>http://repo.spring.io/release</url>
  60. </repository>
  61. </repositories>
  62. </project>

配置 Spring Batch 作业和任务

创建一些Tasklets和批处理作业以执行。

MyTaskOne.java and MyTaskTwo.java

  1. import org.springframework.batch.core.StepContribution;
  2. import org.springframework.batch.core.scope.context.ChunkContext;
  3. import org.springframework.batch.core.step.tasklet.Tasklet;
  4. import org.springframework.batch.repeat.RepeatStatus;
  5. public class MyTaskOne implements Tasklet {
  6. public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
  7. System.out.println("MyTaskOne start..");
  8. // ... your code
  9. System.out.println("MyTaskOne done..");
  10. return RepeatStatus.FINISHED;
  11. }
  12. }
  13. public class MyTaskTwo implements Tasklet {
  14. public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
  15. System.out.println("MyTaskTwo start..");
  16. // ... your code
  17. System.out.println("MyTaskTwo done..");
  18. return RepeatStatus.FINISHED;
  19. }
  20. }

BatchConfig.java

  1. import org.springframework.batch.core.Job;
  2. import org.springframework.batch.core.Step;
  3. import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
  4. import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
  5. import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import com.howtodoinjava.demo.tasks.MyTaskOne;
  10. import com.howtodoinjava.demo.tasks.MyTaskTwo;
  11. @Configuration
  12. @EnableBatchProcessing
  13. public class BatchConfig {
  14. @Autowired
  15. private JobBuilderFactory jobs;
  16. @Autowired
  17. private StepBuilderFactory steps;
  18. @Bean
  19. public Step stepOne(){
  20. return steps.get("stepOne")
  21. .tasklet(new MyTaskOne())
  22. .build();
  23. }
  24. @Bean
  25. public Step stepTwo(){
  26. return steps.get("stepTwo")
  27. .tasklet(new MyTaskTwo())
  28. .build();
  29. }
  30. @Bean(name="demoJobOne")
  31. public Job demoJobOne(){
  32. return jobs.get("demoJobOne")
  33. .start(stepOne())
  34. .next(stepTwo())
  35. .build();
  36. }
  37. @Bean(name="demoJobTwo")
  38. public Job demoJobTwo(){
  39. return jobs.get("demoJobTwo")
  40. .flow(stepOne())
  41. .build()
  42. .build();
  43. }
  44. }

application.properties

  1. #Disable batch job's auto start
  2. spring.batch.job.enabled=false

配置 Quartz 作业和触发器

现在创建 Quartz 作业,它将运行 SpringBatch。

CustomQuartzJob.java

  1. import org.quartz.DisallowConcurrentExecution;
  2. import org.quartz.JobExecutionContext;
  3. import org.quartz.JobExecutionException;
  4. import org.quartz.PersistJobDataAfterExecution;
  5. import org.springframework.batch.core.Job;
  6. import org.springframework.batch.core.JobParameters;
  7. import org.springframework.batch.core.JobParametersBuilder;
  8. import org.springframework.batch.core.configuration.JobLocator;
  9. import org.springframework.batch.core.launch.JobLauncher;
  10. import org.springframework.context.ApplicationContext;
  11. import org.springframework.scheduling.quartz.QuartzJobBean;
  12. @PersistJobDataAfterExecution
  13. @DisallowConcurrentExecution
  14. public class CustomQuartzJob extends QuartzJobBean {
  15. private String jobName;
  16. private JobLauncher jobLauncher;
  17. private JobLocator jobLocator;
  18. public String getJobName() {
  19. return jobName;
  20. }
  21. public void setJobName(String jobName) {
  22. this.jobName = jobName;
  23. }
  24. @Override
  25. protected void executeInternal(JobExecutionContext context) throws JobExecutionException
  26. {
  27. try
  28. {
  29. ApplicationContext applicationContext = (ApplicationContext) context.getScheduler().getContext().get("applicationContext");
  30. jobLocator = (JobLocator) applicationContext.getBean(JobLocator.class);
  31. jobLauncher = (JobLauncher) applicationContext.getBean(JobLauncher.class);
  32. Job job = jobLocator.getJob(jobName);
  33. JobParameters params = new JobParametersBuilder()
  34. .addString("JobID", String.valueOf(System.currentTimeMillis()))
  35. .toJobParameters();
  36. jobLauncher.run(job, params);
  37. }
  38. catch (Exception e)
  39. {
  40. e.printStackTrace();
  41. }
  42. }
  43. }

QuartzConfig.java

  1. import java.io.IOException;
  2. import java.util.Properties;
  3. import org.quartz.JobBuilder;
  4. import org.quartz.JobDataMap;
  5. import org.quartz.JobDetail;
  6. import org.quartz.SchedulerException;
  7. import org.quartz.SimpleScheduleBuilder;
  8. import org.quartz.Trigger;
  9. import org.quartz.TriggerBuilder;
  10. import org.springframework.batch.core.configuration.JobRegistry;
  11. import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
  12. import org.springframework.beans.factory.config.PropertiesFactoryBean;
  13. import org.springframework.context.annotation.Bean;
  14. import org.springframework.context.annotation.Configuration;
  15. import org.springframework.core.io.ClassPathResource;
  16. import org.springframework.scheduling.quartz.SchedulerFactoryBean;
  17. import com.howtodoinjava.demo.jobs.CustomQuartzJob;
  18. @Configuration
  19. public class QuartzConfig
  20. {
  21. @Bean
  22. public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
  23. JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
  24. jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
  25. return jobRegistryBeanPostProcessor;
  26. }
  27. @Bean
  28. public JobDetail jobOneDetail() {
  29. //Set Job data map
  30. JobDataMap jobDataMap = new JobDataMap();
  31. jobDataMap.put("jobName", "demoJobOne");
  32. return JobBuilder.newJob(CustomQuartzJob.class)
  33. .withIdentity("demoJobOne",null)
  34. .setJobData(jobDataMap)
  35. .storeDurably()
  36. .build();
  37. }
  38. @Bean
  39. public JobDetail jobTwoDetail() {
  40. //Set Job data map
  41. JobDataMap jobDataMap = new JobDataMap();
  42. jobDataMap.put("jobName", "demoJobTwo");
  43. return JobBuilder.newJob(CustomQuartzJob.class)
  44. .withIdentity("demoJobTwo",null)
  45. .setJobData(jobDataMap)
  46. .storeDurably()
  47. .build();
  48. }
  49. @Bean
  50. public Trigger jobOneTrigger()
  51. {
  52. SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder
  53. .simpleSchedule()
  54. .withIntervalInSeconds(10)
  55. .repeatForever();
  56. return TriggerBuilder
  57. .newTrigger()
  58. .forJob(jobOneDetail())
  59. .withIdentity("jobOneTrigger",null)
  60. .withSchedule(scheduleBuilder)
  61. .build();
  62. }
  63. @Bean
  64. public Trigger jobTwoTrigger()
  65. {
  66. SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder
  67. .simpleSchedule()
  68. .withIntervalInSeconds(20)
  69. .repeatForever();
  70. return TriggerBuilder
  71. .newTrigger()
  72. .forJob(jobTwoDetail())
  73. .withIdentity("jobTwoTrigger",null)
  74. .withSchedule(scheduleBuilder)
  75. .build();
  76. }
  77. @Bean
  78. public SchedulerFactoryBean schedulerFactoryBean() throws IOException, SchedulerException
  79. {
  80. SchedulerFactoryBean scheduler = new SchedulerFactoryBean();
  81. scheduler.setTriggers(jobOneTrigger(), jobTwoTrigger());
  82. scheduler.setQuartzProperties(quartzProperties());
  83. scheduler.setJobDetails(jobOneDetail(), jobTwoDetail());
  84. scheduler.setApplicationContextSchedulerContextKey("applicationContext");
  85. return scheduler;
  86. }
  87. public Properties quartzProperties() throws IOException
  88. {
  89. PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
  90. propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
  91. propertiesFactoryBean.afterPropertiesSet();
  92. return propertiesFactoryBean.getObject();
  93. }
  94. }

quartz.properties

  1. #scheduler name will be "MyScheduler"
  2. org.quartz.scheduler.instanceName=TestScheduler
  3. org.quartz.scheduler.instanceId=AUTO
  4. #maximum of 3 jobs can be run simultaneously
  5. org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
  6. org.quartz.threadPool.threadCount=50
  7. #Log trigger history
  8. org.quartz.plugin.triggerHistory.class=org.quartz.plugins.history.LoggingTriggerHistoryPlugin
  9. org.quartz.plugin.triggerHistory.triggerFiredMessage=Trigger [{1}.{0}] fired job [{6}.{5}] scheduled at: {2, date, dd-MM-yyyy HH:mm:ss.SSS}, next scheduled at: {3, date, dd-MM-yyyy HH:mm:ss.SSS}
  10. org.quartz.plugin.triggerHistory.triggerCompleteMessage=Trigger [{1}.{0}] completed firing job [{6}.{5}] with resulting trigger instruction code: {9}. Next scheduled at: {3, date, dd-MM-yyyy HH:mm:ss.SSS}
  11. org.quartz.plugin.triggerHistory.triggerMisfiredMessage=Trigger [{1}.{0}] misfired job [{6}.{5}]. Should have fired at: {3, date, dd-MM-yyyy HH:mm:ss.SSS}

配置 H2 数据库

使用quartz.properties文件中的属性将 quartz 配置为使用 H2 数据库。

quartz.properties

  1. #Quartz persistent jobStore config
  2. org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
  3. org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  4. org.quartz.jobStore.tablePrefix=QRTZ_
  5. org.quartz.jobStore.dataSource=myDS
  6. org.quartz.jobStore.useProperties=false
  7. org.quartz.jobStore.isClustered=false
  8. #Quartz dataSource
  9. org.quartz.dataSource.myDS.driver=org.h2.Driver
  10. org.quartz.dataSource.myDS.URL=jdbc:h2:file:~/h2/testdb;INIT=RUNSCRIPT FROM 'classpath:schema.sql'
  11. org.quartz.dataSource.myDS.user=sa
  12. org.quartz.dataSource.myDS.password =
  13. org.quartz.dataSource.myDS.maxConnections=5
  14. org.quartz.dataSource.myDS.validationQuery=select 1

Quartz 不会自动在数据库中创建必要的表,您需要在应用程序启动时创建它们。 我已经使用了上面的org.quartz.dataSource.myDS.URL属性。 模式查询位于schema.sql文件中。

schema.sql

  1. DROP TABLE IF EXISTS QRTZ_CALENDARS;
  2. DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
  3. DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
  4. DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
  5. DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
  6. DROP TABLE IF EXISTS QRTZ_LOCKS;
  7. DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
  8. DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
  9. DROP TABLE IF EXISTS qrtz_simprop_triggers;
  10. DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
  11. DROP TABLE IF EXISTS QRTZ_TRIGGERS;
  12. DROP TABLE IF EXISTS qrtz_simprop_triggers;
  13. CREATE TABLE QRTZ_CALENDARS (
  14. SCHED_NAME VARCHAR(120) NOT NULL,
  15. CALENDAR_NAME VARCHAR (200) NOT NULL ,
  16. CALENDAR IMAGE NOT NULL
  17. );
  18. CREATE TABLE QRTZ_CRON_TRIGGERS (
  19. SCHED_NAME VARCHAR(120) NOT NULL,
  20. TRIGGER_NAME VARCHAR (200) NOT NULL ,
  21. TRIGGER_GROUP VARCHAR (200) NOT NULL ,
  22. CRON_EXPRESSION VARCHAR (120) NOT NULL ,
  23. TIME_ZONE_ID VARCHAR (80)
  24. );
  25. CREATE TABLE QRTZ_FIRED_TRIGGERS (
  26. SCHED_NAME VARCHAR(120) NOT NULL,
  27. ENTRY_ID VARCHAR (95) NOT NULL ,
  28. TRIGGER_NAME VARCHAR (200) NOT NULL ,
  29. TRIGGER_GROUP VARCHAR (200) NOT NULL ,
  30. INSTANCE_NAME VARCHAR (200) NOT NULL ,
  31. FIRED_TIME BIGINT NOT NULL ,
  32. SCHED_TIME BIGINT NOT NULL ,
  33. PRIORITY INTEGER NOT NULL ,
  34. STATE VARCHAR (16) NOT NULL,
  35. JOB_NAME VARCHAR (200) NULL ,
  36. JOB_GROUP VARCHAR (200) NULL ,
  37. IS_NONCONCURRENT BOOLEAN NULL ,
  38. REQUESTS_RECOVERY BOOLEAN NULL
  39. );
  40. CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
  41. SCHED_NAME VARCHAR(120) NOT NULL,
  42. TRIGGER_GROUP VARCHAR (200) NOT NULL
  43. );
  44. CREATE TABLE QRTZ_SCHEDULER_STATE (
  45. SCHED_NAME VARCHAR(120) NOT NULL,
  46. INSTANCE_NAME VARCHAR (200) NOT NULL ,
  47. LAST_CHECKIN_TIME BIGINT NOT NULL ,
  48. CHECKIN_INTERVAL BIGINT NOT NULL
  49. );
  50. CREATE TABLE QRTZ_LOCKS (
  51. SCHED_NAME VARCHAR(120) NOT NULL,
  52. LOCK_NAME VARCHAR (40) NOT NULL
  53. );
  54. CREATE TABLE QRTZ_JOB_DETAILS (
  55. SCHED_NAME VARCHAR(120) NOT NULL,
  56. JOB_NAME VARCHAR (200) NOT NULL ,
  57. JOB_GROUP VARCHAR (200) NOT NULL ,
  58. DESCRIPTION VARCHAR (250) NULL ,
  59. JOB_CLASS_NAME VARCHAR (250) NOT NULL ,
  60. IS_DURABLE BOOLEAN NOT NULL ,
  61. IS_NONCONCURRENT BOOLEAN NOT NULL ,
  62. IS_UPDATE_DATA BOOLEAN NOT NULL ,
  63. REQUESTS_RECOVERY BOOLEAN NOT NULL ,
  64. JOB_DATA IMAGE NULL
  65. );
  66. CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
  67. SCHED_NAME VARCHAR(120) NOT NULL,
  68. TRIGGER_NAME VARCHAR (200) NOT NULL ,
  69. TRIGGER_GROUP VARCHAR (200) NOT NULL ,
  70. REPEAT_COUNT BIGINT NOT NULL ,
  71. REPEAT_INTERVAL BIGINT NOT NULL ,
  72. TIMES_TRIGGERED BIGINT NOT NULL
  73. );
  74. CREATE TABLE qrtz_simprop_triggers
  75. (
  76. SCHED_NAME VARCHAR(120) NOT NULL,
  77. TRIGGER_NAME VARCHAR(200) NOT NULL,
  78. TRIGGER_GROUP VARCHAR(200) NOT NULL,
  79. STR_PROP_1 VARCHAR(512) NULL,
  80. STR_PROP_2 VARCHAR(512) NULL,
  81. STR_PROP_3 VARCHAR(512) NULL,
  82. INT_PROP_1 INTEGER NULL,
  83. INT_PROP_2 INTEGER NULL,
  84. LONG_PROP_1 BIGINT NULL,
  85. LONG_PROP_2 BIGINT NULL,
  86. DEC_PROP_1 NUMERIC(13,4) NULL,
  87. DEC_PROP_2 NUMERIC(13,4) NULL,
  88. BOOL_PROP_1 BOOLEAN NULL,
  89. BOOL_PROP_2 BOOLEAN NULL,
  90. );
  91. CREATE TABLE QRTZ_BLOB_TRIGGERS (
  92. SCHED_NAME VARCHAR(120) NOT NULL,
  93. TRIGGER_NAME VARCHAR (200) NOT NULL ,
  94. TRIGGER_GROUP VARCHAR (200) NOT NULL ,
  95. BLOB_DATA IMAGE NULL
  96. );
  97. CREATE TABLE QRTZ_TRIGGERS (
  98. SCHED_NAME VARCHAR(120) NOT NULL,
  99. TRIGGER_NAME VARCHAR (200) NOT NULL ,
  100. TRIGGER_GROUP VARCHAR (200) NOT NULL ,
  101. JOB_NAME VARCHAR (200) NOT NULL ,
  102. JOB_GROUP VARCHAR (200) NOT NULL ,
  103. DESCRIPTION VARCHAR (250) NULL ,
  104. NEXT_FIRE_TIME BIGINT NULL ,
  105. PREV_FIRE_TIME BIGINT NULL ,
  106. PRIORITY INTEGER NULL ,
  107. TRIGGER_STATE VARCHAR (16) NOT NULL ,
  108. TRIGGER_TYPE VARCHAR (8) NOT NULL ,
  109. START_TIME BIGINT NOT NULL ,
  110. END_TIME BIGINT NULL ,
  111. CALENDAR_NAME VARCHAR (200) NULL ,
  112. MISFIRE_INSTR SMALLINT NULL ,
  113. JOB_DATA IMAGE NULL
  114. );
  115. ALTER TABLE QRTZ_CALENDARS ADD
  116. CONSTRAINT PK_QRTZ_CALENDARS PRIMARY KEY
  117. (
  118. SCHED_NAME,
  119. CALENDAR_NAME
  120. );
  121. ALTER TABLE QRTZ_CRON_TRIGGERS ADD
  122. CONSTRAINT PK_QRTZ_CRON_TRIGGERS PRIMARY KEY
  123. (
  124. SCHED_NAME,
  125. TRIGGER_NAME,
  126. TRIGGER_GROUP
  127. );
  128. ALTER TABLE QRTZ_FIRED_TRIGGERS ADD
  129. CONSTRAINT PK_QRTZ_FIRED_TRIGGERS PRIMARY KEY
  130. (
  131. SCHED_NAME,
  132. ENTRY_ID
  133. );
  134. ALTER TABLE QRTZ_PAUSED_TRIGGER_GRPS ADD
  135. CONSTRAINT PK_QRTZ_PAUSED_TRIGGER_GRPS PRIMARY KEY
  136. (
  137. SCHED_NAME,
  138. TRIGGER_GROUP
  139. );
  140. ALTER TABLE QRTZ_SCHEDULER_STATE ADD
  141. CONSTRAINT PK_QRTZ_SCHEDULER_STATE PRIMARY KEY
  142. (
  143. SCHED_NAME,
  144. INSTANCE_NAME
  145. );
  146. ALTER TABLE QRTZ_LOCKS ADD
  147. CONSTRAINT PK_QRTZ_LOCKS PRIMARY KEY
  148. (
  149. SCHED_NAME,
  150. LOCK_NAME
  151. );
  152. ALTER TABLE QRTZ_JOB_DETAILS ADD
  153. CONSTRAINT PK_QRTZ_JOB_DETAILS PRIMARY KEY
  154. (
  155. SCHED_NAME,
  156. JOB_NAME,
  157. JOB_GROUP
  158. );
  159. ALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD
  160. CONSTRAINT PK_QRTZ_SIMPLE_TRIGGERS PRIMARY KEY
  161. (
  162. SCHED_NAME,
  163. TRIGGER_NAME,
  164. TRIGGER_GROUP
  165. );
  166. ALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD
  167. CONSTRAINT PK_QRTZ_SIMPROP_TRIGGERS PRIMARY KEY
  168. (
  169. SCHED_NAME,
  170. TRIGGER_NAME,
  171. TRIGGER_GROUP
  172. );
  173. ALTER TABLE QRTZ_TRIGGERS ADD
  174. CONSTRAINT PK_QRTZ_TRIGGERS PRIMARY KEY
  175. (
  176. SCHED_NAME,
  177. TRIGGER_NAME,
  178. TRIGGER_GROUP
  179. );
  180. ALTER TABLE QRTZ_CRON_TRIGGERS ADD
  181. CONSTRAINT FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY
  182. (
  183. SCHED_NAME,
  184. TRIGGER_NAME,
  185. TRIGGER_GROUP
  186. ) REFERENCES QRTZ_TRIGGERS (
  187. SCHED_NAME,
  188. TRIGGER_NAME,
  189. TRIGGER_GROUP
  190. ) ON DELETE CASCADE;
  191. ALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD
  192. CONSTRAINT FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY
  193. (
  194. SCHED_NAME,
  195. TRIGGER_NAME,
  196. TRIGGER_GROUP
  197. ) REFERENCES QRTZ_TRIGGERS (
  198. SCHED_NAME,
  199. TRIGGER_NAME,
  200. TRIGGER_GROUP
  201. ) ON DELETE CASCADE;
  202. ALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD
  203. CONSTRAINT FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY
  204. (
  205. SCHED_NAME,
  206. TRIGGER_NAME,
  207. TRIGGER_GROUP
  208. ) REFERENCES QRTZ_TRIGGERS (
  209. SCHED_NAME,
  210. TRIGGER_NAME,
  211. TRIGGER_GROUP
  212. ) ON DELETE CASCADE;
  213. ALTER TABLE QRTZ_TRIGGERS ADD
  214. CONSTRAINT FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS FOREIGN KEY
  215. (
  216. SCHED_NAME,
  217. JOB_NAME,
  218. JOB_GROUP
  219. ) REFERENCES QRTZ_JOB_DETAILS (
  220. SCHED_NAME,
  221. JOB_NAME,
  222. JOB_GROUP
  223. );
  224. COMMIT;

运行演示

配置日志记录并启动应用程序

为了格式化日志语句,我创建了自定义logback.xml

logback.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration scan="true">
  3. <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
  4. <encoder>
  5. <charset>UTF-8</charset>
  6. <Pattern>%d{yyyy-MM-dd HH:mm:ss} %p %X{TXNID} - %m%n</Pattern>
  7. </encoder>
  8. </appender>
  9. <root level="INFO">
  10. <appender-ref ref="consoleAppender" />
  11. </root>
  12. </configuration>

作为 Spring 启动应用程序启动该应用程序。

App.java

  1. package com.howtodoinjava.demo;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. @SpringBootApplication
  5. public class App
  6. {
  7. public static void main(String[] args)
  8. {
  9. SpringApplication.run(App.class, args);
  10. }
  11. }

验证作业执行

在服务器控制台中验证正在执行的 Spring Batch 作业。

Console

  1. . ____ _ __ _ _
  2. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
  3. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
  4. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
  5. ' |____| .__|_| |_|_| |_\__, | / / / /
  6. =========|_|==============|___/=/_/_/_/
  7. :: Spring Boot :: (v2.0.3.RELEASE)
  8. 2018-07-06 09:29:07 INFO - Starting App on MACHINE-ID with PID 13456 (C:\Users\lokesh\workspace\App\target\classes started by lokesh in C:\Users\lokesh\workspace\App)
  9. 2018-07-06 09:29:07 INFO - No active profile set, falling back to default profiles: default
  10. 2018-07-06 09:29:08 INFO - Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@23529fee: startup date [Fri Jul 06 09:29:08 IST 2018]; root of context hierarchy
  11. 2018-07-06 09:29:10 INFO - Bean 'quartzConfig' of type [com.howtodoinjava.demo.config.QuartzConfig$EnhancerBySpringCGLIB$78eca775] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  12. 2018-07-06 09:29:10 INFO - Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$EnhancerBySpringCGLIB$11c34f14] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  13. 2018-07-06 09:29:10 INFO - Bean 'org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari' of type [org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  14. 2018-07-06 09:29:10 INFO - Bean 'org.springframework.boot.context.properties.ConversionServiceDeducer$Factory' of type [org.springframework.boot.context.properties.ConversionServiceDeducer$Factory] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  15. 2018-07-06 09:29:10 INFO - Bean 'spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties' of type [org.springframework.boot.autoconfigure.jdbc.DataSourceProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  16. 2018-07-06 09:29:10 INFO - Bean 'dataSource' of type [com.zaxxer.hikari.HikariDataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  17. 2018-07-06 09:29:10 INFO - HikariPool-1 - Starting...
  18. 2018-07-06 09:29:11 INFO - HikariPool-1 - Start completed.
  19. 2018-07-06 09:29:11 INFO - Executing SQL script from URL [file:/C:/Users/lokesh/workspace/App/target/classes/schema.sql]
  20. 2018-07-06 09:29:11 INFO - Executed SQL script from URL [file:/C:/Users/lokesh/workspace/App/target/classes/schema.sql] in 66 ms.
  21. 2018-07-06 09:29:11 INFO - Bean 'org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker' of type [org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  22. 2018-07-06 09:29:11 INFO - Bean 'org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration' of type [org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$EnhancerBySpringCGLIB$1186297a] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  23. 2018-07-06 09:29:11 INFO - Bean 'jobRegistry' of type [com.sun.proxy.$Proxy49] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  24. 2018-07-06 09:29:12 INFO - Tomcat initialized with port(s): 8080 (http)
  25. 2018-07-06 09:29:12 INFO - Initializing ProtocolHandler ["http-nio-8080"]
  26. 2018-07-06 09:29:12 INFO - Starting service [Tomcat]
  27. 2018-07-06 09:29:12 INFO - Starting Servlet Engine: Apache Tomcat/8.5.31
  28. 2018-07-06 09:29:12 INFO - Initializing Spring embedded WebApplicationContext
  29. 2018-07-06 09:29:12 INFO - Root WebApplicationContext: initialization completed in 4505 ms
  30. 2018-07-06 09:29:12 INFO - Servlet webServlet mapped to [/console/*]
  31. 2018-07-06 09:29:12 INFO - Servlet dispatcherServlet mapped to [/]
  32. 2018-07-06 09:29:12 INFO - Mapping filter: 'characterEncodingFilter' to: [/*]
  33. 2018-07-06 09:29:12 INFO - Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
  34. 2018-07-06 09:29:12 INFO - Mapping filter: 'httpPutFormContentFilter' to: [/*]
  35. 2018-07-06 09:29:12 INFO - Mapping filter: 'requestContextFilter' to: [/*]
  36. 2018-07-06 09:29:13 INFO - No database type set, using meta data indicating: H2
  37. 2018-07-06 09:29:13 INFO - No TaskExecutor has been set, defaulting to synchronous executor.
  38. 2018-07-06 09:29:13 INFO - Executing SQL script from class path resource [org/quartz/impl/jdbcjobstore/tables_h2.sql]
  39. 2018-07-06 09:29:13 INFO - Executed SQL script from class path resource [org/quartz/impl/jdbcjobstore/tables_h2.sql] in 21 ms.
  40. 2018-07-06 09:29:13 INFO - Using ConnectionProvider class 'org.quartz.utils.C3p0PoolingConnectionProvider' for data source 'myDS'
  41. 2018-07-06 09:29:13 INFO - MLog clients using slf4j logging.
  42. 2018-07-06 09:29:13 INFO - Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
  43. 2018-07-06 09:29:13 INFO - Using default implementation for ThreadExecutor
  44. 2018-07-06 09:29:14 INFO - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
  45. 2018-07-06 09:29:14 INFO - Quartz Scheduler v.2.3.0 created.
  46. 2018-07-06 09:29:14 INFO - Using thread monitor-based data access locking (synchronization).
  47. 2018-07-06 09:29:14 INFO - JobStoreTX initialized.
  48. 2018-07-06 09:29:14 INFO - Scheduler meta-data: Quartz Scheduler (v2.3.0) 'schedulerFactoryBean' with instanceId 'NON_CLUSTERED'
  49. Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  50. NOT STARTED.
  51. Currently in standby mode.
  52. Number of jobs executed: 0
  53. Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 50 threads.
  54. Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is not clustered.
  55. 2018-07-06 09:29:14 INFO - Quartz scheduler 'schedulerFactoryBean' initialized from an externally provided properties instance.
  56. 2018-07-06 09:29:14 INFO - Quartz scheduler version: 2.3.0
  57. 2018-07-06 09:29:14 INFO - JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@3db64bd4
  58. 2018-07-06 09:29:14 INFO - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 8jgsvm9wug78a3djgl4a|73ab3aac, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> org.h2.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 8jgsvm9wug78a3djgl4a|73ab3aac, idleConnectionTestPeriod -> 50, initialPoolSize -> 3, jdbcUrl -> jdbc:h2:file:~/h2/testdb;INIT=RUNSCRIPT FROM 'classpath:schema.sql', maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 5, maxStatements -> 0, maxStatementsPerConnection -> 120, minPoolSize -> 1, numHelperThreads -> 3, preferredTestQuery -> select 1, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> true, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
  59. 2018-07-06 09:29:15 INFO - Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
  60. 2018-07-06 09:29:15 INFO - Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@23529fee: startup date [Fri Jul 06 09:29:08 IST 2018]; root of context hierarchy
  61. 2018-07-06 09:29:15 INFO - Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
  62. 2018-07-06 09:29:15 INFO - Mapped "{[/error],produces=}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
  63. 2018-07-06 09:29:15 INFO - Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
  64. 2018-07-06 09:29:15 INFO - Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
  65. 2018-07-06 09:29:15 INFO - Executing SQL script from class path resource [org/springframework/batch/core/schema-h2.sql]
  66. 2018-07-06 09:29:16 INFO - Executed SQL script from class path resource [org/springframework/batch/core/schema-h2.sql] in 105 ms.
  67. 2018-07-06 09:29:16 INFO - Registering beans for JMX exposure on startup
  68. 2018-07-06 09:29:16 INFO - Bean with name 'dataSource' has been autodetected for JMX exposure
  69. 2018-07-06 09:29:16 INFO - Located MBean 'dataSource': registering with JMX server as MBean [com.zaxxer.hikari:name=dataSource,type=HikariDataSource]
  70. 2018-07-06 09:29:16 INFO - Starting beans in phase 2147483647
  71. 2018-07-06 09:29:16 INFO - Starting Quartz Scheduler now
  72. 2018-07-06 09:29:16 INFO - Freed 0 triggers from 'acquired' / 'blocked' state.
  73. 2018-07-06 09:29:16 INFO - Recovering 0 jobs that were in-progress at the time of the last shut-down.
  74. 2018-07-06 09:29:16 INFO - Recovery complete.
  75. 2018-07-06 09:29:16 INFO - Removed 0 'complete' triggers.
  76. 2018-07-06 09:29:16 INFO - Removed 0 stale fired job entries.
  77. 2018-07-06 09:29:16 INFO - Scheduler schedulerFactoryBean_$_NON_CLUSTERED started.
  78. 2018-07-06 09:29:16 INFO - Starting ProtocolHandler ["http-nio-8080"]
  79. 2018-07-06 09:29:16 INFO - Using a shared selector for servlet write/read
  80. 2018-07-06 09:29:16 INFO - Tomcat started on port(s): 8080 (http) with context path ''
  81. 2018-07-06 09:29:16 INFO - Started App in 9.709 seconds (JVM running for 10.821)
  82. 2018-07-06 09:29:17 INFO - Trigger [DEFAULT.jobOneTrigger] fired job [DEFAULT.demoJobOne] scheduled at: 06-07-2018 09:29:13.524, next scheduled at: 06-07-2018 09:29:23.524
  83. 2018-07-06 09:29:17 INFO - Trigger [DEFAULT.jobTwoTrigger] fired job [DEFAULT.demoJobTwo] scheduled at: 06-07-2018 09:29:13.565, next scheduled at: 06-07-2018 09:29:33.565
  84. 2018-07-06 09:29:17 INFO - Job: [FlowJob: [name=demoJobTwo]] launched with the following parameters: [{JobID=1530849557198}]
  85. 2018-07-06 09:29:17 INFO - Executing step: [stepOne]
  86. MyTaskOne start..
  87. MyTaskOne done..
  88. 2018-07-06 09:29:17 INFO - Job: [FlowJob: [name=demoJobTwo]] completed with the following parameters: [{JobID=1530849557198}] and the following status: [COMPLETED]
  89. 2018-07-06 09:29:17 INFO - Trigger [DEFAULT.jobTwoTrigger] completed firing job [DEFAULT.demoJobTwo] with resulting trigger instruction code: DO NOTHING. Next scheduled at: 06-07-2018 09:29:33.565
  90. 2018-07-06 09:29:17 INFO - Job: [SimpleJob: [name=demoJobOne]] launched with the following parameters: [{JobID=1530849557093}]
  91. 2018-07-06 09:29:17 INFO - Executing step: [stepOne]
  92. MyTaskOne start..
  93. MyTaskOne done..
  94. 2018-07-06 09:29:17 INFO - Executing step: [stepTwo]
  95. MyTaskTwo start..
  96. MyTaskTwo done..
  97. 2018-07-06 09:29:17 INFO - Job: [SimpleJob: [name=demoJobOne]] completed with the following parameters: [{JobID=1530849557093}] and the following status: [COMPLETED]
  98. 2018-07-06 09:29:17 INFO - Trigger [DEFAULT.jobOneTrigger] completed firing job [DEFAULT.demoJobOne] with resulting trigger instruction code: DO NOTHING. Next scheduled at: 06-07-2018 09:29:23.524
  99. 2018-07-06 09:29:23 INFO - Trigger [DEFAULT.jobOneTrigger] fired job [DEFAULT.demoJobOne] scheduled at: 06-07-2018 09:29:23.524, next scheduled at: 06-07-2018 09:29:33.524
  100. 2018-07-06 09:29:23 INFO - Job: [SimpleJob: [name=demoJobOne]] launched with the following parameters: [{JobID=1530849563538}]
  101. 2018-07-06 09:29:23 INFO - Executing step: [stepOne]
  102. MyTaskOne start..
  103. MyTaskOne done..
  104. 2018-07-06 09:29:23 INFO - Executing step: [stepTwo]
  105. MyTaskTwo start..
  106. MyTaskTwo done..
  107. 2018-07-06 09:29:23 INFO - Job: [SimpleJob: [name=demoJobOne]] completed with the following parameters: [{JobID=1530849563538}] and the following status: [COMPLETED]
  108. 2018-07-06 09:29:23 INFO - Trigger [DEFAULT.jobOneTrigger] completed firing job [DEFAULT.demoJobOne] with resulting trigger instruction code: DO NOTHING. Next scheduled at: 06-07-2018 09:29:33.524

验证 H2 控制台

使用浏览器 URL 访问 H2 控制台:http://localhost:8080/console/

Spring Batch   Quartz   H2 Jdbcjobstore 示例 - 图2

H2 控制台

将我的问题放在评论部分。

学习愉快!