All writing

WRITING Spring Boot

How to parse and generate an Excel file in Spring Boot: What is Apache POI?

Read and generate Excel files in Spring Boot using Apache POI, with upload and export examples.

Improvisation No. 30 (Cannons) by Vasily Kandinsky
Vasily Kandinsky Improvisation No. 30 (Cannons), 1913Art Institute of Chicago CC0

Keeping files directly in the database would be quite difficult. Therefore, it is better to read the data from the excel file and keep it in the database and create an excel file by pulling the data from the database when needed.

Apache POI is a powerful library for handling Microsoft Office files (Excel, Word, etc.) with Java applications. In this article, I will provide a basic guide on how to read and write Excel files.

We will make an example application: First we will get an Excel file in MultipartFile format, then we will parse this file, process it and create it again as an Excel file.

Excel worksheet with name, surname, and username columns and three sample rows.

Contents of the Excel File

Adding Dependencies

First you need to add the POI library to your Maven project. Add the following dependencies to your pom.xml file:

Code
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.2</version> <!-- Check the version -->
</dependency>

Model and Repository Setup

UserEntity will be the table where we will keep the data we read from excel. FileEntity is the table where we will keep information about the file. In this way, we will also proceed in accordance with SQL Normalization.

Code
@Entity
public class UserEntity {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
 
    private Long fileId;
 
    private String name;
    private String surname;
    private String username;
 
    // getters, setters 
}
Code
@Entity
public class FileEntity {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
 
    private String title;
    private Date createdDate;
 
    // getters, setters 
}

We define the JPA repositories:

Code
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
    List<UserEntity> findByFileId(Long fileId);
}
Code
@Repository
public interface FileRepository extends JpaRepository<FileEntity, Long> {
}

We will create an layer to avoid making requests directly to the repository from the service class.

We cannot know how many rows there will be in the Excel file. Therefore, saving one by one may overload the database. With JPA’s saveAll method, we can add all records at once.

Code
public interface UserDataService{
    void saveAll(List<UserEntity> entities);
 
    List<UserEntity> findByFileId(Long fileId);
}
 
@Service
public class UserDataServiceImpl impl UserDataService{
    private final UserRepository repository;
 
    public void saveAll(List<UserEntity> entities) {
         repository.saveAll(entities)
    }
 
    public List<UserEntity> findByFileId(Long fileId) {
        repository.findByFileId(fileId);
    }
}
Code
public interface FileDataService{
    void save(FileEntity entity);
}
 
@Service
public class FileDataServiceImpl impl FileDataService{
    private final FileRepository repository;
 
    public void save(FileEntity entity) {
         repository.save(entity)
    }
}

Uploading Excel File

We will upload the Excel file uploaded by the user in a RestController class. For this we will use the @PostMapping annotation:

Code
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;
 
    @PostMapping("/upload")
    public ResponseEntity upload(MultipartFile file) {
         excelService.upload(file);
         return ResponseEntity.ok();
        
    }
}

Parsing Excel File

The service class is where we will do our actual processing. Here we will open the file we received and read the data in it line by line. Then we will save this data to a list with UserEntity. Finally, we will add this list to the database.

Code
public interface UserService{
    void upload(MultipartFile file);
}
 
@Service
public class UserServiceImpl impl UserService{
    private final UserDataService dataService;
    private final FileDataService fileDataService;
 
    public void upload(MultipartFile file) throws IOException {
        // 1. Open Excel File with InputStream
        try (InputStream in= file.getInputStream()) {
            Workbook workbook = new XSSFWorkbook(in);
            Sheet sheet = workbook.getSheetAt(0);
 
            Row header = sheet.getRow(0);
 
            if (Objects.isNull(header) || header.getPhysicalNumberOfCells != 3)
                throw new SomeException(...);
            
            if (!"name".equalsIgnoreCase(header.getCell(0).getStringCellValue()) 
                    || !"surname".equalsIgnoreCase(header.getCell(0).getStringCellValue())
                    || !"username".equalsIgnoreCase(header.getCell(0).getStringCellValue()))
                throw new SomeException(...);
 
            FileEntity file = FileEntity.builder()
                .title(file.getOriginalFileName())
                .createdDate(new Date())
            .build();
 
            fileDataService.save(file);
 
            List<UserEntity> entities = new ArrayList<>();
 
            // Read from excel file
            StreamSupport.stream(sheet.spliterator(), false)
              .skip(1)
              .forEach( row -> {
                  // Do validation for each row.
 
                  // Remember to update the data in the file table in case of errors in the file content!
 
                  UserEntity user = UserEntity.builder()
                      .fileId(file.getId())
                      .name(row.getCell(0).getStringCellValue())
                      .surname(row.getCell(1).getStringCellValue())
                      .username(row.getCell(2).getStringCellValue())
                  .build();
 
                  entities.save(user);
              }); 
 
            dataService.saveAll(entities);
        }
    }
}

That’s all we have to do about file uploading. Now let’s serve this file we uploaded by fetching it from the table with fileId and writing it to an excel file.

Export Excel File

We will retrieve the Excel file uploaded by the user in a RestController class. For this we will use the @PostMapping annotation and we need to get fileId from user:

Code
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;
    
    @PostMapping("/export")
    public ResponseEntity<byte[]> export(Long fileId) 
        byte[] output = userService.export(file);
 
        return ResponseEntity.ok()
                .header("Content-Disposition", "attachment; filename=" + file.getOriginalFileName())
                .contentType(MediaType.parseMediaType("application/vdn.openxmlformates-officecoument.spreadsheetml.sheet"))
                .body(output);
    }
}

Parsing Excel File

Previously we kept an uploaded file in two separate tables. Here we will write the records with the fileId to an excel file.

Code
public interface UserService{
    void upload(MultipartFile file);
 
    byte[] export(Long fileId);
}
 
@Service
public class UserServiceImpl impl UserService{
    private final UserDataService dataService;
    private final FileDataService fileDataService;
    public void upload(MultipartFile file) throws IOException { ... }
    
    public byte[] export(Long fileId) throws IOException { 
        if (Objects.isNull(fileId)
            // Throw exception.
 
        List<UserEntity> entities = dataService.findByFileId(fileId);
 
        if (entities.isEmpty())
            // Throw Exception
 
        // Creating new Workbook
        Workbook workbook = new XSSFWorkbook(in);
        Sheet sheet = workbook.createSheet("Users");
 
        
        Row header = sheet.createRow(0);
 
        CellStyle headerStyle = workbook.createCellStyle();
        
        // Making header text style bolder
        XSSFFont font = ((XSSFFont) workbook).createFont();
 
        font.setBold(true);
 
        Row headerRow = sheet.createRow(0);
        headerRow.createCell(0).setCellValue("NAME");
        headerRow.createCell(1).setCellValue("SURNAME");
 
        headerRow.setStyle(headerStyle);
 
        CellStyle cellStyle = workbook.createCellStyle();
 
        // Writing each entity to excel
        entities.forEach(e -> {
            Row row = sheet.createRow(sheet.getLastRowNum() + 1);
 
            row.createCell(0).setCellValue(e.getName());
            row.createCell(1).setCellValue(e.getSurname());
 
            row.setStyle(cellStyle);
        });
      
 
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        workbook.write(outputStream);
        workbook.close();
 
        return outputStream.toByteArray();
    }
}

This example shows the basic steps of parse an Excel file, save it to the database and convert it back to an Excel file. Thanks to the flexible nature of Apache POI, these operations can be performed seamlessly.

Thank you for reading my article! If you have any questions, feedback or thoughts you would like to share, I would love to hear them in the comments.

You can follow me on Medium for more information on this topic and my other posts. Don’t forget to clap my posts to help them reach more people!

Thank you! 👨‍💻🚀

To follow me on LinkedIn: https://www.linkedin.com/in/tamerardal/

Resources:

  1. Baeldung, Working with Microsoft Excel in Java
  2. Baeldung, Upload and Display Excel Files with Spring MVC
  3. Geeks for geeks, Reading and Writing Data to Excel File in Java using Apache POI

Originally published on Medium.

Read the original on Medium