ayush
ayush

Reputation: 712

Problem in Building Spring Boot Batch Application: Spring Boot

I am building a Spring Boot application (version 2.7.7) to dump data from .csv to MySQL db. I am getting several exceptions on building the project.
application.properties-

spring.datasource.driver-class-name= com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/batch_db
spring.datasource.username=hbstudent
spring.datasource.password=hbstudent
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
server.port=8081

spring.batch.jdbc.initialize-schema=always 
#to initalize the tables

Batch Configuration-

@Configuration
@EnableBatchProcessing 
public class BatchConfig {
    @Autowired
    private DataSource dataSource;

    @Bean
    public FlatFileItemReader<User> reader(){
        FlatFileItemReader<User> reader=new FlatFileItemReader<>();
        reader.setResource(new ClassPathResource("records.csv"));
        reader.setLineMapper(getLineMapper()); //how to map the line which is read.
        reader.setLinesToSkip(1); // skip lines when error occurs
        return reader;
    }
    private LineMapper<User> getLineMapper() {
        DefaultLineMapper<User> lineMapper=new DefaultLineMapper<>();
        DelimitedLineTokenizer delimitedLineTokenizer=new DelimitedLineTokenizer();

        delimitedLineTokenizer.setNames(new String[]{"Roll Number","Name of Student","Personal Email ID","Email ID","MOBILE NO"});
        delimitedLineTokenizer.setIncludedFields(new int[]{1,2,3,4,5});

        BeanWrapperFieldSetMapper<User> fieldSetMapper=new BeanWrapperFieldSetMapper<>();
        fieldSetMapper.setTargetType(User.class);
        lineMapper.setLineTokenizer(delimitedLineTokenizer);
        lineMapper.setFieldSetMapper(fieldSetMapper);

        return lineMapper;
    }
    @Bean
    public UserItemProcessor processor(){
        return new UserItemProcessor();
    }
    @Bean
    public JdbcBatchItemWriter<User> writer(){
        JdbcBatchItemWriter<User> writer= new JdbcBatchItemWriter<>();
        writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<User>());
        writer.setSql("insert into user(rollNo,name,personalEmail,officialEmail,mobileNo) values (:rollNo,:name,:personalEmail,:officalEmail,:mobileNo)");
        writer.setDataSource(dataSource);
        return writer;
    }

    @Bean 
    public Job importUserJob(){

        return new JobBuilder("USER_IMPORT_JOB")
        .incrementer(new RunIdIncrementer())
        .flow(step1())
        .end()
        .build();
    }

    @Bean
    public Step step1(){
        return new StepBuilder("Step 1")
        .<User,User>chunk(5)
        .reader(reader())
        .writer(writer())
        .build();
    }
}

On building the project I am getting the following exceptions-

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.example.spring_boot_batch_demo.SpringBootBatchDemoApplication]; nested exception is java.lang.IllegalStateException: Error processing condition on org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration$DataSourceInitializerConfiguration
        at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:609) ~[spring-context-5.3.24.jar:5.3.24]
        at org.springframework.context.annotation.ConfigurationClassParser.access$800(ConfigurationClassParser.java:110) ~[spring-context-5.3.24.jar:5.3.24]
        at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.lambda$processGroupImports$1(ConfigurationClassParser.java:812) ~[spring-context-5.3.24.jar:5.3.24]
        at java.base/java.util.ArrayList.forEach(Unknown Source) ~[na:na]
        at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:809) ~[spring-context-5.3.24.jar:5.3.24]
        at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:780) ~[spring-context-5.3.24.jar:5.3.24]
        at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:192) ~[spring-context-5.3.24.jar:5.3.24]     
        at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:331) ~[spring-context-5.3.24.jar:5.3.24]
        at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247) ~[spring-context-5.3.24.jar:5.3.24]
        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311) ~[spring-context-5.3.24.jar:5.3.24]
        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112) ~[spring-context-5.3.24.jar:5.3.24]
        at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746) ~[spring-context-5.3.24.jar:5.3.24]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564) ~[spring-context-5.3.24.jar:5.3.24]  
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.7.jar:2.7.7]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.7.jar:2.7.7]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.7.jar:2.7.7]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.7.jar:2.7.7]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.7.jar:2.7.7]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.7.jar:2.7.7]
        at com.example.spring_boot_batch_demo.SpringBootBatchDemoApplication.main(SpringBootBatchDemoApplication.java:10) ~[classes/:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[na:na]
        at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-2.7.7.jar:2.7.7]
Caused by: java.lang.IllegalStateException: Error processing condition on org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration$DataSourceInitializerConfiguration
        at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:60) ~[spring-boot-autoconfigure-2.7.7.jar:2.7.7]
        at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:108) ~[spring-context-5.3.24.jar:5.3.24]
        at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:225) ~[spring-context-5.3.24.jar:5.3.24]
        at org.springframework.context.annotation.ConfigurationClassParser.processMemberClasses(ConfigurationClassParser.java:371) ~[spring-context-5.3.24.jar:5.3.24]
        at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:271) ~[spring-context-5.3.24.jar:5.3.24]
        at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:249) ~[spring-context-5.399) ~[spring-context-5.3.24.jar:5.3.24]
        ... 24 common frames omittedCaused by: java.lang.IllegalArgumentException: No enum constant org.springframework.boot.sql.init.DatabaseInitializationMode.ALWAYS
        at java.base/java.lang.Enum.valueOf(Unknown Source) ~[na:na]
        at org.springframework.boot.sql.init.DatabaseInitializationMode.valueOf(DatabaseInitializationMode.java:26) ~[spring-boot-2.7.7.jar:2.7.7]
        at org.springframework.boot.autoconfigure.sql.init.OnDatabaseInitializationCondition.getDatabaseInitializationMode(OnDatabaseInitializationCondition.java:75) ~[spring-boot-autoconfigure-2.7.7.jar:2.7.7]
        at org.springframework.boot.autoconfigure.sql.init.OnDatabaseInitializationCondition.getMatchOutcome(OnDatabaseInitializationCondition.java:60) ~[spring-boot-autoconfigure-2.7.7.jar:2.7.7]
        at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:47) ~[spring-boot-autoconfigure-2.7.7.jar:2.7.7]
        ... 30 common frames omitted

For referred several other posts which stated versioning problem, but it didn't help. I cannot find the problem with the code, can somebody help me?

Upvotes: 1

Views: 805

Answers (1)

Rohit Agarwal
Rohit Agarwal

Reputation: 988

I have analysed your code, Here are my findings.

  1. Found some issues in BatchConfig class. You are using new JobBuilder(), new StepBuilder() while creating Job and Step. The right way is to use JobBuilderFactory, StepBuilderFactory.
  2. I can see entity class (user) name is in lower, It should be in camelcase (User).

PS: I tested with PostgreSQL database using these properties but I am sure it will work with MySQL database as well

application.properties

spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/batch_db
spring.datasource.username=root
spring.datasource.password=admin
#show the queries in the console
spring.jpa.show-sql=true
#to format the queries for readability
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
server.port=8081
spring.batch.jdbc.initialize-schema=always
#spring.batch.job.enabled=false
#to initalize the tables

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.7</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ayushsingh</groupId>
    <artifactId>spring_boot_batch_demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring_boot_batch_demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
          </dependency>
      
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>com.mysql</groupId>-->
<!--            <artifactId>mysql-connector-j</artifactId>-->
<!--            <scope>runtime</scope>-->
<!--        </dependency>-->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

BatchConfig

package com.ayushsingh.spring_boot_batch_demo.config;

import com.ayushsingh.spring_boot_batch_demo.entities.User;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider;
import org.springframework.batch.item.database.JdbcBatchItemWriter;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import javax.sql.DataSource;

@Configuration //this is important- enables batch processing in the project
@EnableBatchProcessing
public class BatchConfig {
    @Autowired
    private DataSource dataSource;

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Bean
    public FlatFileItemReader<User> reader(){
        FlatFileItemReader<User> reader=new FlatFileItemReader<>();
        reader.setResource(new ClassPathResource("records.csv"));
        reader.setLineMapper(getLineMapper()); //how to map the line which is read.
        reader.setLinesToSkip(1); // skip lines when error occurs
        return reader;
    }
    private LineMapper<User> getLineMapper() {
        DefaultLineMapper<User> lineMapper=new DefaultLineMapper<>();
        DelimitedLineTokenizer delimitedLineTokenizer=new DelimitedLineTokenizer();

        delimitedLineTokenizer.setNames(new String[]{"Roll Number","Name of Student","Personal Email ID","SMVDU Email ID","MOBILE NO"});
        delimitedLineTokenizer.setIncludedFields(new int[]{1,2,3,4,5});

        BeanWrapperFieldSetMapper<User> fieldSetMapper=new BeanWrapperFieldSetMapper<>();
        fieldSetMapper.setTargetType(User.class);
        lineMapper.setLineTokenizer(delimitedLineTokenizer);
        lineMapper.setFieldSetMapper(fieldSetMapper);

        return lineMapper;
    }
    @Bean
    public UserItemProcessor processor(){
        return new UserItemProcessor();
    }
    @Bean
    public JdbcBatchItemWriter<User> writer(){
        JdbcBatchItemWriter<User> writer= new JdbcBatchItemWriter<>();
        writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<User>());
        writer.setSql("insert into user(rollNo,name,personalEmail,officialEmail,mobileNo) values (:rollNo,:name,:personalEmail,:officalEmail,:mobileNo)");
        writer.setDataSource(dataSource);
        return writer;
    }



    @Bean 
    public Job importUserJob(){

        return jobBuilderFactory.get("USER_IMPORT_JOB")
        .incrementer(new RunIdIncrementer())
        .flow(step1())
        .end()
        .build();
    }

    @Bean
    public Step step1(){
        return stepBuilderFactory.get("Step 1")
        .<User,User>chunk(5)
        .reader(reader())
        .writer(writer())
        .build();
    }
}

Upvotes: 1

Related Questions