Hello everyone,
Greetings today!
Today we will develop a Spring Batch application that saves data from a CSV file to a database using Spring Boot.
Before you start, if you want more details on Spring Batch components and architecture, read my post on Spring Batch Component Architecture
Below are some of the success criteria for the application.
Application Name :: StudentReportCardGeneration
Success Criteria
- The school authority will place one CSV file in the following format containing all student grades.
- Read a CSV file and add all student grades to the Student_Marks table.
- Calculation of student grades and pass or fail rating status for all students
- Student results and percentages must be emailed to each student.
- processMarksCSVFile [readCSVFile as a reader for incoming CSV files, StudentMarksProcessor as a processor to calculate percentages and results, and WriteStudentMarks as a writer to save the CSV file records in the database. ]
- emailResultStep will be the tasklet used to send the results to the students.
package com.student.report.config; @Configuration public class BatchConfig { @Autowired private JobBuilderFactory jobBuilderFactory; @Autowired private StepBuilderFactory stepBuilderFactory; @Bean(name = "generateReportCard") public Job generateReportCard() { return jobBuilderFactory.get("generateReportCard") .incrementer(new RunIdIncrementer()) .start(processMarksCSVFile()) .next(emailResultStep()).build(); } @Bean public Step processMarksCSVFile() { return stepBuilderFactory.get("processMarksCSVFile") .Each record in the CSV file is mapped to StudentReportCard.java.chunk(1) .reader(readCSVFile()) .processor(studentMarksProcessor()) .writer(writeStudentMarks()) .build(); } @Bean public FlatFileItemReader readCSVFile() { FlatFileItemReader csvFileReader = new FlatFileItemReader<>(); csvFileReader.setResource (new FileSystemResource ("F://CodeSpace//Students//Student_Marks.csv")); csvFileReader.setLinesToSkip(1); csvFileReader.setLineMapper(getStudentLineMapper()); return csvFileReader; } @Bean public LineMapper getStudentLineMapper() { DefaultLineMapper studentLineMapper = new DefaultLineMapper (); studentLineMapper.setLineTokenizer (new DelimitedLineTokenizer() { { setNames (new String[] { "Roll-No", "Maths-Marks", "English-Marks", "Science-Marks", "Email-Address" }); } }); studentLineMapper.setFieldSetMapper (new BeanWrapperFieldSetMapper () { { setTargetType(StudentReportCard.class); } }); return studentLineMapper; } @Bean public StudentMarksProcessor studentMarksProcessor() { return new StudentMarksProcessor(); } @Bean public StudentMarksWriter writeStudentMarks() { return new StudentMarksWriter(); } @Bean public Step emailResultStep(){ return stepBuilderFactory.get("emailResultStep") .tasklet(emailResultsTasklet()) .build(); } @Bean(name ="emailResultsTasklet" ) public EmailResultsTasklet emailResultsTasklet(){ return new EmailResultsTasklet(); } }
package com.student.report.model; import lombok.*; import javax.persistence.*; import java.math.BigDecimal; @Setter @Getter @ToString @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "STUDENT_MARKS") public class StudentReportCard { @Id @Column(name = "ROLL_NO") private long rollNo; @Column(name ="EMAIL_ADDRESS") private String emailAddress; @Column(name = "MATHS_MARKS") private BigDecimal mathsMarks; @Column(name = "SCIENCE_MARKS") private BigDecimal scienceMarks; @Column(name = "ENGLISH_MARKS") private BigDecimal englishMarks; @Column(name = "PECENTAGE") private BigDecimal percentage; @Column(name = "RESULT") private String result; }
package com.student.report.processor; import com.student.report.model.StudentReportCard; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.item.ItemProcessor; import java.math.BigDecimal; public class StudentMarksProcessor implements ItemProcessor{ private static final Logger LOGGER = LoggerFactory.getLogger(StudentMarksProcessor.class); @Override public StudentReportCard process (StudentReportCard studentReportCard) throws Exception { BigDecimal percentage =calculatePecentage(studentReportCard); studentReportCard.setPercentage(percentage); if(percentage.compareTo(new BigDecimal(35))>=0){ studentReportCard.setResult("Pass"); }else{ studentReportCard.setResult("Fail"); } return studentReportCard; } private BigDecimal calculatePecentage (StudentReportCard studentReportCard) { return ((studentReportCard.getEnglishMarks() .add(studentReportCard.getMathsMarks()) .add(studentReportCard.getScienceMarks())) .multiply(new BigDecimal(100))) .divide(new BigDecimal(300),2 ,BigDecimal.ROUND_HALF_UP); } }
package com.student.report.repository; import com.student.report.model.StudentReportCard; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface StudentReportCardRepository extends JpaRepositorySo, after calculating each student's percentage and score, we'll create a writer that will be used to save the records from the CVS file to the database.{ }
package com.student.report.writer; import com.student.report.model.StudentReportCard; import com.student.report.repository.StudentReportCardRepository; import org.springframework.batch.item.ItemWriter; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; public class StudentMarksWriter implements ItemWriter{ @Autowired private StudentReportCardRepository studentReportCardRepository; @Override public void write(List list) throws Exception { list.stream().forEach(x->{ studentReportCardRepository.save(x); }); } }
package com.student.report.tasklet; public class EmailResultsTasklet implements Tasklet { private static final Logger logger= LoggerFactory.getLogger(EmailResultsTasklet.class); @Autowired private StudentReportCardRepository studentReportCardRepository; @Autowired private EmailService emailService; @Override public RepeatStatus execute (StepContribution stepContribution, ChunkContext chunkContext) throws Exception { studentReportCardRepository.findAll() .stream() .forEach(x->{ emailService.sendSimpleMessage (x.getEmailAddress(), "Your Result","Your result "+x.getResult() +" with "+x.getPercentage() +"%"); }); stepContribution.setExitStatus(ExitStatus.COMPLETED); return RepeatStatus.FINISHED; } }
package com.student.report.service; @Component public class EmailService { private static final Logger LOGGER = LoggerFactory.getLogger(EmailService.class); @Autowired private JavaMailSender emailSender; public void sendSimpleMessage( String to, String subject, String text) { try{ SimpleMailMessage message = new SimpleMailMessage(); message.setFrom("add-email-address -from-which-you-want-to-send-mails"); message.setTo(to); message.setSubject(subject); message.setText(text); emailSender.send(message); }catch(Exception e){ LOGGER.error("Error while sending email " +e.getMessage(),e); } } }
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:orcl spring.datasource.username=username spring.datasource.password=password spring.datasource.driver-class-name= oracle.jdbc.driver.OracleDriver spring.jpa.hibernate.ddl-auto=create spring.mail.host=smtp.gmail.com spring.mail.port=587 spring.mail.username=username[email address] spring.mail.password=password spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true
package com.student.report; @SpringBootApplication @EnableBatchProcessing public class StudentReportMgtApplication { @Autowired JobLauncher jobLauncher; @Autowired Job generateReportCard; public static void main(String[] args) { SpringApplication .run(StudentReportMgtApplication.class, args); } @Scheduled(cron = "0 */1 * * * ?") public void perform() throws Exception { JobParameters params = new JobParametersBuilder() .addString("JobID", String.valueOf(System.currentTimeMillis())) .toJobParameters(); jobLauncher.run(generateReportCard, params); } }
Spring Boot JDBC Template CRUD Operation
Spring Boot Rest API - Validate File Type Using Custom Annotations
Caching In Spring Boot + Caching In Spring Rest API + Clear Cache In Spring Boot
Spring REST API Authentication Using JWT + Spring Security JWT Authentication With Spring Boot
Spring Boot Rest API - Validate File Size Using Custom Annotation
Named Query In Hibernate & JPA + @NamedNativeQuery + @NamedQuery
Calling REST API With HttpClient In Java 11
0 Comments
If you have any doubts let me know.