0

I am all new to Spring Boot and I have read som documentation about how you create a Spring boot application.

I have created an application in Spring Boot, this application should run a Spring Batch job (I am rewriting an old job in Spring Batch to a stand alone Spring Boot applikation). I have created the structure of the job with steps and so on. All the job does right now is moving files and that works. I work with embedded databases during development, h2, and Spring Boot has generated the whole database for Spring Batch. Really nice :)

So now my problem, in one of the steps I have to fetch and store data in another database. And I don't know (understand) how I should create this database and access the database in the job.

So in my application.properties

spring.h2.console.enabled=true
spring.h2.console.path=/h2

spring.datasource.url=jdbc:h2:mem:springdb
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver

persons.h2.console.enabled=true
persons.h2.console.path=/h2

persons.datasource.url=jdbc:h2:mem:persons
persons.datasource.username=sa
persons.datasource.password=
persons.datasource.driverClassName=org.h2.Driver

In test I will change database to a SQL-database on another server.

I have some entitys (example)

public class Person {

    private Long id;
    private String name;
    private String familyname;
    private Long birthDate;

    public Person () {
    }
...with getters and setters

In the configuration I have

    @Primary
    @Bean
    @ConfigurationProperties(prefix = "persons.datasource")
    public DataSource personsDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource batchDataSource() {
        return DataSourceBuilder.create().build();
    }

And the job

    @Autowired
    PersonItemWriter itemWriter;

    @Autowired
    PersonItemProcessor itemProcessor;

    @Autowired
    PersonItemReader workReader;

    @Bean(name = "personsImportJob")
    public Job personImportJob() {

        Step downloadFiles = stepBuilderFactory.get("download-files")
                .tasklet(downloadTasklet())
                .build();

        Step syncDbAndSavePersons = stepBuilderFactory.get("syncandsave-persons")
                .<File, Person>chunk(50)
                .reader(workReader)
                .processor(itemProcessor)
                .writer(itemWriter)
                .build();

        Step deleteFiles = stepBuilderFactory.get("delete-files")
                .tasklet(deleteTasklet())
                .build();

        Job job = jobBuilderFactory.get("personsimport-job")
                .incrementer(new RunIdIncrementer())
                .flow(downloadFiles)
                .next(syncDbAndSavePersons)
                .next(deleteFiles)
                .end()
                .build();

        return job;
    }

My writer

@Component
public class PersonItemWriter implements ItemWriter<Person> {

    private static final Logger LOGGER = LoggerFactory.getLogger(PersonItemWriter.class);

    @Override
    public void write(List<? extends Person> list) throws Exception {
        LOGGER.info("Write Person to db");
    }

}

Now this works, the step syncAndSavePersons does not do anything right now, but I want this step to access another database and update posts to the persons-database.

Can I do this without JPA? Because the existing job doesn't use JPA and if I have to use JPA there will be a lot of code changes and I want to avoid this. I just want to move the job with minimum of changes,

If I run my application with this the only database that is created is the database for spring batch. How can I make sure that the other database is also created? Or is that impossible when I am working with h2 embedded databases? Before I added the second database I didn't have the datasource configuration at all in my code, but the database was created anyway. I think Spring Boot just created the batch datasource. So maybe I won't need that configuration?

UPDATE: I solved it by removing the properties for the database in te properties file. The only thing I left is:

    spring:
        datasource:
            initialization-mode: never
        h2:
            console:
                enabled: true

I then created a class called EmbeddedDataSourceConfig:

@Profile("default")
@Configuration
public class EmbeddedDataSourceConfig {

    @Bean
    @Primary
    public DataSource dataSource() {

        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();

        return builder
                .setType(EmbeddedDatabaseType.H2)
                .addScript("classpath:/org/springframework/batch/core/schema-h2.sql")
                .build();
    }
   @Bean(name = "personsDataSource")
    public DataSource personsDataSource() {

        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();

        return builder
                .setName("persons")
                .setType(EmbeddedDatabaseType.H2)
                .addScript("classpath:persons_schema.sql")
                .build();
    }

    @Bean(name = "personsTransactionManager")
    public PlatformTransactionManager personsTransactionManager(
            @Qualifier("personsDataSource") DataSource dataSource) {

        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "personsJdbcTemplate")
    public JdbcTemplate personsJdbcTemplate(@Qualifier("personsDataSource") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

and in the job I changed to following

@Bean(name = "personsImportJob")
public Job personImportJob(@Qualifier("personsTransactionManager") PlatformTransactionManager personsTransactionManager) {

    Step downloadFiles = stepBuilderFactory.get("download-files")
            .tasklet(downloadTasklet())
            .build();

    Step syncDbAndSavePersons = stepBuilderFactory.get("syncandsave-persons")
            .<File, Person>chunk(50)
            .reader(workReader)
            .processor(itemProcessor)
            .writer(itemWriter)
            .transactionManager(personsTransactionManager)
            .build();
    ...
}

That's it. Now it generates two h2 in-memory databases.

user2018311
  • 105
  • 2
  • 13
  • Possible duplicate of [Spring Boot Configure and Use Two DataSources](https://stackoverflow.com/questions/30337582/spring-boot-configure-and-use-two-datasources) – Wim Deblauwe Nov 14 '19 at 10:36
  • If I add code as accepted answer suggest I get error: `Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'primaryDataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?` – user2018311 Nov 14 '19 at 12:46

0 Answers0