在 Spring Boot 整合 Quartz 时,为了省去手动导入 SQL 的麻烦,很多同学喜欢开启 initialize-schema: always。然而,这个配置在生产环境是个“大坑”,因为它默认策略往往是“先删后建”,导致定时任务数据全丢。本文提供一套企业级智能检测代码,实现“表不存在则创建,存在则跳过”,完美适配 MySQL 及国产数据库。


一、 场景与痛点:为什么官方配置不够用?

1. 进退两难的配置

application.yml 中,Spring Boot 提供了 Quartz 的初始化配置:

spring:
  quartz:
    job-store-type: jdbc
    jdbc:
      # 选项:ALWAYS, EMBEDDED, NEVER
      initialize-schema: ALWAYS 
  • 选 ALWAYS:方便是方便,但官方默认脚本(如 schema-mysql.sql)第一行通常是 DROP TABLE IF EXISTS。每次服务重启,任务全没了!
  • 选 NEVER:安全是安全,但部署新环境(特别是 DevOps 自动部署或交付给客户私有化部署)时,必须让 DBA 手动导脚本,极易漏导或版本不一致。
  • 先使用 ALWAYS 启动,再修改为 NERVER ,测试可以这么搞,投产这样显得太不专业了

2. 国产数据库的挑战

在信创项目中,我们常遇到 达梦 (DM8)人大金仓 (Kingbase) 等数据库。
手动维护多套 SQL 脚本非常痛苦,我们希望程序能利用 JDBC 标准 API,智能识别当前表结构是否存在。


二、 核心原理:Hook 机制实现“幂等性”

我们要达到的效果是:
启动时先检查 QRTZ_TRIGGERS 表是否存在。

  • 👉 不存在:动态将配置改为 ALWAYS,执行建表脚本。
  • 👉 已存在:动态将配置改为 NEVER,保护现有数据。

为了实现这一点,我们需要利用 Spring 的 Bean 覆盖机制,自定义 QuartzDataSourceScriptDatabaseInitializer


三、 最佳实践代码(建议收藏)

只需添加以下两个类到你的工程中即可。

1. 配置类:拦截与决策

利用 Bean 的优先级,接管数据源初始化逻辑。

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.quartz.QuartzDataSource;
import org.springframework.boot.autoconfigure.quartz.QuartzDataSourceScriptDatabaseInitializer;
import org.springframework.boot.autoconfigure.quartz.QuartzProperties;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.boot.jdbc.init.DatabaseInitializationMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Slf4j
@Configuration
public class QuartzAutoConfig {

    @Bean
    public QuartzDataSourceScriptDatabaseInitializer quartzDataSourceScriptDatabaseInitializer(
            DataSource dataSource, 
            @QuartzDataSource ObjectProvider<DataSource> quartzDataSource,
            QuartzProperties properties) {
        
        // 1. 获取正确的数据源
        DataSource dataSourceToUse = getDataSource(dataSource, quartzDataSource);
        QuartzProperties.Jdbc jdbc = properties.getJdbc();
        
        // 2. 核心逻辑:判断是否需要初始化
        QuartzDataSourceInitializerUtils initializerUtils = 
                new QuartzDataSourceInitializerUtils(dataSourceToUse, jdbc.getInitializeSchema());
        
        if (!initializerUtils.isFeatureEnabled()) {
            log.info("[Quartz] 检测到内部表已存在,跳过初始化步骤 (Mode: NEVER)");
            // 关键点:动态修改配置为 NEVER,阻止 Spring Boot 运行脚本
            jdbc.setInitializeSchema(DatabaseInitializationMode.NEVER);
        } else {
            log.info("[Quartz] 检测到表缺失,准备执行初始化脚本 (Mode: ALWAYS)");
            jdbc.setInitializeSchema(DatabaseInitializationMode.ALWAYS);
        }
        
        // 3. 返回 Spring Boot 原生初始化器,执行上述决策结果
        return new QuartzDataSourceScriptDatabaseInitializer(dataSourceToUse, properties);
    }

    private DataSource getDataSource(DataSource dataSource, ObjectProvider<DataSource> quartzDataSource) {
        DataSource dataSourceIfAvailable = quartzDataSource.getIfAvailable();
        return (dataSourceIfAvailable != null) ? dataSourceIfAvailable : dataSource;
    }
}

2. 工具类:JDBC 元数据检测(适配国产库)

这个工具类利用 JDBC 标准接口 DatabaseMetaData,兼容性极强。

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; // 建议引入 commons-lang3
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.boot.jdbc.init.DatabaseInitializationMode;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class QuartzDataSourceInitializerUtils {

    // 只需要检测其中一张核心表即可
    private static final String CHECK_TABLE_NAME = "QRTZ_TRIGGERS";
    private static final String[] TYPE_TABLE = new String[]{"TABLE"};

    private final DatabaseInitializationMode initializeSchema;
    private final DataSource dataSource;

    public QuartzDataSourceInitializerUtils(DataSource dataSource, DatabaseInitializationMode properties) {
        this.initializeSchema = properties;
        this.dataSource = dataSource;
    }

    public boolean isFeatureEnabled() {
        // 如果配置文件显式配置为 NEVER,则尊重配置
        if (initializeSchema == DatabaseInitializationMode.NEVER) {
            return false;
        }
        
        // 默认逻辑:如果是嵌入式数据库(H2等)通常默认开启
        boolean enabled = initializeSchema != DatabaseInitializationMode.EMBEDDED
                || EmbeddedDatabaseConnection.isEmbedded(this.dataSource);

        // --- 核心改进逻辑 START ---
        // 如果原本逻辑认为不需要开启(enabled=false),但我们想强制检查一下表是否存在
        // 或者原本逻辑认为要开启(enabled=true),我们也要检查表是否已经存在了,避免重复删表
        
        // 统一策略:只要表存在,就不要开启初始化
        List<String> tables = getAllTablesByKeyword(CHECK_TABLE_NAME);
        if (!tables.isEmpty()) {
            // 表存在,强制关闭初始化
            return false; 
        } else {
            // 表不存在,尝试小写再次检查(适配 Postgres/Kingbase 等大小写敏感数据库)
            tables = getAllTablesByKeyword(CHECK_TABLE_NAME.toLowerCase());
            if (!tables.isEmpty()) {
                return false;
            }
        }
        
        // 表确实不存在,开启初始化
        return true;
        // --- 核心改进逻辑 END ---
    }

    /**
     * 基于 JDBC 元数据获取表名,完美适配国产数据库
     */
    private List<String> getAllTablesByKeyword(String tableNamePattern) {
        List<String> tables = new ArrayList<>();
        Connection connection = null;
        ResultSet resultSet = null;
        try {
            connection = dataSource.getConnection();
            DatabaseMetaData metaData = connection.getMetaData();
            
            // 获取数据库产品名称
            String dbProductName = metaData.getDatabaseProductName();
            String catalog = connection.getCatalog();
            String schema = null;

            // --- 国产化适配重点 ---
            if (StringUtils.containsIgnoreCase(dbProductName, "Oracle") || 
                StringUtils.containsIgnoreCase(dbProductName, "DM")) {
                // 达梦(DM) 和 Oracle 一样,Schema 通常是用户名(大写)
                schema = metaData.getUserName();
            } else if (StringUtils.containsIgnoreCase(dbProductName, "PostgreSQL") || 
                       StringUtils.containsIgnoreCase(dbProductName, "Kingbase")) {
                // 人大金仓(Kingbase) 和 PG 一样,Schema 默认是 public
                schema = "public"; 
                // 如果使用了 schema 隔离,这里可以使用 connection.getSchema()
                if (StringUtils.isNotBlank(connection.getSchema())) {
                    schema = connection.getSchema();
                }
            } else {
                // MySQL 等其他数据库
                schema = connection.getSchema();
            }

            // 执行查询
            resultSet = metaData.getTables(catalog, schema, tableNamePattern, TYPE_TABLE);

            while (resultSet.next()) {
                tables.add(resultSet.getString("TABLE_NAME"));
            }
        } catch (SQLException e) {
            log.error("检测 Quartz 表结构失败", e);
            // 宁可报错也不要盲目返回 false 导致误删表,根据业务需求决定是否抛出异常
        } finally {
            // 建议使用 try-with-resources 或工具类关闭
            closeQuietly(resultSet);
            closeQuietly(connection);
        }
        return tables;
    }

    private void closeQuietly(AutoCloseable closeable) {
        try {
            if (closeable != null) closeable.close();
        } catch (Exception ignored) {}
    }
}

3. 流程可视化

这套代码的运行逻辑如下,彻底规避了“手滑”导致的事故。


四、 进阶:国产数据库适配指南

随着信创国产化的推进,很多项目需要迁移到 达梦 (Dameng)人大金仓 (Kingbase)OpenGauss
Quartz 默认自带了 MySQL、Oracle、PG 的脚本,但对国产库支持稍显不足。

1. 核心问题:BLOB 与 驱动代理

Quartz 需要序列化 JobDataMap 存储到数据库的 BLOB 字段中。不同数据库对 BLOB 的处理方式不同,因此需要配置 driverDelegateClass

2. 适配方案代码

我们需要创建一个 QuartzConfig 配置类来动态指定属性,而不是死写在 yml 里。


@Configuration
@Slf4j
public class QuartzConfig implements SchedulerFactoryBeanCustomizer {

    private final DataSource dataSource;

    public QuartzConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void customize(SchedulerFactoryBean schedulerFactoryBean) {
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        // 延时启动,避免影响应用启动速度
        schedulerFactoryBean.setStartupDelay(10);

        // 动态计算 Delegate,适配国产库
        Properties props = new Properties();
        props.put("org.quartz.jobStore.driverDelegateClass", getDriverDelegateClass());
        schedulerFactoryBean.setQuartzProperties(props);
    }

    /**
     * 根据数据库类型选择适配的代理类
     * 这一步是国产化适配的灵魂!
     */
    private String getDriverDelegateClass() {
        try {
            DatabaseMetaData metaData = dataSource.getConnection().getMetaData();
            String dbProductName = metaData.getDatabaseProductName();

            // 打印一下,看看实际运行的是什么库
            System.out.println("Current Database: " + dbProductName);

            if (dbProductName.contains("Oracle") || dbProductName.contains("DM")) {
                // 达梦数据库通常兼容 Oracle 语法
                return "org.quartz.impl.jdbcjobstore.oracle.OracleDelegate";
            } else if (dbProductName.contains("PostgreSQL") || dbProductName.contains("Kingbase") || dbProductName.contains("Zenith")) {
                // 人大金仓、OpenGauss 通常兼容 PG 语法
                return "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate";
            } else if (dbProductName.contains("Microsoft SQL Server")) {
                return "org.quartz.impl.jdbcjobstore.MSSQLDelegate";
            }  else if (dbProductName.contains("MySQL") || dbProductName.contains("MariaDB")) {
                return "org.quartz.impl.jdbcjobstore.StdJDBCDelegate";
            } else {
                // 默认标准代理
                return "org.quartz.impl.jdbcjobstore.StdJDBCDelegate";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "org.quartz.impl.jdbcjobstore.StdJDBCDelegate";
        }
    }

}

3. 建表脚本哪里找?

  • 达梦 (DM):直接使用 Quartz 官方的 tables_oracle.sql,通常可以直接运行。如果报错,只需将类型修改为达梦对应类型(如 blob)。
  • 人大金仓 (Kingbase):直接使用 tables_postgres.sql。注意大小写敏感问题,建议脚本中表名统一小写。

五、 springboot 2.x/3.x/4.x适配

文中代码是以 springboot 3 的版本做的适配,QuartzDataSourceInitializerUtils源码是通用的,但是Quartz对于 springboot 2、3、4 的版本有一定的差异,完整示例可以参考

springboot 4 (4.0.0)


import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.quartz.autoconfigure.QuartzDataSourceScriptDatabaseInitializer;
import org.springframework.boot.quartz.autoconfigure.QuartzJdbcProperties;
import org.springframework.boot.quartz.autoconfigure.SchedulerFactoryBeanCustomizer;
import org.springframework.boot.sql.init.DatabaseInitializationMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.sql.DataSource;

import java.sql.DatabaseMetaData;
import java.util.Properties;

@Configuration
@Slf4j
public class QuartzConfig implements SchedulerFactoryBeanCustomizer {

    private final DataSource dataSource;

    public QuartzConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void customize(SchedulerFactoryBean schedulerFactoryBean) {
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        // 延时启动,避免影响应用启动速度
        schedulerFactoryBean.setStartupDelay(10);

        // 动态计算 Delegate,适配国产库
        Properties props = new Properties();
        props.put("org.quartz.jobStore.driverDelegateClass", getDriverDelegateClass());
        schedulerFactoryBean.setQuartzProperties(props);
    }

    /**
     * 根据数据库类型选择适配的代理类
     * 这一步是国产化适配的灵魂!
     */
    private String getDriverDelegateClass() {
        try {
            DatabaseMetaData metaData = dataSource.getConnection().getMetaData();
            String dbProductName = metaData.getDatabaseProductName();

            // 打印一下,看看实际运行的是什么库
            System.out.println("Current Database: " + dbProductName);

            if (dbProductName.contains("Oracle") || dbProductName.contains("DM")) {
                // 达梦数据库通常兼容 Oracle 语法
                return "org.quartz.impl.jdbcjobstore.oracle.OracleDelegate";
            } else if (dbProductName.contains("PostgreSQL") || dbProductName.contains("Kingbase") || dbProductName.contains("Zenith")) {
                // 人大金仓、OpenGauss 通常兼容 PG 语法
                return "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate";
            } else if (dbProductName.contains("Microsoft SQL Server")) {
                return "org.quartz.impl.jdbcjobstore.MSSQLDelegate";
            }  else if (dbProductName.contains("MySQL") || dbProductName.contains("MariaDB")) {
                return "org.quartz.impl.jdbcjobstore.StdJDBCDelegate";
            } else {
                // 默认标准代理
                return "org.quartz.impl.jdbcjobstore.StdJDBCDelegate";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "org.quartz.impl.jdbcjobstore.StdJDBCDelegate";
        }
    }

    @Bean
    public org.springframework.boot.quartz.autoconfigure.QuartzDataSourceScriptDatabaseInitializer quartzDataSourceScriptDatabaseInitializer(QuartzJdbcProperties properties) {
        QuartzDataSourceInitializerUtils
                quartzDataSourceInitializer = new QuartzDataSourceInitializerUtils(dataSource, properties.getInitializeSchema());
        if (!quartzDataSourceInitializer.isFeatureEnabled()) {
            log.info("Quartz内部表已经进行了初始化,不再进行Quartz内部表初始化");
            properties.setInitializeSchema(DatabaseInitializationMode.NEVER);
        } else {
            properties.setInitializeSchema(DatabaseInitializationMode.ALWAYS);
        }
        return new QuartzDataSourceScriptDatabaseInitializer (dataSource, properties);
    }
}


spring 3.x (3.5.7)


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.quartz.QuartzDataSource;
import org.springframework.boot.autoconfigure.quartz.QuartzDataSourceScriptDatabaseInitializer;
import org.springframework.boot.autoconfigure.quartz.QuartzProperties;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.boot.sql.init.DatabaseInitializationMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.sql.DataSource;

import java.sql.DatabaseMetaData;
import java.util.Properties;

@Configuration
@Slf4j
public class QuartzConfig implements SchedulerFactoryBeanCustomizer {

    private final DataSource dataSource;

    public QuartzConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void customize(SchedulerFactoryBean schedulerFactoryBean) {
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        // 延时启动,避免影响应用启动速度
        schedulerFactoryBean.setStartupDelay(10);

        // 动态计算 Delegate,适配国产库
        Properties props = new Properties();
        props.put("org.quartz.jobStore.driverDelegateClass", getDriverDelegateClass());
        schedulerFactoryBean.setQuartzProperties(props);
    }

    /**
     * 根据数据库类型选择适配的代理类
     * 这一步是国产化适配的灵魂!
     */
    private String getDriverDelegateClass() {
        try {
            DatabaseMetaData metaData = dataSource.getConnection().getMetaData();
            String dbProductName = metaData.getDatabaseProductName();

            // 打印一下,看看实际运行的是什么库
            System.out.println("Current Database: " + dbProductName);

            if (dbProductName.contains("Oracle") || dbProductName.contains("DM")) {
                // 达梦数据库通常兼容 Oracle 语法
                return "org.quartz.impl.jdbcjobstore.oracle.OracleDelegate";
            } else if (dbProductName.contains("PostgreSQL") || dbProductName.contains("Kingbase") || dbProductName.contains("Zenith")) {
                // 人大金仓、OpenGauss 通常兼容 PG 语法
                return "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate";
            } else if (dbProductName.contains("Microsoft SQL Server")) {
                return "org.quartz.impl.jdbcjobstore.MSSQLDelegate";
            }  else if (dbProductName.contains("MySQL") || dbProductName.contains("MariaDB")) {
                return "org.quartz.impl.jdbcjobstore.StdJDBCDelegate";
            } else {
                // 默认标准代理
                return "org.quartz.impl.jdbcjobstore.StdJDBCDelegate";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "org.quartz.impl.jdbcjobstore.StdJDBCDelegate";
        }
    }

    @Bean
    public QuartzDataSourceScriptDatabaseInitializer quartzDataSourceScriptDatabaseInitializer(
            DataSource dataSource, @QuartzDataSource ObjectProvider<DataSource> quartzDataSource,
            QuartzProperties properties) {
        DataSource dataSourceToUse = getDataSource(dataSource, quartzDataSource);
        QuartzProperties.Jdbc jdbc = properties.getJdbc();
        QuartzDataSourceInitializerUtils
                quartzDataSourceInitializer = new QuartzDataSourceInitializerUtils(dataSource,
                jdbc.getInitializeSchema());
        if (!quartzDataSourceInitializer.isFeatureEnabled()) {
            log.info("Quartz内部表已经进行了初始化,不再进行Quartz内部表初始化");
            jdbc.setInitializeSchema(DatabaseInitializationMode.NEVER);
        } else {
            jdbc.setInitializeSchema(DatabaseInitializationMode.ALWAYS);
        }
        return new QuartzDataSourceScriptDatabaseInitializer (dataSourceToUse, properties);
    }

    private DataSource getDataSource(DataSource dataSource, ObjectProvider<DataSource> quartzDataSource) {
        DataSource dataSourceIfAvailable = quartzDataSource.getIfAvailable();
        return (dataSourceIfAvailable != null) ? dataSourceIfAvailable : dataSource;
    }
}

spring 2.x (2.5.15)


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.quartz.QuartzDataSource;
import org.springframework.boot.autoconfigure.quartz.QuartzDataSourceInitializer;
import org.springframework.boot.autoconfigure.quartz.QuartzProperties;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.boot.jdbc.DataSourceInitializationMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.sql.DataSource;

import java.sql.DatabaseMetaData;
import java.util.Properties;

@Configuration
@Slf4j
public class QuartzConfig implements SchedulerFactoryBeanCustomizer {

    private final DataSource dataSource;

    public QuartzConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void customize(SchedulerFactoryBean schedulerFactoryBean) {
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        // 延时启动,避免影响应用启动速度
        schedulerFactoryBean.setStartupDelay(10);

        // 动态计算 Delegate,适配国产库
        Properties props = new Properties();
        props.put("org.quartz.jobStore.driverDelegateClass", getDriverDelegateClass());
        schedulerFactoryBean.setQuartzProperties(props);
    }

    /**
     * 根据数据库类型选择适配的代理类
     * 这一步是国产化适配的灵魂!
     */
    private String getDriverDelegateClass() {
        try {
            DatabaseMetaData metaData = dataSource.getConnection().getMetaData();
            String dbProductName = metaData.getDatabaseProductName();

            // 打印一下,看看实际运行的是什么库
            System.out.println("Current Database: " + dbProductName);

            if (dbProductName.contains("Oracle") || dbProductName.contains("DM")) {
                // 达梦数据库通常兼容 Oracle 语法
                return "org.quartz.impl.jdbcjobstore.oracle.OracleDelegate";
            } else if (dbProductName.contains("PostgreSQL") || dbProductName.contains("Kingbase") || dbProductName.contains("Zenith")) {
                // 人大金仓、OpenGauss 通常兼容 PG 语法
                return "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate";
            } else if (dbProductName.contains("Microsoft SQL Server")) {
                return "org.quartz.impl.jdbcjobstore.MSSQLDelegate";
            }  else if (dbProductName.contains("MySQL") || dbProductName.contains("MariaDB")) {
                return "org.quartz.impl.jdbcjobstore.StdJDBCDelegate";
            } else {
                // 默认标准代理
                return "org.quartz.impl.jdbcjobstore.StdJDBCDelegate";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "org.quartz.impl.jdbcjobstore.StdJDBCDelegate";
        }
    }

    @Bean
    public QuartzDataSourceInitializer quartzDataSourceInitializer(DataSource dataSource,
                                                                   @QuartzDataSource
                                                                   ObjectProvider<DataSource> quartzDataSource, ResourceLoader resourceLoader,
                                                                   QuartzProperties properties) {
        DataSource dataSourceToUse = getDataSource(dataSource, quartzDataSource);
        QuartzProperties.Jdbc jdbc = properties.getJdbc();
        DataSourceInitializationMode initializeSchema = jdbc.getInitializeSchema();
        QuartzDataSourceInitializerUtils
                quartzDataSourceInitializer = new QuartzDataSourceInitializerUtils(dataSource, initializeSchema);
        if (!quartzDataSourceInitializer.isFeatureEnabled()) {
            log.info("Quartz内部表已经进行了初始化,不再进行Quartz内部表初始化");
            jdbc.setInitializeSchema(DataSourceInitializationMode.NEVER);
        } else {
            jdbc.setInitializeSchema(DataSourceInitializationMode.ALWAYS);
        }
        return new QuartzDataSourceInitializer (dataSourceToUse, resourceLoader, properties);
    }
    private DataSource getDataSource(DataSource dataSource, ObjectProvider<DataSource> quartzDataSource) {
        DataSource dataSourceIfAvailable = quartzDataSource.getIfAvailable();
        return (dataSourceIfAvailable != null) ? dataSourceIfAvailable : dataSource;
    }
}

完整示例代码

https://github.com/zhengmingliang/ScheduleTask

结语

在云原生和信创时代,基础设施的可移植性和自适应性至关重要。通过这短短几十行代码,我们不仅解决了 Quartz 删库的风险,更让应用具备了在不同数据库环境中“即插即用”的能力。

赶紧把这套代码加入你的企业级脚手架吧!


Q.E.D.


寻门而入,破门而出