6

There are a couple of examples of how we could use Spring Batch without persisting metadata to database. Here are some examples and related questions regarding the matter:

However I have a slightly different use case: I have some jobs that run every hour or so, of which I would like to persist the metadata into my database (e.g. creating reports, running some tests, both of which might be slightly heavy in processing). I have some other types of jobs that run every minute or so (e.g. unlocking user accounts which are locked due to repeated wrong entry of password, etc.) which do not involve much of processing but a simple sql query.

Here's the question in two parts:

  1. Is there a way to keep metadata of the first type of jobs (e.g. reports processing) in the database while not using database persistence at all for second type of jobs (e.g. unlocking user accounts)?

  2. Or, would even using Spring Batch for second type of jobs be overkill/not needed at all? Would a method with @Scheduled annotation be enough?

Hasan Can Saral
  • 2,950
  • 5
  • 43
  • 78

2 Answers2

1

Here's how I achieved what I'm looking for, in regards to the suggestions by Mahmoud Ben Hassine (Removed @EnableBatchProcessing, for some unrelated issue - see here):

I have two configuration classes:

@Configuration
public class SpringBatchConfiguration extends DefaultBatchConfigurer {

    @Inject public SpringBatchConfiguration(DataSource dataSource) {
        super(dataSource);
    }

    @Bean(name = "persistentJobLauncher")
    public JobLauncher jobLauncher() throws Exception {
        return super.createJobLauncher();
    }

    @Bean
    @Primary
    public StepBuilderFactory stepBuilderFactory() {
        return new StepBuilderFactory(super.getJobRepository(), super.getTransactionManager());
    }

    @Bean
    @Primary
    public JobBuilderFactory jobBuilderFactory(){
        return new JobBuilderFactory(super.getJobRepository());
    }

    @Bean
    public JobExplorer jobExplorer() {
        return super.getJobExplorer();
    }

    @Bean
    public JobRepository jobRepository() {
        return super.getJobRepository();
    }

    @Bean
    public ListableJobLocator jobLocator() {
        return new MapJobRegistry();
    }
}

and the in-memory one:

@Configuration
public class SpringInMemoryBatchConfiguration extends DefaultBatchConfigurer {

    @Inject public SpringInMemoryBatchConfiguration() {
    }

    @Bean(name = "inMemoryJobLauncher")
    public JobLauncher inMemoryJobLauncher() throws Exception {
        return super.createJobLauncher();
    }

    @Bean(name = "inMemoryStepBuilderFactory")
    public StepBuilderFactory stepBuilderFactory() {
        return new StepBuilderFactory(super.getJobRepository(), super.getTransactionManager());
    }

    @Bean(name = "inMemoryJobBuilderFactory")
    public JobBuilderFactory inMemoryJobBuilderFactory(){
        return new JobBuilderFactory(super.getJobRepository());
    }
}

and when I want to start a "persistent" job, I use @Qualifier(value = "persistentJobLauncher") JobLauncher launcher and to start an "in-memory" one: @Qualifier(value = "inMemoryJobLauncher") JobLauncher launcher.

Hasan Can Saral
  • 2,950
  • 5
  • 43
  • 78
0
  1. Is there a way to keep metadata of the first type of jobs (e.g. reports processing) in the database while not using database persistence at all for second type of jobs (e.g. unlocking user accounts)?

This is a matter of configuration. You can use Spring profiles for the datasource. The idea is to define a persistent datasource and an in-memory one. Then configure all your jobs as usual with a datasource and run them with the right profile at runtime. Here is a quick example:

import javax.sql.DataSource;

import com.zaxxer.hikari.HikariDataSource;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;

@Configuration
@EnableBatchProcessing
public class JobsConfiguration {

    @Bean
    public Job job1() {
        return null;
    }

    @Bean
    public Job job2() {
        return null;
    }

    @Bean
    @Profile("in-memory")
    public DataSource embeddedDataSource() {
        return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.HSQL)
                .addScript("/org/springframework/batch/core/schema-hsqldb.sql")
                .build();
    }

    @Bean
    @Profile("persistent")
    public DataSource persistentDataSource() {
        HikariDataSource hikariDataSource = new HikariDataSource();
        // TODO configure datasource
        return hikariDataSource;
    }
}

If you run your app with -Dspring.profiles.active=in-memory, there will be only the embedded datasource bean in your application context, which will be used by the job repository automatically configured by @EnableBatchProcessing.

  1. Or, would even using Spring Batch for second type of jobs be overkill/not needed at all? Simply a method with @Scheduled annotation would be enough?

which do not involve much of processing but a simple sql query: If it's a simple sql query and you don't need to persist meta-data, then it is probably better to use a method annotated with @Scheduled that uses a jdbcTemplate to run the query.

Mahmoud Ben Hassine
  • 28,519
  • 3
  • 32
  • 50
  • Thanks for the answer. I need an example on how to tell Spring to use different datasources for different jobs. The available configuration only allows me to use a single datasource for all jobs. I could not see a way to configure datasource per job. – Hasan Can Saral Oct 24 '19 at 09:19
  • `how to tell Spring to use different datasources for different jobs. `: The point of using profiles is to configure all jobs with a single data source (and not to have to configure each one with its own datasource) and let spring inject the correct one according to the selected profile at runtime. I added an example in the answer. – Mahmoud Ben Hassine Oct 24 '19 at 11:01
  • "...Then configure all your jobs as usual with a datasource..." that's exactly the opposite of what I'm trying to achieve. You are misinterpreting the question. I have more than one job, in the very same application, that I would like to run with different datasources. – Hasan Can Saral Oct 24 '19 at 11:06
  • No, I got your question and my example contains 2 jobs deliberately. If you use spring boot, you can use `--spring.batch.job.names=job1 --spring.profiles.active=in-memory` and your job1 will run with the in-memory datasource. If you don't use spring boot, you can still add the profile to the job beans as well to tell spring which job should use which datasource. – Mahmoud Ben Hassine Oct 24 '19 at 11:25
  • But only my `job1` will run right?, For `job2` I'll have to have another instance of the application. – Hasan Can Saral Oct 24 '19 at 11:32
  • How do you package and run your jobs? How do you schedule them? – Mahmoud Ben Hassine Oct 24 '19 at 11:38
  • There's only one boot jar, contains all of the jobs. I run them in different methods annotated with `@Scheduled`. – Hasan Can Saral Oct 24 '19 at 11:44
  • That's a different story. You will probably need to register two job repository beans (in-memory and persistent) and configure each job to use the right one. – Mahmoud Ben Hassine Oct 24 '19 at 12:56
  • I made significant process, and have updated the question. Would appreciate your help very much. Thanks. – Hasan Can Saral Jan 09 '20 at 12:22