文章
努力加载图片中...
SpringBoot 3.5.3 给 Quartz 配置独立数据源
  • 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 中添加配置:

yml
spring: 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

启动项目。

不出意外的话,就要出意外了。 报错:

text
org.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

yml
spring: 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

java
import 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 的配置

yml
spring: 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

不出意外的话,又要出意外了。

text
Caused 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 数据源

于是我找到了以下三个资料

第三个是在评论区里找到的信息。

总结出两点

  1. quartz 数据源配置需要添加 @QuartzDataSource 指定
  2. 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.JobStoreTXorg.springframework.scheduling.quartz.LocalDataSourceJobStore 都是 Quartz 调度框架用于将任务(jobs)、触发器(triggers)等调度数据持久化到关系型数据库的组件,但它们在设计目标、使用场景和事务管理方面存在关键区别。

以下是两者的主要区别:

  1. 核心来源与依赖
  • JobStoreTX:

    • 这是 Quartz 框架原生提供的 JDBC JobStore 实现。
    • 它不依赖于 Spring 框架,可以独立于 Spring 使用。
    • 直接由 Quartz 调度器(StdSchedulerFactory)配置和管理。
  • LocalDataSourceJobStore:

    • 这是 Spring 框架对 Quartz 的扩展实现,位于 spring-context-support 模块中。
    • 它是 JobStoreCMT 的一个子类,专门为在 Spring 管理的非-JTA (non-JTA) 本地事务环境中使用而设计。
    • 强依赖于 Spring 的 DataSource 和事务管理机制。

  1. 事务管理方式 (最关键的区别)

这是两者最核心的差异。

  • 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 一起使用。

  1. 数据源 (DataSource) 的使用
  • JobStoreTX:

    • 可以通过 Quartz 的配置文件 (quartz.properties) 直接配置 DataSource,或者通过代码注入。
    • 对 DataSource 的管理相对独立于应用的其他部分。
  • LocalDataSourceJobStore:

    • 必须使用由 Spring 容器管理的 DataSource Bean。
    • 通常通过 Spring 的配置(XML 或 Java Config)将同一个 DataSource 注入给 LocalDataSourceJobStore 和应用中的其他 DAO/Service 层,确保它们使用相同的连接池和事务上下文。

  1. 总结
特性 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. 总结

  1. 在 SpringBoot 3.5.3 中集成 Quartz。
  2. 在 SpringBoot 3.5.3 中配置多数据源。
  3. 在 SpringBoot 3.5.3 中给 Quartz 配置独立数据源,持久化数据。
作者: Xigrut发布时间: 2025-09-17 21:35:20上次编辑时间: 2026-06-18 19:26:51 许可协议: CC BY-NC-SA 4.0
留言区