2096 字
3 分钟
9 次
- 2025-09-17
1 前言
我的博客网站需要统计浏览量,浏览量统计数据是在 Redis 中 ,并且目前采用的同步方式是定时任务定期将数据同步到 Mysql 中,所以就打算集成 Quartz 。
我查阅了资料后,我发现 Quartz 居然有持久化到数据库的功能,我就决定试试。
2 集成
2.1 配置 Quartz
先加依赖
xml<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
在 application.yml 中添加配置:
ymlspring: quartz: job-store-type: jdbc jdbc: initialize-schema: never // 手动导入,不自动创建表 scheduler-name: quartzScheduler properties: org: quartz: scheduler: instanceName: xgrQuartzScheduler instanceId: AUTO jobStore: class: org.quartz.impl.jdbcjobstore.JobStoreTX driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate dataSource: xgrDS tablePrefix: QRTZ_ isClustered: true threadPool: class: org.quartz.simpl.SimpleThreadPool threadCount: 10 threadPriority: 5 dataSource: xgrDS: driver: com.mysql.cj.jdbc.Driver URL: jdbc:mysql://xxx.xxx.xxx.xxx:3306/quartz_db?useSSL=false&serverTimezone=UTC user: root password: yourpassword maxConnections: 10
然后在官网找到 MySQL 建表脚本 github-quartz-jdbcjobstore
找到 tables_mysql.sql 或者 tables_mysql_innodb.sql
注意一下官方原话:
PLEASE consider using mysql with innodb tables to avoid locking issues就是确保你的数据表是使用 innodb 引擎创建的。
打开 Navicat 创建表 quartz_db,导入 tables_mysql.sql

启动项目。
不出意外的话,就要出意外了。 报错:
textorg.springframework.beans.factory.BeanCreationException: Error creating bean with name 'quartzScheduler' defined in class path resource [org/springframework/boot/autoconfigure/quartz/QuartzAutoConfiguration.class]: Could not initialize DataSource: xgrDS
查了下资料,原来这个 dataSource 是要自己配置一个呀。也就是 SpringBoot 中要配置多数据源,其中一个给 quartz 用。
那么下一步就先配置一下多数据源了。
2.2 SpringBoot 配置多数据源
参考文章: Spring Boot 2.7.5 HikariCP 连接池多数据源配置_springboot hikaricp 多数据源-CSDN博客
我的 SpringBoot 用的是 HikariDataSource 而不是 Druid,所以找了篇 HikariDataSource 配置多数据源的文章。
稍微改造一下 application.yml
ymlspring: datasource: primary: type: com.zaxxer.hikari.HikariDataSource url: jdbc:mysql://localhost:3306/xgrblog?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2b8 username: # 自己的用户名 password: # 自己的密码 hikari: connection-test-query: SELECT 1 connection-timeout: 60000 idle-timeout: 600000 max-lifetime: 600000 maximum-pool-size: 12 minimum-idle: 12 pool-name: XgrBlog2DB-HikariPool # 改一下连接池名字 quartz: type: com.zaxxer.hikari.HikariDataSource url: jdbc:mysql://localhost:3306/xgrblog-quartz?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2b8 username: # 自己的用户名 password: # 自己的密码 hikari: connection-test-query: SELECT 1 connection-timeout: 60000 idle-timeout: 600000 max-lifetime: 600000 maximum-pool-size: 12 minimum-idle: 12 pool-name: XgrBlog2DB-Quartz-HikariPool
然后创建一个配置类 HikariDataSourceConfig
javaimport com.zaxxer.hikari.HikariDataSource; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.autoconfigure.quartz.QuartzDataSource; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @Configuration public class HikariDataSourceConfig { @Primary @Bean("primaryDataSourceProperties") @ConfigurationProperties("spring.datasource.primary") public DataSourceProperties primaryDataSourceProperties() { return new DataSourceProperties(); } @Primary @Bean("primaryDataSource") @Qualifier(value = "primaryDataSource") @ConfigurationProperties("spring.datasource.primary.hikari") public HikariDataSource primaryDataSource() { return primaryDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build(); } @Bean("quartzDataSourceProperties") @ConfigurationProperties("spring.datasource.quartz") public DataSourceProperties quartzDataSourceProperties() { return new DataSourceProperties(); } @Bean("quartzDataSource") @QuartzDataSource @Qualifier("quartzDataSource") @ConfigurationProperties("spring.datasource.quartz.hikari") public HikariDataSource quartzDataSource() { return quartzDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build(); } }
最后修改一下 application.yml 中 quartz 的配置
ymlspring: quartz: job-store-type: jdbc jdbc: initialize-schema: never // 手动导入,不自动创建表 scheduler-name: quartzScheduler properties: org: quartz: scheduler: instanceName: xgrQuartzScheduler instanceId: AUTO jobStore: class: org.quartz.impl.jdbcjobstore.JobStoreTX driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate dataSource: quartzDataSource # 改成 Bean 名字 tablePrefix: QRTZ_ isClustered: true threadPool: class: org.quartz.simpl.SimpleThreadPool threadCount: 10 threadPriority: 5 # 删掉 dataSource 相关配置
启动 SpringBoot
不出意外的话,又要出意外了。
textCaused by: org.quartz.JobPersistenceException: Failed to obtain DB connection from data source 'quartzDataSource': java.sql.SQLException: There is no DataSource named 'quartz' Caused by: java.sql.SQLException: There is no DataSource named 'quartzDataSource'
怎么会找不到数据源呢?我翻了一下控制台:
只启动了主数据源,没有启动 Quartz 数据源
于是我找到了以下三个资料
- SpringBoot - 集成Quartz框架之独立数据源(四)_springboot 配置quartz datasource-CSDN博客
- SpringBoot之独立quartz数据源 - 半纸情书 - 博客园
- Springboot2.6整合Quartz2.3.2找不到数据源问题记录_failed to obtain db connection from data source-CSDN博客
第三个是在评论区里找到的信息。
总结出两点:
- quartz 数据源配置需要添加
@QuartzDataSource指定 application.yml中 quartz 的jobStore配置,需要将class: org.quartz.impl.jdbcjobstore.JobStoreTX改成org.springframework.scheduling.quartz.LocalDataSourceJobStore
修改以后,启动项目
不出意外的话……没出意外?!

两个数据源启动成功

定时任务执行成功

数据库中也有数据
搞定。
为了搞明白为什么将 class: org.quartz.impl.jdbcjobstore.JobStoreTX 改成 org.springframework.scheduling.quartz.LocalDataSourceJobStore 就能成功,
我问了下 ai
org.quartz.impl.jdbcjobstore.JobStoreTX 和 org.springframework.scheduling.quartz.LocalDataSourceJobStore 都是 Quartz 调度框架用于将任务(jobs)、触发器(triggers)等调度数据持久化到关系型数据库的组件,但它们在设计目标、使用场景和事务管理方面存在关键区别。
以下是两者的主要区别:
- 核心来源与依赖
-
JobStoreTX:- 这是 Quartz 框架原生提供的 JDBC JobStore 实现。
- 它不依赖于 Spring 框架,可以独立于 Spring 使用。
- 直接由 Quartz 调度器(
StdSchedulerFactory)配置和管理。
-
LocalDataSourceJobStore:- 这是 Spring 框架对 Quartz 的扩展实现,位于
spring-context-support模块中。 - 它是
JobStoreCMT的一个子类,专门为在 Spring 管理的非-JTA (non-JTA) 本地事务环境中使用而设计。 - 强依赖于 Spring 的
DataSource和事务管理机制。
- 这是 Spring 框架对 Quartz 的扩展实现,位于
- 事务管理方式 (最关键的区别)
这是两者最核心的差异。
-
JobStoreTX:- 自行管理数据库连接和事务。
- 它会从配置的
DataSource获取连接,并自己调用connection.setAutoCommit(false)、commit()和rollback()来控制事务边界。 - 适用于 非托管环境(如 standalone Java 应用、简单的 Web 应用),即没有 JTA (Java Transaction API) 事务管理器的环境。
- 事务的开始、提交和回滚完全由 Quartz 内部逻辑处理。
-
LocalDataSourceJobStore:- 不直接管理事务,而是参与 Spring 管理的事务。
- 它假设数据库操作是在 Spring 的声明式或编程式事务管理之下进行的。
- 当 Spring 开启了一个事务时,
LocalDataSourceJobStore会获取 Spring 事务同步的Connection并参与该事务。它不会自己调用commit()或rollback(),而是让 Spring 的事务管理器来最终决定提交还是回滚。 - 适用于 Spring 托管的本地事务环境,通常与
DataSourceTransactionManager一起使用。
- 数据源 (
DataSource) 的使用
-
JobStoreTX:- 可以通过 Quartz 的配置文件 (
quartz.properties) 直接配置DataSource,或者通过代码注入。 - 对
DataSource的管理相对独立于应用的其他部分。
- 可以通过 Quartz 的配置文件 (
-
LocalDataSourceJobStore:- 必须使用由 Spring 容器管理的
DataSourceBean。 - 通常通过 Spring 的配置(XML 或 Java Config)将同一个
DataSource注入给LocalDataSourceJobStore和应用中的其他 DAO/Service 层,确保它们使用相同的连接池和事务上下文。
- 必须使用由 Spring 容器管理的
- 总结
| 特性 | JobStoreTX |
LocalDataSourceJobStore |
|---|---|---|
| 来源 | Quartz 原生 | Spring 扩展 |
| 依赖 Spring | 否 | 是 |
| 事务管理 | 自行管理 (开启/提交/回滚) | 参与 Spring 管理的事务 |
| 适用环境 | Standalone, 非托管事务环境 | Spring 托管的本地事务环境 |
| DataSource | 可独立配置 | 必须使用 Spring Bean 的 DataSource |
| 典型配置 | quartz.properties |
Spring 配置 (XML/Java) |
简单来说:
- 如果你 没有使用 Spring,或者你的应用是一个简单的独立应用,你应该使用
JobStoreTX。 - 如果你 正在使用 Spring 框架,并且希望 Quartz 的数据库操作能 无缝集成到 Spring 的事务管理中(例如,一个 Service 方法同时更新业务数据和调度数据,需要保证两者在一个事务里),那么你应该使用
LocalDataSourceJobStore。
AI 可太好用了((
3. 总结
- 在 SpringBoot 3.5.3 中集成 Quartz。
- 在 SpringBoot 3.5.3 中配置多数据源。
- 在 SpringBoot 3.5.3 中给 Quartz 配置独立数据源,持久化数据。