spring-projects / spring-batch-extensions

Spring Batch Extensions
242 stars 258 forks source link

Object (POJO) is getting null #49

Closed julianjupiter closed 1 year ago

julianjupiter commented 5 years ago

I'm using Spring Batch Excel Extension to read Excel (.xlx) file. I cloned the source and did mvn install and added the dependency to my Spring Boot project. I also added Apache poi-ooxml.

My Excel file has simple data:

Id  Last Name   First Name
3   Aguinaldo   Emilio
4   Aquino      Melchora
5   Dagohoy     Francisco
6   Luna        Antonio
7   Jacinto     Emilio

This is my Student class:

@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    @NotBlank(message = "{NotBlank.student.lastName}")
    private String lastName;
    @NotBlank(message = "{NotBlank.student.firstName}")
    private String firstName;
    private LocalDateTime createdAt;

    // getters, setters
}

I created utility class whose method does actual reading of Excel file:

public class ExcelUtils {
    public static <T> ItemReader<T> excelToItemReader(Path file, Class<T> clazz) throws Exception {
        PoiItemReader<T> reader = new PoiItemReader<>();
        reader.setLinesToSkip(1);
        System.out.println("File Name: " + file.toString()); // Displays: File Name: uploads/excel/<Excel file selected to import>
        Resource resource = new FileSystemResource(file);
        System.out.println("File exists? " + resource.exists()); // Displays: File exists? true
        reader.setResource(resource);
        reader.setRowMapper(excelRowMapper(clazz));
        return reader;
    }

    private static <T> RowMapper<T> excelRowMapper(Class<T> clazz) {
        BeanWrapperRowMapper<T> rowMapper = new BeanWrapperRowMapper<>();
        rowMapper.setTargetType(clazz);
        return rowMapper;
    }
}

After uploading the files, I would select a file to import its data to my database:

@PostMapping("/import")
public String importStudents(@RequestParam String fileName, RedirectAttributes redirectAttributes) throws Exception {
    ItemReader<Student> studentItemReader = ExcelUtils.excelToItemReader(storageService.load(fileName), Student.class);
    Student student = studentItemReader.read();
    if (student != null) {
        System.out.println("Student has data.");
        studentService.save(student);
    } else {
        System.out.println("Student is null");
        throw new Exception("Student is null");
    }

    redirectAttributes.addFlashAttribute("message", "You successfully imported students data from " + fileName + "!");

    return "redirect:/students";
}

I don't understand why student is getting null when there is not error being logged in console at all.

mdeinum commented 3 years ago

Your usage of this class is at first a little weird as you are using it outside the scope of Spring Batch but as a regular way to parse Excel files. You are probably better of using Apache POI directly instead of this wrapper.

That being said, due to your usage you are missing some callback methods to properly initialize the reader and prepare it to read rows.

First you need to call afterPropertiesSet to ensure proper setup of the PoiItemReader and BeanWrapperRowMapper. Next you need to call open with an empty ExecutionContext to properly open the Excel file for reading. So your ExcelUtils should look something like this.

public class ExcelUtils {
    public static <T> ItemReader<T> excelToItemReader(Path file, Class<T> clazz) throws Exception {
        PoiItemReader<T> reader = new PoiItemReader<>();
        reader.setLinesToSkip(1);
        System.out.println("File Name: " + file.toString()); // Displays: File Name: uploads/excel/<Excel file selected to import>
        Resource resource = new FileSystemResource(file);
        System.out.println("File exists? " + resource.exists()); // Displays: File exists? true
        reader.setResource(resource);
        reader.setRowMapper(excelRowMapper(clazz));
        reader.afterPropertiesSet(); // To ensure proper set
        reader.open(new ExecutionContext()); // To ensure properly opened and accessible file
        return reader;
    }

    private static <T> RowMapper<T> excelRowMapper(Class<T> clazz) {
        BeanWrapperRowMapper<T> rowMapper = new BeanWrapperRowMapper<>();
        rowMapper.setTargetType(clazz);
        rowMapper.afterPropertiesSet();
        return rowMapper;
    }
}

And when you are doing reading the file you should actually call close() on the PoiItemReader to properly close the underlying resources. If you don't call close() you have a resource leak and eventually your application will halt with an error indicating that you have too many files open.

@PostMapping("/import")
public String importStudents(@RequestParam String fileName, RedirectAttributes redirectAttributes) throws Exception {
    PoiItemReader<Student> studentItemReader = ExcelUtils.excelToItemReader(storageService.load(fileName), Student.class);
    try {
      Student student = studentItemReader.read();
    finally {
     studentItemReader.close(); // Close reader to release resources
    }
    if (student != null) {
        System.out.println("Student has data.");
        studentService.save(student);
    } else {
        System.out.println("Student is null");
        throw new Exception("Student is null");
    }

    redirectAttributes.addFlashAttribute("message", "You successfully imported students data from " + fileName + "!");

    return "redirect:/students";
}
mdeinum commented 1 year ago

Closing due to inactivity.