36

I'm using Spring Boot and have issues scheduling a cron task using values existing in database.

For the time being, I'm reading values from properties file like below :

@Scheduled(cron= "${time.export.cron}")
public void performJob() throws Exception {
   // do something
}

This works nicely, but instead of getting values from properties file, I want to get them from database table. Is it possible and how ?

Batiaev
  • 1,173
  • 1
  • 14
  • 30
Daniel
  • 584
  • 3
  • 8
  • 20
  • For spring it doesn't matter where the properties come from, files, database, git as long as they in the end are properties. – M. Deinum May 09 '16 at 10:25
  • 3
    I tried to implement a method that brings string value from database and try to inject this string in @Scheduled but it says : The value for annotation attribute Scheduled.cron must be a constant expression – Daniel May 09 '16 at 11:05
  • You don't need to change anything on the `@Scheduled` you only need to retrieve the properties from the database and pass them to the `context:property-placeholder` or if you use java config use an `ApplicationContextInitializer` to add a `PropertySource` to do the same. – M. Deinum May 09 '16 at 11:17

4 Answers4

51

you can add a bean to get cron value from database in the SpringBootApplication main class or in any of the configuration class. Example code is below:

@Autowired
private CronRepository cronRepo;

@Bean
public int getCronValue()
{
    return cronRepo.findOne("cron").getCronValue();
}

you should create a table and provide suitable values in the database. After that you can provide the bean inside the @Scheduled. Example code is below:

@Scheduled(cron="#{@getCronValue}")

Hope it works for your issue.

Ajinkya
  • 22,324
  • 33
  • 110
  • 161
Vignesh
  • 686
  • 6
  • 10
  • 5
    It's working fine for me but we need to add @EnableScheduling on class level to run/execute @Scheduled(cron="#{@getCronValue}") – Piyush Chaudhari Jul 06 '18 at 09:56
  • 1
    Nice!! It helped me a lot! Thanks. – Vinícius Carneiro de Brito Sep 21 '18 at 20:27
  • 1
    This is good it saved my time a lot. Thank you. I am curious about if I am updating cron time value at DB is it will pick up latest one or old cron time? – Mahender Ambala Feb 25 '19 at 13:34
  • I tried this approach but i am getting this error "Requested bean is currently in creation: Is there an unresolvable circular reference?". – user4130072 Aug 13 '20 at 18:47
  • where to mention table name, column name to get cron expression from db. it will support without application restart. can we use multiple jobs? it will work for spring mvc? please advice – deadend Aug 06 '21 at 02:37
  • @MahenderAmbala no it will not pick up. It only picks when the app starts so you have to restart your app if you want Scheduled to pick up the updated value from DB. – Kaan Ateşel Mar 08 '22 at 19:49
  • How can I pass arguments parameter to `getCronValue`? – Hungnn Feb 23 '23 at 03:33
2

Using @Bean annotated method will do the trick. But as SpringBoot only going to call this method 1 time, and return cached version after that, you will need to restart Spring to getting new value.

To get new run time from database, using SchedulingConfigurer:

@Configuration
public class SchedulerConfig implements SchedulingConfigurer {

    @Autowired
    private YourService yourService;

    @Bean
    public YourJob yourJob() {
        return new YourJob();
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(
                () -> yourJob().performJob(),
                (TriggerContext triggerContext) -> yourService.getCron()
        );
    }

}

Note: don't use @Scheduled with this way.

FatalFzrry
  • 29
  • 2
  • It seems the point of this solution is to support future changes to the cron value without restarting, so how do you do that when your cron changes in the database? – Ken Clark May 07 '20 at 13:41
  • @KenClark everytime the cron job triggered it will read from database to get the next time run. – FatalFzrry May 08 '20 at 07:45
1

To achieve your goals you must configure your scheduler at runtime. It means you need to use more low-level scheduler API. Precisely when you have already prepared connect with your database you can configure your scheduler. I think you need to get rid of using @Scheduled annotation and manully manage your scheduler.

I think these topics can help to describe what I mean:

  1. How to change Spring's @Scheduled fixedDelay at runtime

  2. Scheduling a job with Spring programmatically (with fixedRate set dynamically)

However always you can use wild approaches where you would intercept the bean creation and replace original annotation on annotation with custom metadata but in order to implement it you must know many framework details and how @Scheduled annatation processor works.

Community
  • 1
  • 1
Alex
  • 139
  • 2
  • 8
1

You need to load properties from the database table in which your value stored. and merge that db properties with application properties

    @Autowired
    private DataSource dataSource;

    @Autowired
    private DatabaseConfiguration configuration;

    @Bean(name = "propertyConfig")
    public DatabaseConfiguration getDatabaseConfiguration() {
        DatabaseConfiguration configuration = new DatabaseConfiguration(dataSource, "propertyTable", "key", "value");
        return configuration;
    }

    @Bean(name = "dbProperty")
    public Properties getDBProperties(){
        Properties properties = ConfigurationConverter.getProperties(configuration);
        return properties;
    }

For more help refer https://analyzejava.wordpress.com/2015/01/16/loading-configuration-properties-from-database-in-spring-based-application/

NIrav Modi
  • 6,038
  • 8
  • 32
  • 47