Spring Boot
@Scheduled
cron
database
Java

Spring Boot Getting Scheduled cron value from database

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Reading a cron expression from a database is a common requirement when operations teams need to change schedules without redeploying the application. The important constraint is that a plain @Scheduled annotation is usually fixed at startup. If you want schedule changes from the database to take effect at runtime, you need a dynamic scheduling approach built around Spring's scheduler APIs rather than a static annotation value.

Why @Scheduled Is Usually the Wrong Tool

A fixed annotation is excellent for stable schedules.

java
1import org.springframework.scheduling.annotation.Scheduled;
2import org.springframework.stereotype.Component;
3
4@Component
5public class ReportJob {
6    @Scheduled(cron = "0 0/5 * * * ?")
7    public void run() {
8        System.out.println("Running report job");
9    }
10}

The problem is that this cron value is resolved as part of application startup. If the value changes later in a database row, the existing scheduled task does not automatically reschedule itself.

For a database-driven schedule, use TaskScheduler.schedule with a Trigger. In current Spring Framework documentation, TaskScheduler schedules a Runnable against a Trigger, and the Trigger contract now centers on nextExecution(TriggerContext). Source: Task Execution and Scheduling, TaskScheduler API, Trigger API.

Build a Dynamic Scheduler

A clean pattern is to register a scheduler bean and have a trigger read the latest cron expression each time the next execution is calculated.

java
1import org.springframework.context.annotation.Bean;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.scheduling.TaskScheduler;
4import org.springframework.scheduling.annotation.EnableScheduling;
5import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
6
7@Configuration
8@EnableScheduling
9public class SchedulingConfig {
10    @Bean
11    public TaskScheduler taskScheduler() {
12        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
13        scheduler.setPoolSize(2);
14        scheduler.setThreadNamePrefix("dynamic-cron-");
15        scheduler.initialize();
16        return scheduler;
17    }
18}

Now schedule the job with a trigger that looks up the current cron string.

java
1import jakarta.annotation.PostConstruct;
2import org.springframework.scheduling.TaskScheduler;
3import org.springframework.scheduling.support.CronTrigger;
4import org.springframework.stereotype.Component;
5
6@Component
7public class DynamicJobRegistrar {
8    private final TaskScheduler scheduler;
9    private final CronConfigService cronConfigService;
10    private final ReportJob reportJob;
11
12    public DynamicJobRegistrar(
13            TaskScheduler scheduler,
14            CronConfigService cronConfigService,
15            ReportJob reportJob) {
16        this.scheduler = scheduler;
17        this.cronConfigService = cronConfigService;
18        this.reportJob = reportJob;
19    }
20
21    @PostConstruct
22    public void register() {
23        scheduler.schedule(reportJob::run, triggerContext -> {
24            String cron = cronConfigService.getCronExpression("report-job");
25            return new CronTrigger(cron).nextExecution(triggerContext);
26        });
27    }
28}

With this design, future executions reflect the database value because the trigger reads the cron expression again when calculating the next run.

The Database Service Layer

Keep the persistence concern separate from the scheduling concern. A service can fetch the current cron value and apply validation or fallback rules.

java
1import org.springframework.stereotype.Service;
2
3@Service
4public class CronConfigService {
5    public String getCronExpression(String jobName) {
6        // Replace with repository lookup.
7        return "0 */10 * * * *";
8    }
9}

In a real application, this service should validate the value before handing it to the scheduler. If the database contains an invalid expression, your trigger should fall back to a last known-good cron instead of breaking scheduling entirely.

Operational Concerns That Matter

Dynamic cron is not only a coding problem. It has runtime consequences.

First, consider caching. Reading the database on every trigger evaluation may be acceptable for a few jobs, but it is wasteful at larger scale. A short-lived cache or a config-refresh event is often a better balance.

Second, consider clustered deployments. If three instances run the same Spring Boot service, all three may execute the same scheduled job unless you add a distributed lock or central scheduler ownership.

Third, decide on time zone handling. Cron values interpreted in the wrong zone create subtle production bugs, especially when daylight saving time changes.

Common Pitfalls

The most common mistake is expecting @Scheduled to notice database changes automatically. It does not, unless you add your own rescheduling mechanism.

Another issue is allowing invalid cron strings into the database. A dynamic scheduler is only as reliable as its validation path.

Teams also forget that multiple application instances will each schedule the same job unless cluster coordination is added.

Finally, avoid mixing business logic and scheduling logic in the same class. Keep the job body separate from the schedule lookup. That makes the code easier to test and easier to change later.

Summary

  • A plain @Scheduled value is typically fixed at startup and not truly database-driven.
  • Use TaskScheduler.schedule with a Trigger for runtime cron lookup.
  • Fetch the cron expression through a service layer and validate it before use.
  • Plan for caching, time zones, and clustered deployments.
  • Keep scheduling infrastructure separate from the actual job logic.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.