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.