<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bruno Barbosa</title>
    <description>The latest articles on DEV Community by Bruno Barbosa (@brunbs).</description>
    <link>https://dev.to/brunbs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1198311%2F87868d62-51d0-4b70-9f94-1e8a1d674bb3.jpeg</url>
      <title>DEV Community: Bruno Barbosa</title>
      <link>https://dev.to/brunbs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/brunbs"/>
    <language>en</language>
    <item>
      <title>[Java Spring Boot] Returning a CSV file from an endpoint</title>
      <dc:creator>Bruno Barbosa</dc:creator>
      <pubDate>Tue, 21 Nov 2023 14:48:46 +0000</pubDate>
      <link>https://dev.to/brunbs/java-spring-boot-retuning-a-csv-file-from-an-endpoint-1803</link>
      <guid>https://dev.to/brunbs/java-spring-boot-retuning-a-csv-file-from-an-endpoint-1803</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Imagine a situation where you have an extremely long database of information about students.&lt;br&gt;
Your client has requested that you provide an endpoint to return all registered students.&lt;br&gt;
You can combine with the front end to return this information in a paged form as presenting thousands of data directly on the screen would be terrible!&lt;/p&gt;

&lt;p&gt;But, for some reason, the client also wants to have the complete list with all the information available, but in CSV format so that they can view it in Excel.&lt;/p&gt;

&lt;p&gt;If you want to see how to return paginated information using java spring boot, you can see this other article I wrote &lt;a href="https://dev.to/brunbs/how-to-return-paginated-data-in-spring-boot-11cm"&gt;by clicking here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's focus, then, on how to develop this endpoint that will return a CSV file with the list of all students.&lt;/p&gt;

&lt;p&gt;You can view the repository of this article &lt;a href="https://github.com/brunbs/springboot-csv"&gt;by clicking here&lt;/a&gt;.&lt;br&gt;
In this repository you will find a main branch, containing a controller that returns all student information, without pagination.&lt;/p&gt;

&lt;p&gt;The branch that will have our csv return endpoint development is the feature/csv-downloader branch&lt;/p&gt;

&lt;p&gt;I'll quickly introduce the main classes of our little project.&lt;/p&gt;
&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;p&gt;Student Entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Data
@Builder
public class StudentEntity {

    private Integer registration;

    private String name;
    private Integer grade;

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As simple as possible, our goal is to build a CSV, for this we will use a simple entity class.&lt;/p&gt;

&lt;p&gt;To be able to create CSV file we will need two dependencies in our POM.xml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.poi&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;poi&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;5.2.3&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.poi&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;poi-ooxml&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;5.2.3&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's see what the service that will generate the csv looks like, I'll put it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class StudentServiceImpl.class:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's create a method called getStudentsCSV, this method will receive an object of type HttpServletResponse, which we will use for HTTP communication, being able to add header information and our CSV file to be returned to the browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Override
    public void getStudentsCSV(HttpServletResponse response) {
        List&amp;lt;StudentEntity&amp;gt; students = StudentRepository.findAll();


    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For now all it does is receive the list of students from the repository.&lt;br&gt;
Let's now create some private methods that will be responsible for creating our csv file, our spreadsheet, our lines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;createSheet method:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private XSSFSheet createSheet(XSSFWorkbook workbook) {
        XSSFSheet sheet = workbook.createSheet("Student Report");
        sheet.setColumnWidth(0, 4000);
        sheet.setColumnWidth(1, 4000);
        sheet.setColumnWidth(2, 4000);
        return sheet;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method receives a Workbook, within it we will create our spreadsheet using the createSheet method, which receives the name of the spreadsheet we are going to create (A workbook can have several spreadsheets inside).&lt;br&gt;
After creating the spreadsheet, we can customize the columns within it, in our case we will change the length of the first 3 columns to 4000.&lt;/p&gt;

&lt;p&gt;Okay, now that we have our method that creates the spreadsheet, let's create the method that creates cells in our spreadsheet, being as generic as possible:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;createCell method:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    private void createCell(XSSFRow row, int index, String value) {
        XSSFCell cell = row.createCell(index);
        cell.setCellValue(value);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method receives an object of type XSSFRow that represents a row in our spreadsheet, it also receives an index to know the position of the cell in the row, and a value that would be the content that that cell will receive.&lt;/p&gt;

&lt;p&gt;So let's create an object of type XSSFCell and use the createCell method of the XSSFRow object to create a cell, passing the index, that is, the column that the cell will be in.&lt;br&gt;
And finally, we will use the setCellValue method of our XSSFCell that we created before to place the content inside.&lt;br&gt;
Very simple.&lt;/p&gt;

&lt;p&gt;Now that we have a generic method that creates cells in a row of our spreadsheet, we can create headers and fill lines.&lt;/p&gt;

&lt;p&gt;Let's start with the method that creates the header:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;createHeader method:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private void createHeader(XSSFSheet sheet) {
        XSSFRow header = sheet.createRow(0);
        createCell(header, 0, "Registration");
        createCell(header, 1, "Name");
        createCell(header, 2, "Grade");
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method receives an object referring to the worksheet.&lt;br&gt;
First we create an XSSFRow object that will represent a row in our spreadsheet, as we are going to make a header, we are going to create the first row, so we will use the createRow method of the XSSFSheet class passing the value 0.&lt;/p&gt;

&lt;p&gt;Now let's use the createCell method that we created previously by passing: the header (as this represents the spreadsheet row, the cell column and the content).&lt;br&gt;
We are calling this method 3 times because we have 3 columns to add:&lt;br&gt;
Registration - Student registration&lt;br&gt;
Name - The student's name&lt;br&gt;
Grade - the student's grade&lt;/p&gt;

&lt;p&gt;Now that we have our first row in the spreadsheet (our header) we will populate the other rows with the return from the repository. To do this, we will create a method called createRow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;createRow method:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private void createRow(XSSFSheet sheet, List&amp;lt;StudentEntity&amp;gt; students) {
        XSSFRow row;
        int rowCounter = 1;

        for(var student : students) {
            row = sheet.createRow(rowCounter);
            createCell(row, 0, student.getRegistration().toString());
            createCell(row, 1, student.getName());
            createCell(row, 2, student.getGrade().toString());
            rowCounter++;
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method receives as a parameter our spreadsheet (XSSFSheet) and a list of students that returned from our repository.&lt;br&gt;
First we will create an object of type XSSFRow and an integer that will be our spreadsheet row counter, starting at 1. It will start at 1 because row 0 has already been created, it is our header.&lt;/p&gt;

&lt;p&gt;Now let's iterate through the list of students where, for each student record, we will create a row using the createRow method of the XSSFSheet class passing the row we are creating.&lt;br&gt;
And then let's call the createCell method, created by us. Let's pass our XSSFRow, the column we are going to fill and the content to this method. In this case, we call it 3 times because we have 3 attributes in the entity, with column 0 being the student's registration, column 1 being the name and column 2 being the grade.&lt;br&gt;
After making the assignments, we increment our line count so that, in the next iteration, the information is added in a new line.&lt;/p&gt;

&lt;p&gt;Very well, now that we have the main classes for creating our spreadsheet, let's call these methods in our service in the report generation method that will be called by the controller, looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Override
    public void getStudentsCSV(HttpServletResponse response) {
        List&amp;lt;StudentEntity&amp;gt; students = StudentRepository.findAll();

        try(XSSFWorkbook workbook = new XSSFWorkbook()){
            String headerKey = "Content-Disposition";
            String headerValue = "attachment; filename=report.csv";
            response.setHeader(headerKey, headerValue);

            XSSFSheet sheet = createSheet(workbook);
            createHeader(sheet);
            createRow(sheet, students);

            ServletOutputStream out = response.getOutputStream();
            out.flush();
            workbook.write(out);
            out.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NOTE: here we are throwing a RuntimeException because I am not concerned with exception handling here, I believe the correct option is to create a custom exception for possible errors and use a handler to handle this exception.&lt;/p&gt;

&lt;p&gt;Let's understand what's going on here:&lt;/p&gt;

&lt;p&gt;1 - We call our repository that will return all students.&lt;br&gt;
2 - We create our workbook (Workbook), which is an object of type XSSFWorkbook.&lt;br&gt;
3 - We create a header to add to our HttpServletResponse. This header is called "Content-Disposition" and has the value "attachment; filename=report.csv". It will inform the browser that we are returning an attachment and it already tells the name of the attachment that will be saved on the user's computer.&lt;br&gt;
4 - We call our method create spreadsheet&lt;br&gt;
5 - We call our method create the header&lt;br&gt;
6 - We call our method of filling the lines with the student information returned by the repository&lt;br&gt;
7 - We created a ServletOutputStream to return binary information to our client. This object receives getOutputStream() from our HttpServletResponse.&lt;br&gt;
8 - We flush() the ServletOutputStream which will force the writing of information, add it to our workbook and then close it with the out() method&lt;/p&gt;

&lt;p&gt;So, this service will return our CSV. Our complete Service was as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.csv.downloader.service.Impl;


import com.csv.downloader.domain.entity.StudentEntity;
import com.csv.downloader.domain.response.StudentResponse;
import com.csv.downloader.repository.StudentRepository;
import com.csv.downloader.service.StudentService;
import com.csv.downloader.util.StudentMapper;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Override
    public List&amp;lt;StudentResponse&amp;gt; getStudents() {
        List&amp;lt;StudentEntity&amp;gt; students = StudentRepository.findAll();
        return studentMapper.entityToResponseList(students);
    }

    @Override
    public void getStudentsCSV(HttpServletResponse response) {
        List&amp;lt;StudentEntity&amp;gt; students = StudentRepository.findAll();

        try(XSSFWorkbook workbook = new XSSFWorkbook()){
            String headerKey = "Content-Disposition";
            String headerValue = "attachment; filename=report.csv";
            response.setHeader(headerKey, headerValue);

            XSSFSheet sheet = createSheet(workbook);
            createHeader(sheet);
            createRow(sheet, students);

            ServletOutputStream out = response.getOutputStream();
            out.flush();
            workbook.write(out);
            out.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    private XSSFSheet createSheet(XSSFWorkbook workbook) {
        XSSFSheet sheet = workbook.createSheet("Student Report");
        sheet.setColumnWidth(0, 4000);
        sheet.setColumnWidth(1, 4000);
        sheet.setColumnWidth(2, 4000);
        return sheet;
    }

    private void createHeader(XSSFSheet sheet) {
        XSSFRow header = sheet.createRow(0);
        createCell(header, 0, "Registration");
        createCell(header, 1, "Name");
        createCell(header, 2, "Grade");
    }

    private void createCell(XSSFRow row, int index, String value) {
        XSSFCell cell = row.createCell(index);
        cell.setCellValue(value);
    }

    private void createRow(XSSFSheet sheet, List&amp;lt;StudentEntity&amp;gt; students) {
        XSSFRow row;
        int rowCounter = 1;

        for(var student : students) {
            row = sheet.createRow(rowCounter);
            createCell(row, 0, student.getRegistration().toString());
            createCell(row, 1, student.getName());
            createCell(row, 2, student.getGrade().toString());
            rowCounter++;
        }
    }


}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The controller that calls this service is the StudentsController:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;StudentController.class:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.csv.downloader.controller;

import com.csv.downloader.domain.response.StudentResponse;
import com.csv.downloader.service.StudentService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("students")
@Tag(name = "Student Controller", description = "Endpoint for returning csv with a list of students")
public class StudentController {

    @Autowired
    private StudentService studentService;

    @GetMapping("all")
    @Operation(summary = "list all students")
    ResponseEntity&amp;lt;List&amp;lt;StudentResponse&amp;gt;&amp;gt; getAllStudents() {
        return ResponseEntity.ok().body(studentService.getStudents());
    }

    @GetMapping("download")
    @Operation(summary = "download a csv with all students info")
    void getStudentsCSVReport(HttpServletResponse response) {
        studentService.getStudentsCSV(response);
    }

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;In the end, the CSV file looked like this:"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3FTbypJO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uunlwjktg49lveik1c00.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3FTbypJO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uunlwjktg49lveik1c00.png" alt="A CSV file running in Excel program showing the list of students with registration, name and grade" width="424" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To call the endpoint, simply run the application and do a get to:&lt;br&gt;
localhost:8080/students/download&lt;/p&gt;

&lt;p&gt;or access the application's swagger:&lt;br&gt;
&lt;a href="http://localhost:8080/swagger-ui/index.html"&gt;http://localhost:8080/swagger-ui/index.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1F3GblKx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hbo59g7ljp1u3n9wh4uq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1F3GblKx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hbo59g7ljp1u3n9wh4uq.png" alt="Swagger image with the download endpoint running" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this we finish our implementation.&lt;br&gt;
I hope this article helped you. If you have any questions or suggestions for improvements, feel free to comment and I will be happy to answer.&lt;/p&gt;

</description>
      <category>java</category>
      <category>webdev</category>
      <category>springboot</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>[Java Spring Boot] Como fazer seu endpoint retornar um arquivo CSV</title>
      <dc:creator>Bruno Barbosa</dc:creator>
      <pubDate>Tue, 21 Nov 2023 14:42:54 +0000</pubDate>
      <link>https://dev.to/brunbs/java-spring-boot-como-fazer-seu-endpoint-retornar-um-arquivo-csv-3a62</link>
      <guid>https://dev.to/brunbs/java-spring-boot-como-fazer-seu-endpoint-retornar-um-arquivo-csv-3a62</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Imagine uma situação em que você tem um banco de dados extremamente longo com informações sobre estudantes.&lt;br&gt;
Seu cliente solicitou que você disponibilize um endpoint para retornar todos os estudantes cadastrados.&lt;br&gt;
Você pode combinar com o front end para retornar essa informação de forma paginada pois apresentar milhares de dados direto na tela seria terrível!&lt;/p&gt;

&lt;p&gt;Mas, por algum motivo, o cliente também quer ter à disposição a lista completa com todas as informações, porém em formato CSV para que consiga visualizar no Excel.&lt;/p&gt;

&lt;p&gt;Caso você queira ver como retornar informações paginadas utilizando java spring boot, você pode ver este outro artigo que escrevi &lt;a href="https://dev.to/brunbs/como-retornar-dados-paginados-no-spring-boot-mhl"&gt;clicando aqui&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vamos focar, então, em como desenvolver esse endpoint que retornará um arquivo CSV com a lista de todos os estudantes.&lt;/p&gt;

&lt;p&gt;Você pode ver o repositório deste artigo &lt;a href="https://github.com/brunbs/springboot-csv" rel="noopener noreferrer"&gt;clicando aqui&lt;/a&gt;.&lt;br&gt;
Neste repositório você irá encontrar uma branch main, contendo um controller que retornar todas as informações de estudantes, sem paginação.&lt;/p&gt;

&lt;p&gt;A branch que terá nosso desenvolvimento do endpoint de retorno do csv é a branch feature/csv-downloader&lt;/p&gt;

&lt;p&gt;Vou rapidamente apresentar as principais classes do nosso pequeno projeto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Desenvolvimento
&lt;/h2&gt;

&lt;p&gt;Entidade Estudante:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Data
@Builder
public class StudentEntity {

    private Integer registration;

    private String name;
    private Integer grade;

}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;O mais simples possível, nosso objetivo é construir um CSV, pra isso vamos utilizar uma classe simples de entidade.&lt;/p&gt;

&lt;p&gt;Para poder criar arquivo CSV vamos precisar de duas dependências no nosso POM.xml:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.poi&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;poi&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;5.2.3&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.poi&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;poi-ooxml&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;5.2.3&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Agora vamos ver como é o service que irá gerar o csv, vou colocar:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Classe StudentServiceImpl.class:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vamos criar um método chamado getStudentsCSV, esse método irá receber um objeto do tipo HttpServletResponse, que vamos utilizar para comunicação HTTP, podendo adicionar informações de header e nosso arquivo CSV a ser retornado para o navegador.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Override
    public void getStudentsCSV(HttpServletResponse response) {
        List&amp;lt;StudentEntity&amp;gt; students = StudentRepository.findAll();


    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Por enquanto tudo o que ele faz é receber a lista de estudantes do repository.&lt;br&gt;
Vamos agora criar alguns métodos privados que serão responsáveis por criar nosso arquivo csv, nossa planilha, nossas linhas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Método createSheet:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

private XSSFSheet createSheet(XSSFWorkbook workbook) {
        XSSFSheet sheet = workbook.createSheet("Student Report");
        sheet.setColumnWidth(0, 4000);
        sheet.setColumnWidth(1, 4000);
        sheet.setColumnWidth(2, 4000);
        return sheet;
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Esse método recebe um Workbook (Pasta de Trabalho), dentro dele vamos criar nossa planilha utilizando o método createSheet, que recebe o nome da planilha que vamos criar (Uma pasta de trabalho pode ter várias planilhas dentro).&lt;br&gt;
após criada a planilha, podemos personalizar as colunas dentro dela, no nosso caso vamos alterar o comprimento das 3 primeiras colunas para 4000.&lt;/p&gt;

&lt;p&gt;Certo, agora que temos nosso método que cria a planilha, vamos criar o método que cria células na nossa planilha, sendo o mais genérico possível:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Método createCell:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

    private void createCell(XSSFRow row, int index, String value) {
        XSSFCell cell = row.createCell(index);
        cell.setCellValue(value);
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Esse método recebe um objeto do tipo XSSFRow que representa uma linha da nossa planilha, recebe também um índice para saber qual a posição da célula na linha, e um value que seria o conteúdo que aquela célula receberá.&lt;/p&gt;

&lt;p&gt;Então vamos criar um objeto do tipo XSSFCell e vamos utilizar o método createCell do objeto XSSFRow para criar uma célula, passando o índice, ou seja, a coluna que a célula estará.&lt;br&gt;
E, por fim, vamos utilizar o método setCellValue do nosso XSSFCell que criamos antes para colocar o conteúdo dentro.&lt;br&gt;
Bem simples.&lt;/p&gt;

&lt;p&gt;Agora que já temos um método genérico que cria células em uma linha da nossa planilha, podemos criar cabeçalhos e preencher linhas.&lt;/p&gt;

&lt;p&gt;Vamos começar com o método que cria o cabeçalho:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Método createHeader:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

private void createHeader(XSSFSheet sheet) {
        XSSFRow header = sheet.createRow(0);
        createCell(header, 0, "Registration");
        createCell(header, 1, "Name");
        createCell(header, 2, "Grade");
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Esse método recebe uma objeto referente a planilha.&lt;br&gt;
Primeiramente criamos um objeto XSSFRow que irá representar uma linha na nossa planilha, como vamos fazer um cabeçalho, vamos criar a primeira linha, por isso utilizaremos o método createRow da class XSSFSheet passando o valor 0.&lt;/p&gt;

&lt;p&gt;Agora vamos utilizar o método createCell que criamos anteriormente passando: o header (pois este representa a linha da planilha, a coluna da célula e o conteúdo).&lt;br&gt;
Estamos chamando esse método 3 vezes pois temos 3 colunas para adicionar:&lt;br&gt;
Registration - A matrícula do estudante&lt;br&gt;
Name - O nome do estudante&lt;br&gt;
Grade -  a nota do estudante&lt;/p&gt;

&lt;p&gt;Agora que temos a nossa primeira linha da planilha (nosso cabeçalho) vamos popular as outras linhas com o retorno do repository, para isso, vamos criar um método chamado createRow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Método createRow:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

private void createRow(XSSFSheet sheet, List&amp;lt;StudentEntity&amp;gt; students) {
        XSSFRow row;
        int rowCounter = 1;

        for(var student : students) {
            row = sheet.createRow(rowCounter);
            createCell(row, 0, student.getRegistration().toString());
            createCell(row, 1, student.getName());
            createCell(row, 2, student.getGrade().toString());
            rowCounter++;
        }
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Esse método recebe como parâmetro a nossa planilha (XSSFSheet) e uma lista de estudantes que retornou do nosso repository.&lt;br&gt;
Primeiro vamos criar um objeto do tipo XSSFRow e um inteiro que será nosso contador de linhas da planilha, iniciando em 1. Ele irá iniciar em 1 pois a linha 0 já foi criada, é nosso cabeçalho.&lt;/p&gt;

&lt;p&gt;Agora vamos iterar pela lista de estudantes onde, para cada registro de estudante, vamos criar uma linha utilizando o método createRow da classe XSSFSheet passando a linha que estamos criando.&lt;br&gt;
E então vamos chamar o método createCell, criado por nós. Vamos passar para esse método nosso XSSFRow, a coluna que vamos preencher e o conteúdo. No caso, chamamos 3 vezes pois temos 3 atributos na entidade, sendo que a coluna 0 é a matrícula do estudante, a coluna 1 é o nome e a coluna 2 é a nota.&lt;br&gt;
depois de fazermos as atribuições, incrementamos nosso contados de linhas para que, na próxima iteração, as informações sejam adicionadas em uma nova linha.&lt;/p&gt;

&lt;p&gt;Muito bem, agora que temos as principais classes para criação da nossa planilha, vamos chamar esses métodos no nosso service lá no método de gerar relatório que será chamado pelo controller, ficando assim:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Override
    public void getStudentsCSV(HttpServletResponse response) {
        List&amp;lt;StudentEntity&amp;gt; students = StudentRepository.findAll();

        try(XSSFWorkbook workbook = new XSSFWorkbook()){
            String headerKey = "Content-Disposition";
            String headerValue = "attachment; filename=report.csv";
            response.setHeader(headerKey, headerValue);

            XSSFSheet sheet = createSheet(workbook);
            createHeader(sheet);
            createRow(sheet, students);

            ServletOutputStream out = response.getOutputStream();
            out.flush();
            workbook.write(out);
            out.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;OBS: aqui estamos lançando uma RuntimeException pois não estou preocupado com o tratamento de exceções aqui, acredito que o correto seja criar uma exception personalizada para possíveis erros e utilizar um handler para tratar essa exception.&lt;/p&gt;

&lt;p&gt;Vamos entender o que está acontecendo aqui:&lt;/p&gt;

&lt;p&gt;1 - Chamamos nosso repository que irá retornar todos os estudantes.&lt;br&gt;
2 - Criamos nossa pasta de trabalho (Workbook), que é um objeto do tipo XSSFWorkbook.&lt;br&gt;
3 - Criamos um header para adicionar no nosso HttpServletResponse. Esse header se chama "Content-Disposition" e tem como valor "attachment; filename=report.csv". Ele irá informar para o navegador que estamos retornando um anexo e já diz o nome do anexo que será salvo no computador do usuário.&lt;br&gt;
4 - Chamamos nosso método de criar planilha&lt;br&gt;
5 - Chamamos nosso método de criar o cabeçalho&lt;br&gt;
6 - Chamamos nosso método de preencher as linhas com as informações de estudantes retornadas pelo repository&lt;br&gt;
7 - Criamos um ServletOutputStream para retornar informações binárias para o nosso client. Esse objeto recebe o getOutputStream() a partir do nosso HttpServletResponse.&lt;br&gt;
8 - Fazemos o flush() do ServletOutputStream que irá forçar a escrita das informações, adicionamos ele no nosso workbook e então encerramos com o método out()&lt;/p&gt;

&lt;p&gt;Assim, esse service irá retornar nosso CSV. Nosso Service completo ficou da seguinte forma:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package com.csv.downloader.service.Impl;


import com.csv.downloader.domain.entity.StudentEntity;
import com.csv.downloader.domain.response.StudentResponse;
import com.csv.downloader.repository.StudentRepository;
import com.csv.downloader.service.StudentService;
import com.csv.downloader.util.StudentMapper;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Override
    public List&amp;lt;StudentResponse&amp;gt; getStudents() {
        List&amp;lt;StudentEntity&amp;gt; students = StudentRepository.findAll();
        return studentMapper.entityToResponseList(students);
    }

    @Override
    public void getStudentsCSV(HttpServletResponse response) {
        List&amp;lt;StudentEntity&amp;gt; students = StudentRepository.findAll();

        try(XSSFWorkbook workbook = new XSSFWorkbook()){
            String headerKey = "Content-Disposition";
            String headerValue = "attachment; filename=report.csv";
            response.setHeader(headerKey, headerValue);

            XSSFSheet sheet = createSheet(workbook);
            createHeader(sheet);
            createRow(sheet, students);

            ServletOutputStream out = response.getOutputStream();
            out.flush();
            workbook.write(out);
            out.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    private XSSFSheet createSheet(XSSFWorkbook workbook) {
        XSSFSheet sheet = workbook.createSheet("Student Report");
        sheet.setColumnWidth(0, 4000);
        sheet.setColumnWidth(1, 4000);
        sheet.setColumnWidth(2, 4000);
        return sheet;
    }

    private void createHeader(XSSFSheet sheet) {
        XSSFRow header = sheet.createRow(0);
        createCell(header, 0, "Registration");
        createCell(header, 1, "Name");
        createCell(header, 2, "Grade");
    }

    private void createCell(XSSFRow row, int index, String value) {
        XSSFCell cell = row.createCell(index);
        cell.setCellValue(value);
    }

    private void createRow(XSSFSheet sheet, List&amp;lt;StudentEntity&amp;gt; students) {
        XSSFRow row;
        int rowCounter = 1;

        for(var student : students) {
            row = sheet.createRow(rowCounter);
            createCell(row, 0, student.getRegistration().toString());
            createCell(row, 1, student.getName());
            createCell(row, 2, student.getGrade().toString());
            rowCounter++;
        }
    }


}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;O controller que chama esse service é o StudentsController:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;StudentController.class:&lt;/strong&gt;&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;package com.csv.downloader.controller;&lt;/p&gt;

&lt;p&gt;import com.csv.downloader.domain.response.StudentResponse;&lt;br&gt;
import com.csv.downloader.service.StudentService;&lt;br&gt;
import io.swagger.v3.oas.annotations.Operation;&lt;br&gt;
import io.swagger.v3.oas.annotations.tags.Tag;&lt;br&gt;
import jakarta.servlet.http.HttpServletResponse;&lt;br&gt;
import org.springframework.beans.factory.annotation.Autowired;&lt;br&gt;
import org.springframework.http.ResponseEntity;&lt;br&gt;
import org.springframework.web.bind.annotation.*;&lt;/p&gt;

&lt;p&gt;import java.util.List;&lt;/p&gt;

&lt;p&gt;@RestController&lt;br&gt;
@RequestMapping("students")&lt;br&gt;
@Tag(name = "Student Controller", description = "Endpoint for returning csv with a list of students")&lt;br&gt;
public class StudentController {&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Autowired
private StudentService studentService;

@GetMapping("all")
@Operation(summary = "list all students")
ResponseEntity&amp;amp;lt;List&amp;amp;lt;StudentResponse&amp;amp;gt;&amp;amp;gt; getAllStudents() {
    return ResponseEntity.ok().body(studentService.getStudents());
}

@GetMapping("download")
@Operation(summary = "download a csv with all students info")
void getStudentsCSVReport(HttpServletResponse response) {
    studentService.getStudentsCSV(response);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Resultado&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;No final, o arquivo CSV ficou da seguinte forma:"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuunlwjktg49lveik1c00.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuunlwjktg49lveik1c00.png" alt="A CSV file running in Excel program showing the list of students with registration, name and grade"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para chamar o endpoint, basta rodar a aplicação e fazer um get para:&lt;br&gt;
localhost:8080/students/download&lt;/p&gt;

&lt;p&gt;ou acessar o swagger da aplicação:&lt;br&gt;
&lt;a href="http://localhost:8080/swagger-ui/index.html" rel="noopener noreferrer"&gt;http://localhost:8080/swagger-ui/index.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhbo59g7ljp1u3n9wh4uq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhbo59g7ljp1u3n9wh4uq.png" alt="Swagger image with the download endpoint running"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Com isso finalizamos nossa implementação.&lt;br&gt;
Espero que este artigo tenha ajudado vocês. Quaisquer dúvidas ou sugestões de melhorias podem comentar que ficarei feliz em responder.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to return paginated data in Spring Boot</title>
      <dc:creator>Bruno Barbosa</dc:creator>
      <pubDate>Tue, 14 Nov 2023 14:31:26 +0000</pubDate>
      <link>https://dev.to/brunbs/how-to-return-paginated-data-in-spring-boot-11cm</link>
      <guid>https://dev.to/brunbs/how-to-return-paginated-data-in-spring-boot-11cm</guid>
      <description>&lt;p&gt;At some point in your journey as a back end developer you will certainly come across a demand related to pagination.&lt;/p&gt;

&lt;p&gt;We will often have a LOT of data persisted in databases and it would be impossible to present them all to the user. When we enter a virtual store, we are not presented with all the products at once, but rather in parts, using controllable filters and being able to advance pages to see the next products.&lt;/p&gt;

&lt;p&gt;At first it may seem hopeless as you may try to go through the arduous path of counting elements, making divisions to determine the number of pages, filtering lists, etc.&lt;/p&gt;

&lt;p&gt;However, we have a very practical, easy and quick-to-implement solution for us Java Spring developers, which is to use PagingAndSortingRepository passing a Pageable to the method.&lt;/p&gt;

&lt;p&gt;These names may seem confusing at first, but don't worry, let's see what they are.&lt;/p&gt;

&lt;p&gt;Before we start, I will present the base project that we will use. As always, I will point out that here I am not concerned with details such as exception handling, etc. so as not to lose focus on what we are really developing: paginated returns.&lt;/p&gt;

&lt;p&gt;You can download the base code in the "basecode" branch of the following repository created for this tutorial: &lt;a href="https://github.com/brunbs/spring-pageable-example" rel="noopener noreferrer"&gt;CLICK HERE&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Base Project:&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let's create a small project (based on a previous project used in other posts) to persist and return student data. The data used will be very basic, being: registration number, name and last name.&lt;br&gt;
Very simple so we don't lose focus.&lt;/p&gt;

&lt;p&gt;The project was created in Java 21 with spring boot version 3.1.5&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependencies used:&lt;/strong&gt;&lt;br&gt;
Lombok - to reduce boilerplate code&lt;br&gt;
Mapstruct - facilitates object mapping&lt;br&gt;
H2 - so we don't waste time configuring databases&lt;br&gt;
JPA - since we will work with persistence&lt;br&gt;
OpenAi - we will use a swagger so that you can run and use it without needing postman or another tool&lt;/p&gt;

&lt;p&gt;Now I will present the main classes to you:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A) Student Entity - StudentEntity.class&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Entity
@Table(name = "students")
@Data
public class StudentEntity {

    @Id
    @Column(name = "registration")
    private Long registration;

    private String name;
    private String lastName;


}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;B) Request Class - StudentRequest.class&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Data
public class StudentRequest {

    private Long registration;
    private String name;
    private String lastName;

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;C) Response Class - StudentResponse.class&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Data
public class StudentResponse {

    private Long registration;
    private String name;
    private String lastName;

}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;D) Controller&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@RestController
@RequestMapping("students")
public class StudentController {

    @Autowired
    private StudentService studentService;

    @PostMapping
    ResponseEntity&amp;lt;StudentResponse&amp;gt; createStudent(@RequestBody StudentRequest studentRequest) {
        return ResponseEntity.ok().body(studentService.createStudent(studentRequest));
    }

    @GetMapping("all")
    ResponseEntity&amp;lt;List&amp;lt;StudentResponse&amp;gt;&amp;gt; getAllStudents() {
        return ResponseEntity.ok().body(studentService.getStudents());
    }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here we have two endpoints: a POST to create student, receiving an object from the request class already presented&lt;br&gt;
and a GET to list all created students, without pagination or filters&lt;/p&gt;

&lt;p&gt;We will now see our Repository class:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E) StudentRespository.class&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public interface StudentRepository extends JpaRepository&amp;lt;StudentEntity, Long&amp;gt; {

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Very basic class that extends JpaRepository to use the standard save and findAll methods. We'll talk a little more about JpaRepository soon because it's important!&lt;/p&gt;

&lt;p&gt;And finally, our Service class:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F) StudentServiceImpl.class&lt;/strong&gt; - here is an Impl because it implements an interface.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private StudentRepository studentRepository;

    @Override
    public StudentResponse createStudent(StudentRequest studentRequest) {

        StudentEntity studentToSave = studentMapper.requestToEntity(studentRequest);

        StudentEntity savedStudent = studentRepository.save(studentToSave);

        return studentMapper.entityToResponse(savedStudent);
    }

    @Override
    public List&amp;lt;StudentResponse&amp;gt; getStudents() {
        List&amp;lt;StudentEntity&amp;gt; students = studentRepository.findAll();
        return studentMapper.entityToResponseList(students);
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Only two methods, one to perform persistence and another to list the data in the database, without filters.&lt;/p&gt;

&lt;p&gt;For those who are curious about using mapstruct, here is the class &lt;strong&gt;StudentMapper.class&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface StudentMapper {

    StudentResponse requestToResponse(StudentRequest studentRequest);
    StudentEntity requestToEntity(StudentRequest request);
    StudentResponse entityToResponse(StudentEntity entity);
    List&amp;lt;StudentResponse&amp;gt; entityToResponseList(List&amp;lt;StudentEntity&amp;gt; entities);

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I love mapstruct hahaha you will almost always find it and lombok in my posts.&lt;/p&gt;

&lt;p&gt;Let's get to implementation!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Implementing Pagination&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You can find this ready-made implementation in the 'main' branch or you can go directly to the 'pageable-implement' branch to see just the implementation code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First Step: adjust repository to implement &lt;em&gt;PagingAndSortingRepository&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This interface provides pagination and ordering methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;findAll(Pageable pageable) - returning a Page&lt;/li&gt;
&lt;li&gt;findAll(Sort sort) - returning an Iterable
In our case we don't need to make any modifications because when we extend &lt;em&gt;JpaRepository&lt;/em&gt;, it already implements the &lt;em&gt;PagingAndSortingRepository&lt;/em&gt; interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that the first method used receives a &lt;strong&gt;Pageable&lt;/strong&gt;, but what is a Pageable?&lt;/p&gt;

&lt;p&gt;It is an interface with paging information, and the PageRequest class, which we will use to filter the information we want, implements this interface!&lt;br&gt;
&lt;a href="https://docs.spring.io/spring-data/data-commons/docs/current/api/org/springframework/data/domain/Pageable.html" rel="noopener noreferrer"&gt;Click here&lt;/a&gt; to see the Pageable documentation!&lt;/p&gt;

&lt;p&gt;Furthermore, it returns a &lt;em&gt;Page&lt;/em&gt;, which is an interface for pagination as it is nothing more than a list of objects with some pagination information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adapt StudentResponse to map entity to response&lt;/strong&gt;&lt;br&gt;
As our repository will now return a Page we need to prepare our response to map entity to response because we don't want to directly expose our entities, right?&lt;br&gt;
(note: I know that here my entity and response classes are the same, but normally they are not!&lt;br&gt;
Second observation: we could use mapstruct for this too! But since not everyone uses it, I'll leave this solution that works for everyone)&lt;/p&gt;

&lt;p&gt;so let's add the following method inside the StudentResponse.class class:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public static StudentResponse fromEntity(StudentEntity entity) {
        StudentResponse response = new StudentResponse();
        response.setRegistration(entity.getRegistration());
        response.setName(entity.getName());
        response.setLastName(entity.getLastName());
        return response;
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;this static method allows us to convert a StudentEntity to a StudentResponse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create the filtered search method in the Service Class - StudentServiceImpl.class&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now let's create a new method for filtered search:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Override
    public Page&amp;lt;StudentResponse&amp;gt; getFilteredStudent(Integer page, Integer size, String orderBy, String direction) {
        PageRequest pageRequest = PageRequest.of(page, size, Sort.Direction.valueOf(direction), orderBy);
        Page&amp;lt;StudentEntity&amp;gt; foundStudents = studentRepository.findAll(pageRequest);
        return foundStudents.map(StudentResponse::fromEntity);
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's analyze this method:&lt;/p&gt;

&lt;p&gt;Firstly, it will return a Page, which means it will return a list of StudentResponse with pagination information, which I will show you all this information later, when we test the application!&lt;/p&gt;

&lt;p&gt;This method receives a series of filters:&lt;br&gt;
page - page we want to receive (in pagination it starts with 0)&lt;br&gt;
size - number of elements per page&lt;br&gt;
orderBy - which field are we going to order by?&lt;br&gt;
direct - whether it is alphabetical/ascending (ASC) or descending (DESC)&lt;/p&gt;

&lt;p&gt;To perform our paged search, our repository expects to receive a Pageable, to do so, we will instantiate a new object of the PageRequest class (this class is a Pageable) and we will pass the filter information we want to it.&lt;/p&gt;

&lt;p&gt;Then we call the findAll(Pageable pageable) method of our repository passing our PageRequest (which is a Pageable) as an argument - remembering that we do not need to create this method in the repository because as it implements PagingAndSortingRepository (as it extends JpaRepository) this method is already ready for us!&lt;/p&gt;

&lt;p&gt;and now let's take the return of this findAll, which is a Page and map it to Page. To do this, we will do a .map() using that static method that we created in our response class and return this to our controller.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adding endpoint for pagination in the controller - StudentController.class&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@GetMapping
    @Operation(summary = "list students using filter with pageable")
    ResponseEntity&amp;lt;Page&amp;lt;StudentResponse&amp;gt;&amp;gt; getFilteredStudent(@RequestParam(value = "page", defaultValue = "0") Integer page,
                                             @RequestParam(value = "size", defaultValue = "3") Integer size,
                                             @RequestParam(value = "orderBy", defaultValue = "lastName") String orderBy,
                                             @RequestParam(value = "direction", defaultValue = "ASC") String direction) {
        return ResponseEntity.ok().body(studentService.getFilteredStudent(page, size, orderBy, direction));
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here we are creating a GET endpoint for the paginated search receiving the filters via queryParams in the request uri. I have set default values ​​for each of the parameters in case they are not filled in.&lt;/p&gt;

&lt;p&gt;We're ready to test! I'm going to test it with you using the swagger that was added in this little project, if you want to access it, just run the application and access:&lt;br&gt;
&lt;a href="http://localhost:8080/swagger-ui/index.html" rel="noopener noreferrer"&gt;http://localhost:8080/swagger-ui/index.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First I will add some students using the create students endpoint:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fovtkncaedlvpmllzr7bn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fovtkncaedlvpmllzr7bn.png" alt="Swagger image creating a new student"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will leave 7 students created for us to test.&lt;/p&gt;

&lt;p&gt;Now let's call the endpoint that returns all students without pagination:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwc12fezboyjmzwd37o8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwc12fezboyjmzwd37o8.png" alt="Swagger image returning all students without pagination"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The return Json was as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

[
  {
    "registration": 1,
    "name": "Bruno",
    "lastName": "Affeldt"
  },
  {
    "registration": 2,
    "name": "Cassia",
    "lastName": "Cunha"
  },
  {
    "registration": 3,
    "name": "João",
    "lastName": "Pedro"
  },
  {
    "registration": 4,
    "name": "Gregorio",
    "lastName": "Oliveira"
  },
  {
    "registration": 5,
    "name": "Edgar",
    "lastName": "Rogerio"
  },
  {
    "registration": 6,
    "name": "Raphael",
    "lastName": "Santos"
  },
  {
    "registration": 7,
    "name": "Maria",
    "lastName": "Rosa"
  }
]


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A complete list of the 7 created students.&lt;/p&gt;

&lt;p&gt;Let's now test our pagination!&lt;/p&gt;

&lt;p&gt;I will use the following parameters:&lt;br&gt;
page: 0&lt;br&gt;
size: 3&lt;br&gt;
orderBy: lastName&lt;br&gt;
direction: ASC&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq09wzrhgbfcjhw1bxz78.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq09wzrhgbfcjhw1bxz78.png" alt="Swagger image of the request to filtered students"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the return Json was as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
  "content": [
    {
      "registration": 1,
      "name": "Bruno",
      "lastName": "Affeldt"
    },
    {
      "registration": 2,
      "name": "Cassia",
      "lastName": "Cunha"
    },
    {
      "registration": 4,
      "name": "Gregorio",
      "lastName": "Oliveira"
    }
  ],
  "pageable": {
    "pageNumber": 0,
    "pageSize": 3,
    "sort": {
      "empty": false,
      "sorted": true,
      "unsorted": false
    },
    "offset": 0,
    "unpaged": false,
    "paged": true
  },
  "last": false,
  "totalPages": 3,
  "totalElements": 7,
  "size": 3,
  "number": 0,
  "sort": {
    "empty": false,
    "sorted": true,
    "unsorted": false
  },
  "numberOfElements": 3,
  "first": true,
  "empty": false
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This return Json has various pagination information that the front end can use. If he just wants the elements, just get what's in the &lt;em&gt;content&lt;/em&gt; field&lt;br&gt;
but notice that we have a lot of other information, such as, for example, if there was ordering, how it was ordered, what is the total number of pages, which page it is on, how many elements there are in total and much other information.&lt;/p&gt;

&lt;p&gt;Yes, the information is in English and you, the developer, should already be familiar with it. However, there is a way to translate terms. However, that is for another post.&lt;/p&gt;

&lt;p&gt;I hope you understood and liked this implementation. Suggestions are welcome and any questions just comment here.&lt;br&gt;
Thank you for reading.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Como retornar dados paginados no Spring Boot</title>
      <dc:creator>Bruno Barbosa</dc:creator>
      <pubDate>Tue, 14 Nov 2023 14:21:16 +0000</pubDate>
      <link>https://dev.to/brunbs/como-retornar-dados-paginados-no-spring-boot-mhl</link>
      <guid>https://dev.to/brunbs/como-retornar-dados-paginados-no-spring-boot-mhl</guid>
      <description>&lt;p&gt;Em algum momento da sua jornada de desenvolvedor back end você com certeza irá se deparar com uma demanda relacionada à paginação.&lt;/p&gt;

&lt;p&gt;Muitas vezes teremos MUITOS dados persistidos em bancos de dados e seria inviável apresentar todos para o usuário. Quando entramos em uma loja virtual, não nos é apresentado todos os produtos de uma vez só, mas sim em partes, utilizando filtros controláveis e podendo avançar páginas para vermos os próximos produtos.&lt;/p&gt;

&lt;p&gt;No início pode parecer desesperador pois você pode tentar ir pelo caminho árduo de contar elementos, fazer divisões para determinar o número de páginas, filtrar listas, etc.&lt;/p&gt;

&lt;p&gt;Porém temos uma solução muito prática, fácil e de rápida implementação para nós desenvolvedores Java Spring, que é utilizar PagingAndSortingRepository passando para o método um Pageable.&lt;/p&gt;

&lt;p&gt;Esses nomes podem parecer confusos no início, mas fique tranquilo pois vamos ver o que são.&lt;/p&gt;

&lt;p&gt;Antes de começarmos, vou apresentar o projeto base que iremos utilizar. Como sempre, vou salientar que aqui não estou preocupado com detalhes como tratamento de exceções, etc para não perdermos o foco do que realmente estamos desenvolvendo: retornos paginados.&lt;/p&gt;

&lt;p&gt;Você pode baixar o código base na branch "basecode" do seguinte repositório criado para este tutorial: &lt;a href="https://github.com/brunbs/spring-pageable-example" rel="noopener noreferrer"&gt;CLICA AQUI&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Projeto Base:&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Vamos criar um pequeno projeto (baseado em projeto anterior utilizado em outras postagens) para persistir e retornar dados de estudantes. Os dados utilizados serão bem básicos, sendo: matrícula, nome e último nome.&lt;br&gt;
Bem simples para não perdermos o foco.&lt;/p&gt;

&lt;p&gt;O projeto foi criado em Java 21 com spring boot versão 3.1.5&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependências utilizadas:&lt;/strong&gt;&lt;br&gt;
Lombok - para reduzir código boilerplate&lt;br&gt;
Mapstruct - facilita mapeamento de objetos&lt;br&gt;
H2 - para não perdermos tempo configurando bancos de dados&lt;br&gt;
JPA - já que trabalharemos com persistência&lt;br&gt;
OpenAi - vamos utilizar um swagger para que você possa rodar e utilizar sem precisar de postman ou outra ferramenta&lt;/p&gt;

&lt;p&gt;Agora vou apresentar as principais classes para vocês:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A) Entidade de Estudante - StudentEntity.class&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Entity
@Table(name = "students")
@Data
public class StudentEntity {

    @Id
    @Column(name = "registration")
    private Long registration;

    private String name;
    private String lastName;


}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;B) Classe de Request - StudentRequest.class&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Data
public class StudentRequest {

    private Long registration;
    private String name;
    private String lastName;

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;C) Classe de Response - StudentResponse.class&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Data
public class StudentResponse {

    private Long registration;
    private String name;
    private String lastName;

}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;D) Controller - por onde chamam nossos endpoints&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@RestController
@RequestMapping("students")
public class StudentController {

    @Autowired
    private StudentService studentService;

    @PostMapping
    ResponseEntity&amp;lt;StudentResponse&amp;gt; createStudent(@RequestBody StudentRequest studentRequest) {
        return ResponseEntity.ok().body(studentService.createStudent(studentRequest));
    }

    @GetMapping("all")
    ResponseEntity&amp;lt;List&amp;lt;StudentResponse&amp;gt;&amp;gt; getAllStudents() {
        return ResponseEntity.ok().body(studentService.getStudents());
    }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Aqui temos dois endpoints: um POST para criar estudante, recebendo um objeto da classe de request já apresentada&lt;br&gt;
e um GET para listar todos os estudantes criados, sem paginação ou filtros&lt;/p&gt;

&lt;p&gt;Veremos agora nossa classe de Repository:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E) StudentRespository.class&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public interface StudentRepository extends JpaRepository&amp;lt;StudentEntity, Long&amp;gt; {

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Classe bem básica que extende JpaRepository para utilizarmos os métodos save e findAll padrões. Falaremos um pouco mais sobre JpaRepository em breve pois ela é importante!&lt;/p&gt;

&lt;p&gt;E, por último, nossa classe Service:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F) StudentServiceImpl.class&lt;/strong&gt; - aqui é uma Impl pois ela implementa uma interface.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private StudentRepository studentRepository;

    @Override
    public StudentResponse createStudent(StudentRequest studentRequest) {

        StudentEntity studentToSave = studentMapper.requestToEntity(studentRequest);

        StudentEntity savedStudent = studentRepository.save(studentToSave);

        return studentMapper.entityToResponse(savedStudent);
    }

    @Override
    public List&amp;lt;StudentResponse&amp;gt; getStudents() {
        List&amp;lt;StudentEntity&amp;gt; students = studentRepository.findAll();
        return studentMapper.entityToResponseList(students);
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Apenas dois métodos, um para realizar persistência e outro para fazer a listagem dos dados no banco de dados, sem filtros.&lt;/p&gt;

&lt;p&gt;Para quem está curioso sobre a utilização do mapstruct, aqui está a classe &lt;strong&gt;StudentMapper.class&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface StudentMapper {

    StudentResponse requestToResponse(StudentRequest studentRequest);
    StudentEntity requestToEntity(StudentRequest request);
    StudentResponse entityToResponse(StudentEntity entity);
    List&amp;lt;StudentResponse&amp;gt; entityToResponseList(List&amp;lt;StudentEntity&amp;gt; entities);

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;eu amo mapstruct hahaha vocês quase sempre encontrarão ele e o lombok nas minhas postagens.&lt;/p&gt;

&lt;p&gt;Vamos à implementação!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Implementando Paginação&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;essa implementação pronta você pode encontrar na branch 'main' ou pode ir direto na branch 'pageable-implement' para ver apenas o código da implementação.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Primeiro Passo: ajustar repository para implementar &lt;em&gt;PagingAndSortingRepository&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Essa interface provê métodos de paginação e ordenação:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;findAll(Pageable pageable) - retornando um Page&lt;/li&gt;
&lt;li&gt;findAll(Sort sort) - retornando um Iterable
No nosso caso nós não precisamos fazer nenhuma modificação pois quando extendemos &lt;em&gt;JpaRepository&lt;/em&gt;, ele já implementa a interface &lt;em&gt;PagingAndSortingRepository&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perceba que o primeiro método utilizado recebe um &lt;strong&gt;Pageable&lt;/strong&gt;, mas o que é um Pageable?&lt;/p&gt;

&lt;p&gt;É uma interface com informações de paginação, e a classe PageRequest, que vamos utilizar para filtrar as informações que queremos implementa essa interface!&lt;br&gt;
&lt;a href="https://docs.spring.io/spring-data/data-commons/docs/current/api/org/springframework/data/domain/Pageable.html" rel="noopener noreferrer"&gt;Clica aqui&lt;/a&gt; para ver a documentação de Pageable!&lt;/p&gt;

&lt;p&gt;Além disso, ela retorna um &lt;em&gt;Page&lt;/em&gt;, que é uma interface para paginação pois ele nada mais é do que uma lista de objetos com algumas informações da paginação.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adaptar StudentResponse para mapear a entidade&lt;/strong&gt;&lt;br&gt;
Como nosso repository agora irá retornar um Page nós precisamos preparar nosso response para mapear entidade para response pois não queremos expor diretamente nossas entidades, não é mesmo?&lt;br&gt;
(obs: sei que aqui minha classe de entidade e de response são iguais, mas normalmente não são!&lt;br&gt;
Segunda observação: poderíamos utilizar o mapstruct para isso também! Mas como nem todos utilizam, vou deixar essa solução que funciona para todos)&lt;/p&gt;

&lt;p&gt;então vamos adicionar o seguinte método dentro da classe StudentResponse.class:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public static StudentResponse fromEntity(StudentEntity entity) {
        StudentResponse response = new StudentResponse();
        response.setRegistration(entity.getRegistration());
        response.setName(entity.getName());
        response.setLastName(entity.getLastName());
        return response;
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;esse método estático nos permite converter uma StudentEntity em um StudentResponse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Criar o método de busca filtrada na Classe de Service - StudentServiceImpl.class&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos criar um método novo para busca filtrada:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Override
    public Page&amp;lt;StudentResponse&amp;gt; getFilteredStudent(Integer page, Integer size, String orderBy, String direction) {
        PageRequest pageRequest = PageRequest.of(page, size, Sort.Direction.valueOf(direction), orderBy);
        Page&amp;lt;StudentEntity&amp;gt; foundStudents = studentRepository.findAll(pageRequest);
        return foundStudents.map(StudentResponse::fromEntity);
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Vamos analisar esse método:&lt;/p&gt;

&lt;p&gt;Primeiramente, ele vai devolver um Page isso quer dizer que ele vai retornar uma lista de StudentResponse com informações de paginação, que vou te mostrar todas essas informações depois, quando testarmos a aplicação!&lt;/p&gt;

&lt;p&gt;Esse método recebe uma série de filtros:&lt;br&gt;
page - página que queremos receber (na paginação começa com 0)&lt;br&gt;
size - número de elementos por página&lt;br&gt;
orderBy - por qual campo vamos ordenar&lt;br&gt;
direct - se é alfabético/crescente (ASC) ou decrescente (DESC)&lt;/p&gt;

&lt;p&gt;Para realizarmos nossa busca paginada, nosso repository espera receber um Pageable, para tal, vamos instanciar um novo objeto da classe PageRequest (essa classe é um Pageable) e vamos passar para ela as informações de filtro que queremos.&lt;/p&gt;

&lt;p&gt;Em seguida chamamos o método findAll(Pageable pageable) do nosso repository passando como argumento nosso PageRequest (que é um Pageable) - lembrando que não precisamos criar esse método no repository pois como ele implementa PagingAndSortingRepository (pois extends JpaRepository) esse método já está pronto pra nós!&lt;/p&gt;

&lt;p&gt;e agora vamos pegar o retorno desse findAll, que é um Page e mapear para Page. Para isso vamos fazer um .map() utilizando aquele método estático que criamos na nossa classe de response e retornar isso para nosso controller.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adicionando endpoint para paginação no controller - StudentController.class&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@GetMapping
    @Operation(summary = "list students using filter with pageable")
    ResponseEntity&amp;lt;Page&amp;lt;StudentResponse&amp;gt;&amp;gt; getFilteredStudent(@RequestParam(value = "page", defaultValue = "0") Integer page,
                                             @RequestParam(value = "size", defaultValue = "3") Integer size,
                                             @RequestParam(value = "orderBy", defaultValue = "lastName") String orderBy,
                                             @RequestParam(value = "direction", defaultValue = "ASC") String direction) {
        return ResponseEntity.ok().body(studentService.getFilteredStudent(page, size, orderBy, direction));
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Aqui estamos criando um endpoint GET para a busca paginada recebendo os filtros por queryParams na uri da requisição. Cada um dos parâmetros coloquei valores padrão para caso não venham preenchidos.&lt;/p&gt;

&lt;p&gt;Estamos prontos para testar! Vou testar com vocês utilizando o swagger que foi adicionado nesse projetinho, caso vocês queiram acessar, basta rodar a aplicação e acessar:&lt;br&gt;
&lt;a href="http://localhost:8080/swagger-ui/index.html" rel="noopener noreferrer"&gt;http://localhost:8080/swagger-ui/index.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Primeiramente vou adicionar alguns estudantes utilizando o endpoint de criar estudantes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fovtkncaedlvpmllzr7bn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fovtkncaedlvpmllzr7bn.png" alt="Swagger image creating a new student"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vou deixar criados 7 estudantes para testarmos.&lt;/p&gt;

&lt;p&gt;Agora vamos chamar o endpoint que retorna todos os estudantes sem paginação:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwc12fezboyjmzwd37o8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwc12fezboyjmzwd37o8.png" alt="Swagger image returning all student without pagination"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O Json de retorno foi o seguinte:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

[
  {
    "registration": 1,
    "name": "Bruno",
    "lastName": "Affeldt"
  },
  {
    "registration": 2,
    "name": "Cassia",
    "lastName": "Cunha"
  },
  {
    "registration": 3,
    "name": "João",
    "lastName": "Pedro"
  },
  {
    "registration": 4,
    "name": "Gregorio",
    "lastName": "Oliveira"
  },
  {
    "registration": 5,
    "name": "Edgar",
    "lastName": "Rogerio"
  },
  {
    "registration": 6,
    "name": "Raphael",
    "lastName": "Santos"
  },
  {
    "registration": 7,
    "name": "Maria",
    "lastName": "Rosa"
  }
]


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Uma lista completa dos 7 estudantes criados.&lt;/p&gt;

&lt;p&gt;Vamos agora testar nossa paginação!&lt;/p&gt;

&lt;p&gt;Vou utilizar os seguintes parâmetros:&lt;br&gt;
page: 0&lt;br&gt;
size: 3&lt;br&gt;
orderBy: lastName&lt;br&gt;
direction: ASC&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq09wzrhgbfcjhw1bxz78.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq09wzrhgbfcjhw1bxz78.png" alt="Swagger image of the request to filtered students"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;E o Json de retorno foi o seguinte:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
  "content": [
    {
      "registration": 1,
      "name": "Bruno",
      "lastName": "Affeldt"
    },
    {
      "registration": 2,
      "name": "Cassia",
      "lastName": "Cunha"
    },
    {
      "registration": 4,
      "name": "Gregorio",
      "lastName": "Oliveira"
    }
  ],
  "pageable": {
    "pageNumber": 0,
    "pageSize": 3,
    "sort": {
      "empty": false,
      "sorted": true,
      "unsorted": false
    },
    "offset": 0,
    "unpaged": false,
    "paged": true
  },
  "last": false,
  "totalPages": 3,
  "totalElements": 7,
  "size": 3,
  "number": 0,
  "sort": {
    "empty": false,
    "sorted": true,
    "unsorted": false
  },
  "numberOfElements": 3,
  "first": true,
  "empty": false
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Esse Json de retorno possui várias informações da paginação que o front end pode utilizar. Caso ele queira apenas os elementos, basta ele pegar o que tem no campo &lt;em&gt;content&lt;/em&gt;&lt;br&gt;
mas perceba que temos várias outras informações, como, por exemplo, se houve ordenação, como foi ordenado, qual o total de páginas, em qual página está, quantos elementos tem no total e muitas outras informações.&lt;/p&gt;

&lt;p&gt;Sim, as informações estão em inglês e você desenvolvedor já deveria estar familiarizado com isso. Porém, tem como traduzir termos. Porém, isso fica para outra postagem.&lt;/p&gt;

&lt;p&gt;Espero que você tenha entendido e gostado dessa implementação. Sugestões são bem vindas e quaisquer dúvidas é só comentar aqui.&lt;br&gt;
Obrigado por ter lido.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>[Java Spring Boot] How to implement a Custom Serializer for your Responses or Json</title>
      <dc:creator>Bruno Barbosa</dc:creator>
      <pubDate>Tue, 07 Nov 2023 13:23:18 +0000</pubDate>
      <link>https://dev.to/brunbs/java-spring-boot-how-to-implement-a-custom-serializer-for-your-responses-or-json-2dhi</link>
      <guid>https://dev.to/brunbs/java-spring-boot-how-to-implement-a-custom-serializer-for-your-responses-or-json-2dhi</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/brunbs/java-spring-boot-como-criar-serializador-personalizado-para-seus-responses-ou-json-de-saida-51p0"&gt;Clique aqui para versão em português&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article complements the previous article, which talks about creating a customizable deserializer for requests, and you can read it &lt;a href="https://dev.to/brunbs/java-springboot-how-to-implement-a-custom-deserializer-for-your-requests-2bo1"&gt;by clicking here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While in the previous article we talked about &lt;strong&gt;deserialization&lt;/strong&gt; (json --&amp;gt; java object), in this one we will cover &lt;strong&gt;serialization&lt;/strong&gt; (java object --&amp;gt; json).&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem description:
&lt;/h2&gt;

&lt;p&gt;Imagine that you are implementing a system that uses an event-based architecture with messaging, for example, Kafka.&lt;br&gt;
So you could have something like this:&lt;/p&gt;

&lt;p&gt;a) A producer, responsible for creating a json (message) that is sent to a messaging topic.&lt;br&gt;
b) A consumer, who will be "listening" to a specific messaging topic, taking messages from the queue and passing them on to the next API, which we will call integrator.&lt;br&gt;
c) Integrating API, responsible for applying business rules to persist the database.&lt;/p&gt;

&lt;p&gt;Okay, with this architecture defined and implemented, let's imagine the following scenario (exactly the same as the one defined in the last article):&lt;br&gt;
You need to modify the system so that all String information is persisted in the database in upper case.&lt;/p&gt;

&lt;p&gt;Could you simply use the string class's toUpperCase() method on all fields before persisting? Of course, yes you could. But imagine that you have many fields.&lt;/p&gt;

&lt;p&gt;I went through this experience and we had &lt;strong&gt;more than 200 fields&lt;/strong&gt; to make this change, leading to a waste of time and excessively large lines of code (in addition to being very annoying, let's face it).&lt;/p&gt;

&lt;p&gt;In the last post I showed how we could make the request that, in this case, would arrive at the integrator API, have string fields deserialized in a customized way, passing through toUpperCase at the application input. Here I'm going to show a &lt;strong&gt;different way&lt;/strong&gt;: let's take our producer application and make it serialize the information using a custom serializer to make the toUpperCase for us. Thus, the message in the messaging will already have the values ​​of the string fields in upperCase, which will be received by the consumer and passed on to the integrator, who will not need to worry about carrying out this type of treatment, just applying other business rules and persisting the data .&lt;/p&gt;

&lt;p&gt;This will be possible because Jackson (standard library for serializing and deserializing java objects to json) uses ObjectMapper to serialize objects. Thus, we can add a custom module to the object mapper to serialize specific types of data, in this case, strings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Base Scenario:
&lt;/h2&gt;

&lt;p&gt;As the basis of the application, we will use a small API that receives student data in its request and then sends a response with that same data. We will not focus on business rules, persistence, messaging communications as this is not our focus, we will talk directly about customized object serialization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Controller Class:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It receives a request at the POST /students endpoint and returns an OK status with the serialized object (A String). This string returned from the service is the result of the serialization done by the object mapper that you will see in the implementation of the service class.&lt;br&gt;
As we are not going to implement messaging here, this String returned from the endpoint will represent the JSON sent to the messaging.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@RestController
public class StudentController {

    @Autowired
    private StudentService studentService;

    @PostMapping("/students")
    ResponseEntity&amp;lt;String&amp;gt; createStudent(@RequestBody StudentRequest studentRequest) {

        String studentJson = studentService.createStudent(studentRequest);

        return ResponseEntity.ok().body(studentJson);
    }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Request Class:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Data
public class StudentRequest {

    private Long registration;
    private String name;
    private String lastName;

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Service Class:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Override
    public String createStudent(StudentRequest studentRequest) {

        //some business logics
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(studentRequest);

        //send json to messaging (kafka)

        return json;
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This implementation as is generates the following result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn7c19pksz9l1eievrpvj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn7c19pksz9l1eievrpvj.png" alt="POSTMAN screenshot showing a request with lower case values ​​returning a response with lower case values ​​too"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's move on to our implementation:&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the Custom Serializer:
&lt;/h2&gt;

&lt;p&gt;Let's start by creating a class that will be responsible for overriding the object mapper's default string object serialization behavior. Let's call this class &lt;strong&gt;UpperCaseSerializer.class&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;

public class UpperCaseSerializer extends JsonSerializer&amp;lt;String&amp;gt; {


    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if(s != null &amp;amp;&amp;amp; !s.isEmpty() &amp;amp;&amp;amp; !s.equals("null")) {
            jsonGenerator.writeString(s.toUpperCase());
        } else {
            jsonGenerator.writeNull();
        }
    }

    @Override
    public Class&amp;lt;String&amp;gt; handledType() {
        return String.class;
    }
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that it extends the JsonSerializer class by passing within the diamond operator (&amp;lt;&amp;gt;) the type of object we want to implement our serializer, in this case, String.&lt;/p&gt;

&lt;p&gt;Then we override the serialize method, which receives the String to be serialized, a JsonGenerator and a SerializerProvider.&lt;br&gt;
We then check for null or empty fields and, if it is not null or empty, the jsonGenerator will write the String (writeString method) receiving our string(s) with the toUpperCase() method, that is, it will write our String in the value of each string field as uppercase.&lt;/p&gt;

&lt;p&gt;If it is empty or null, it will write null.&lt;/p&gt;

&lt;p&gt;Okay, now that we've written our custom Serialization class, just run the program? Not yet, as we have to tell the object mapper that it should use this class. We will do this in our service, right after instantiating the object mapper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;StudentServiceImpl Class:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.seralization.example.request.StudentRequest;
import com.seralization.example.service.StudentService;
import com.seralization.example.util.StudentMapper;
import com.seralization.example.util.UpperCaseSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Override
    public String createStudent(StudentRequest studentRequest) {
        try {
            //some business logics

            ObjectMapper mapper = new ObjectMapper();
            mapper.registerModule(new SimpleModule().addSerializer(new UpperCaseSerializer()));
            String json = mapper.writeValueAsString(studentRequest);

            //send json to messaging (kafka)

            return json;
        } catch (JsonProcessingException e) {
            return "";
        }
    }
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Notice that right after initializing the objectmapper we added a new module to it, pointing to our upper case serializer.&lt;/p&gt;

&lt;p&gt;Once this is done, we return this created json and the controller will show it to us in the endpoint response.&lt;/p&gt;

&lt;p&gt;Let's see the result!&lt;/p&gt;

&lt;h2&gt;
  
  
  Result:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5lscu5ufh2ahra1w2a5z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5lscu5ufh2ahra1w2a5z.png" alt="POSTMAN screenshot showing a request with lower case values ​​returning a response with upper case values"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It worked perfectly well for what we proposed.&lt;br&gt;
Remembering that you can create custom serializer for other types of data too, as you prefer! And this isn't the only way to do it and I don't even know if it's the best, but it was the one that helped me when I needed it.&lt;/p&gt;

&lt;p&gt;That's it for now, if you have suggestions, questions or comments, just comment or call me.&lt;br&gt;
If you want to see the repository of this code, it can be found &lt;a href="https://github.com/brunbs/custom-serialization-deserialization-java-spring/tree/custom-serializer" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This repository has the base code in the main branch, a branch implementing our serializer that we saw in this article and also a branch implementing a deserializer that we saw in the previous article.&lt;/p&gt;

&lt;p&gt;I hope you liked it and that it was useful to you.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>jackson</category>
      <category>programming</category>
    </item>
    <item>
      <title>[Java Spring Boot] Como Criar Serializador Personalizado para seus Responses ou Json de saída</title>
      <dc:creator>Bruno Barbosa</dc:creator>
      <pubDate>Tue, 07 Nov 2023 13:16:55 +0000</pubDate>
      <link>https://dev.to/brunbs/java-spring-boot-como-criar-serializador-personalizado-para-seus-responses-ou-json-de-saida-51p0</link>
      <guid>https://dev.to/brunbs/java-spring-boot-como-criar-serializador-personalizado-para-seus-responses-ou-json-de-saida-51p0</guid>
      <description>&lt;p&gt;Este artigo complementa o artigo anterior, que fala sobre criar deserializador customizável para requests, e que você pode lê-lo &lt;a href="https://dev.to/brunbs/java-springboot-como-criar-deserializador-personalizado-para-seus-requests-e-responses-1nnp"&gt;clicando aqui&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Enquanto no artigo anterior falamos sobre &lt;strong&gt;deserialização&lt;/strong&gt; (json --&amp;gt; objeto java), neste abordaremos a &lt;strong&gt;serialização&lt;/strong&gt; (objeto java --&amp;gt; json).&lt;/p&gt;

&lt;h2&gt;
  
  
  Descrição do Problema:
&lt;/h2&gt;

&lt;p&gt;Imagine que você está implementando um sistema que utiliza uma arquitetura baseada em eventos com mensageria, por exemplo, o Kafka.&lt;br&gt;
Então você poderia ter algo parecido com isso:&lt;/p&gt;

&lt;p&gt;a) Um producer, responsável por criar um json (mensagem) que é enviado a um tópico da mensageria.&lt;br&gt;
b) Um consumer, que ficará "escutando" um determinado tópico da mensageria, pegando mensagens da fila e repassando para a próxima API, que vamos chamar de integradora.&lt;br&gt;
c) API integradora, responsável por aplicar regras de negócio para fazer persistência no banco de dados.&lt;/p&gt;

&lt;p&gt;Certo, com essa arquitetura definida e implementada, vamos imaginar o seguinte cenário (exatamente igual ao definido no último artigo):&lt;br&gt;
Você precisa modificar o sistema para que todas as informações do tipo String sejam persistida no banco de dados em upper case.&lt;/p&gt;

&lt;p&gt;Você poderia simplesmente utilizar o método toUpperCase() da classe string em todos os campos antes de persistir? Claro, poderia sim. Porém imagina que você tem muitos campos.&lt;/p&gt;

&lt;p&gt;Passei por essa experiência e tinhamos &lt;strong&gt;mais de 200 campos&lt;/strong&gt; para fazer essa alteração, levando a um gasto de tempo e de linhas de código demasiadamente grandes (além de ser muito chato, convenhamos).&lt;/p&gt;

&lt;p&gt;Na última postagem mostrei como poderíamos fazer para que o request que, nesse caso, chegaria na API integradora, tivesse campos string deserializados de maneira customizada, passando pelo toUpperCase na entrada da aplicação. Aqui vou mostrar uma &lt;strong&gt;forma diferente&lt;/strong&gt;: vamos pegar nossa aplicação producer e fazer com que ela serialize as informações utilizando um serializador personalizado para fazer o toUpperCase para nós. Assim a mensagem na mensageria já estará com os valores dos campos string em upperCase, que será recebida pelo consumer e repassada à integradora, que não precisará se preocupar em fazer esse tipo de tratamento, apenas aplicar outras regras de negócio e fazer a persistência dos dados.&lt;/p&gt;

&lt;p&gt;Isso será possível pois o Jackson (biblioteca padrão para serialização e deserialização de objetos java para json) utiliza o ObjectMapper para serializar objetos. Assim, podemos adicionar ao object mapper um módulo personalizado para serializar tipos específicos de dados, no caso, strings.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cenário Base:
&lt;/h2&gt;

&lt;p&gt;Vamos utilizar como base da aplicação uma pequena api que recebe no seu request dados de estudantes e então envia um response com esses mesmos dados. Não vamos focar em regras de negócio, persistências, comunicações com mensageria pois não é nosso foco, vamos falar diretamente sobre a serialização customizada de objetos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Classe Controller:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ela recebe um request no endpoint POST /students e retorna um status OK com o objeto serializado (Uma String). Essa string retornada do service é resultado da serialização feita pelo object mapper que você verá na implementação da classe de service.&lt;br&gt;
Como não vamos implementar a mensageria aqui, essa String retornada do endpoint irá representar o JSON enviado à mensageria.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RestController
public class StudentController {

    @Autowired
    private StudentService studentService;

    @PostMapping("/students")
    ResponseEntity&amp;lt;String&amp;gt; createStudent(@RequestBody StudentRequest studentRequest) {

        String studentJson = studentService.createStudent(studentRequest);

        return ResponseEntity.ok().body(studentJson);
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Classe de Request:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Data
public class StudentRequest {

    private Long registration;
    private String name;
    private String lastName;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Classe Service:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Override
    public String createStudent(StudentRequest studentRequest) {

        //some business logics
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(studentRequest);

        //send json to messaging (kafka)

        return json;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essa implementação como está gera o seguinte resultado:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g0FUWbDd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n7c19pksz9l1eievrpvj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g0FUWbDd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n7c19pksz9l1eievrpvj.png" alt="POSTMAN screenshot showing a request with lower case values returning a response with lower case values too" width="600" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos, agora, à nossa implementação:&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementando o Serializador Customizado:
&lt;/h2&gt;

&lt;p&gt;Vamos começar criando uma classe que será responsável por sobrescrever o comportamento padrão de serialização de objetos string do object mapper. Vamos chamar essa classe de &lt;strong&gt;UpperCaseSerializer.class&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;

public class UpperCaseSerializer extends JsonSerializer&amp;lt;String&amp;gt; {


    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if(s != null &amp;amp;&amp;amp; !s.isEmpty() &amp;amp;&amp;amp; !s.equals("null")) {
            jsonGenerator.writeString(s.toUpperCase());
        } else {
            jsonGenerator.writeNull();
        }
    }

    @Override
    public Class&amp;lt;String&amp;gt; handledType() {
        return String.class;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perceba que ela extende a classe JsonSerializer passando dentro do operador diamante ( &amp;lt;&amp;gt; ) o tipo do objeto que queremos implementar nosso serializador, no caso, String.&lt;/p&gt;

&lt;p&gt;Então sobrescrevemos o método serialize, que recebe a String a ser serializada, um JsonGenerator e um SerializerProvider.&lt;br&gt;
Fazemos, então, uma verificação de campos nulo ou vazio e, caso não seja nulo ou vazio, o jsonGenerator irá escrever a String (método writeString) recebendo nossa string (s) com o método toUpperCase(), ou seja, ele irá escrever nossa String no valor de cada campo string como uppercase.&lt;/p&gt;

&lt;p&gt;Se estiver vazio ou nulo, ele irá escrever nulo.&lt;/p&gt;

&lt;p&gt;Certo, agora que escrevemos nossa classe de Serialização customizada é só rodar o programa? Ainda não, pois temos que avisar o object mapper que ele deve utilizar essa classe. Faremos isso lá no nosso service, logo após instanciar o object mapper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Classe StudentServiceImpl:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.seralization.example.request.StudentRequest;
import com.seralization.example.service.StudentService;
import com.seralization.example.util.StudentMapper;
import com.seralization.example.util.UpperCaseSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Override
    public String createStudent(StudentRequest studentRequest) {
        try {
            //some business logics

            ObjectMapper mapper = new ObjectMapper();
            mapper.registerModule(new SimpleModule().addSerializer(new UpperCaseSerializer()));
            String json = mapper.writeValueAsString(studentRequest);

            //send json to messaging (kafka)

            return json;
        } catch (JsonProcessingException e) {
            return "";
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perceba que logo após inicializarmos o objectmapper nós adicionamos um novo módulo a ele, apontando para nosso serializador de upper case.&lt;/p&gt;

&lt;p&gt;Feito isso, colocamos para retornar esse json criado e o controller vai nos mostrar no response do endpoint.&lt;/p&gt;

&lt;p&gt;Vamos ver o resultado!&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultado:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--spIweY8W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5lscu5ufh2ahra1w2a5z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--spIweY8W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5lscu5ufh2ahra1w2a5z.png" alt="POSTMAN screenshot showing a request with lower case values returning a response with upper case values" width="758" height="691"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Funcionou perfeitamente bem para o que propomos.&lt;br&gt;
Lembrando que você pode criar serializador personalizado para outros tipos de dados também, como você preferir! E que essa não é a única forma de fazer e nem sei se é a melhor, mas foi a que me atendeu quando precisei.&lt;/p&gt;

&lt;p&gt;Por enquanto é isso, caso você tenha sugestões, dúvidas ou comentários, é só comentar ou me chamar.&lt;br&gt;
Caso você queira ver o repositório deste código, ele se encontra &lt;a href="https://github.com/brunbs/custom-serialization-deserialization-java-spring/tree/custom-serializer"&gt;aqui&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Esse repositório tem o código base na branch main, uma branch implementando o nosso serializador que vimos nesse artigo e também uma branch com a implementação de um deserializador que vimos no artigo anterior.&lt;/p&gt;

&lt;p&gt;Espero que tenha gostado e que tenha sido útil pra você.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>jackson</category>
      <category>programming</category>
    </item>
    <item>
      <title>[Java SpringBoot] How to implement a Custom Deserializer for your Requests</title>
      <dc:creator>Bruno Barbosa</dc:creator>
      <pubDate>Fri, 03 Nov 2023 18:44:54 +0000</pubDate>
      <link>https://dev.to/brunbs/java-springboot-how-to-implement-a-custom-deserializer-for-your-requests-2bo1</link>
      <guid>https://dev.to/brunbs/java-springboot-how-to-implement-a-custom-deserializer-for-your-requests-2bo1</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/brunbs/java-springboot-como-criar-deserializador-personalizado-para-seus-requests-e-responses-1nnp"&gt;Clique aqui para versão em português&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Some days ago a situation arose to be resolved at work and I decided to share the solution here on dev.to!&lt;/p&gt;

&lt;p&gt;I'll try to summarize the problem as much as I can:&lt;br&gt;
We had a Java SpringBoot API that persisted information in a PostgreSQL database.&lt;br&gt;
This API received an object in the request that was made up of around 200 fields, most of them were strings.&lt;/p&gt;

&lt;p&gt;Some time after the development was completed, the need arose for all textual information to be persisted in upper case (LIKE THIS, WITH CAPS LOCK).&lt;/p&gt;

&lt;p&gt;The team then met to decide which approach we would follow to modify the API in question.&lt;br&gt;
The first proposed alternative was to receive all the fields and manually use the toUpperCase() method of the String class to convert attribute by attribute to upper case.&lt;/p&gt;

&lt;p&gt;As the API had a certain complexity, the time estimated by TL for the task was approximately 1 week.&lt;/p&gt;

&lt;p&gt;As we work with tight deadlines, I thought of another approach to reduce the time cost of the solution: make a custom deserializer so that all string fields are converted to upper case upon arrival of the request, when mapped to an object at the input of the API endpoint.&lt;/p&gt;

&lt;p&gt;This development took about 30 minutes to complete, between research and development, saving the team almost 1 week of work and worked very well.&lt;/p&gt;

&lt;p&gt;I will share with you what was developed using a small use case example API. I would like to make it clear that the objective here is not whether this is the best practice (that's up to you to decide with your team according to your needs and availability), I also didn't worry about making the code more foolproof here in the demonstration, so I didn't include, for example, try catch because this is not the focus of this article.&lt;/p&gt;

&lt;p&gt;For those who want to see the **repository **of the code used, here is the link:&lt;br&gt;
GitHub Repository&lt;/p&gt;

&lt;p&gt;In the repository you will find the main branch with the project's base code before implementation.&lt;br&gt;
You will also find the branch where the custom deserializer was implemented and, also, a branch where I implemented a custom serializer if you want the application to be output, when assembling the JSON that comes out of your application.&lt;br&gt;
(I'll talk about the serializer in the next article!)&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the application:
&lt;/h2&gt;

&lt;p&gt;To give an example, I created a small application that has the POST /students endpoint as its input port, which expects to receive a JSON in the body of the request that will be mapped to an object of the StudentRequest class. Below is the code for the StudentController.java class&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@RestController
public class StudentController {

    @Autowired
    private StudentService studentService;

    @PostMapping("/students")
    ResponseEntity&amp;lt;StudentResponse&amp;gt; createStudent(@RequestBody StudentRequest studentRequest) {
        return ResponseEntity.ok().body(studentService.createStudent(studentRequest));
    }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can see that this class is receiving injection from the service that will handle the business rules.&lt;/p&gt;

&lt;p&gt;Our Request class looks like this (StudentRequest.java):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Data
public class StudentRequest {

    private Long registration;
    private String name;
    private String lastName;

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The response has the same fields, but with the name StudentResponse.&lt;/p&gt;

&lt;p&gt;This service, StudentService.java, will have the business rules to then persist the Student information in the database. I didn't mind implementing persistence in the database because it's not the focus here, but basically we would have a service like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Override
    public StudentResponse createStudent(StudentRequest studentRequest) {

        //some business logics
       // persistance ommited

        return studentMapper.requestToResponse(studentRequest);
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that here, to shorten transformations from the request class to the entity or response class, I am using MapStruct (I will write about MapStruct in another article).&lt;/p&gt;

&lt;p&gt;When we run the project, we can see the following behavior:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95u6qew97latvzfwekla.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95u6qew97latvzfwekla.png" alt="Postman imagem of the request with text field in lower case being returned as they were received, with no modifications"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right, the code written this way (which you can see in the main branch of the repository) ends up persisting the information as it arrives in the request, without any treatment or conversion of the fields into upper case as we want. Let's go, then, to the implementation of our Deserializer, so that the String fields go through toUpperCase() immediately upon entering the request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the Deserializer:
&lt;/h2&gt;

&lt;p&gt;This solution is incredibly simple, let's just create a class called ToUpperCaseConverter.java and make it extend Jackson's StdConverter class.&lt;br&gt;
Inside the diamond operator ( &amp;lt;&amp;gt; ) we will place two types of object, one for input (IN) and one for output (OUT), that is, the StdConverter has as its "signature", StdConverter. As we are going to receive a String and return a String but in upper case, we will use StdConverter.&lt;/p&gt;

&lt;p&gt;And then we will override the convert(String value) method by placing our toUpperCase() in the implementation of this method. Our class looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import com.fasterxml.jackson.databind.util.StdConverter;

public class ToUpperCaseConverter extends StdConverter&amp;lt;String, String&amp;gt; {

    @Override
    public String convert(String value) {
        return value.toUpperCase();
    }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This class, for organization purposes, I placed inside a package called utils.&lt;/p&gt;

&lt;p&gt;Now, how do we mark the fields that need to undergo this conversion? To do this, we just go to our request class and add the @JsonDeserialize annotation and pass to it which class will apply the custom deserialization we created, that is, the ToUpperCaseConverter.java class. Let's add this annotation to all the fields that we need to perform this conversion, thus creating our Request class:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@Data
public class StudentRequest {

    private Long registration;
    @JsonDeserialize(converter = ToUpperCaseConverter.class)
    private String name;
    @JsonDeserialize(converter = ToUpperCaseConverter.class)
    private String lastName;

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, we can run our API and see the result, as shown in the POSTMAN screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwlic2dxkek0jlxgk2vum.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwlic2dxkek0jlxgk2vum.png" alt="Image of the Postman application showing a call with string fields in lower case and the response with fields in upper case after conversion"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can make many adaptations to this logic, including creating a custom Deserializer for the entire Request class! Going field by field instructing what manipulation you want to do with the field, but that's for another post.&lt;/p&gt;

&lt;p&gt;I hope you were able to contribute and questions and suggestions can be sent here, I would be very grateful if you have improvements to suggest.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>programming</category>
      <category>jackson</category>
    </item>
    <item>
      <title>[Java SpringBoot] Como Criar Deserializador Personalizado para seus Requests</title>
      <dc:creator>Bruno Barbosa</dc:creator>
      <pubDate>Fri, 03 Nov 2023 18:33:26 +0000</pubDate>
      <link>https://dev.to/brunbs/java-springboot-como-criar-deserializador-personalizado-para-seus-requests-e-responses-1nnp</link>
      <guid>https://dev.to/brunbs/java-springboot-como-criar-deserializador-personalizado-para-seus-requests-e-responses-1nnp</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Esses dias surgiu uma situação a ser resolvida no trabalho e resolvi compartilhar a solução aqui no dev.to!&lt;/p&gt;

&lt;p&gt;Vou tentar resumir o problema o máximo que eu conseguir:&lt;br&gt;
Tínhamos uma API em Java SpringBoot que persistia informação em um banco de dados PostgreSQL.&lt;br&gt;
Essa API recebia um objeto no request que era composto por cerca de 200 campos, a maioria deles eram strings.&lt;/p&gt;

&lt;p&gt;Um tempo após o desenvolvimento ter sido concluído, surgiu a necessidade de todas as informações textuais serem persistidas em upper case (TIPO ASSIM, COM CAPS LOCK).&lt;/p&gt;

&lt;p&gt;O time, então, se reuniu para decidir qual abordagem seguiríamos para modificar a api em questão.&lt;br&gt;
A primeira alternativa proposta era receber todos os campos e, manualmente, utilizar o método toUpperCase() da classe String para converter atributo por atributo para upper case.&lt;/p&gt;

&lt;p&gt;Como a API tinha certa complexidade, essa solução poderia acabar levando muito tempo e comprometendo a entrega da squad.&lt;/p&gt;

&lt;p&gt;Como trabalhamos com prazos apertados, pensei em uma outra abordagem para diminuir o custo de tempo da solução: fazer um deserializador personalizado para que, todos os campos string sejam convertidos em upper case na chegada do request, ao ser mapeado para um objeto na entrada do endpoint da API.&lt;/p&gt;

&lt;p&gt;Esse desenvolvimento levou cerca de 30 minutos para ser concluído, entre pesquisa e desenvolvimento, economizando um bom tempo de trabalho na equipe e funcionou muito bem.&lt;/p&gt;

&lt;p&gt;Vou compartilhar com vocês o que foi desenvolvido utilizando uma pequena API de exemplo de caso de uso. Gostaria de deixar claro que o objetivo aqui não é se esta é a melhor prática (isso você decide com seu time de acordo com suas necessidades e disponibilidades), também não me preocupei em fazer o código mais a prova de falhas aqui na demonstração, então não coloquei, por exemplo, try catch pois este não é o foco deste artigo.&lt;/p&gt;

&lt;p&gt;Para quem quiser ver o **repositório **do código utilizado, aqui está o link:&lt;br&gt;
&lt;a href="https://github.com/brunbs/custom-serialization-deserialization-java-spring"&gt;&lt;strong&gt;Repositório GitHub&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No repositório você irá encontrar a branch main com o código base do projeto antes da implementação.&lt;br&gt;
Encontrará, também, a branch onde foi implementado o deserializador customizado e, também, uma branch onde implementei um serializador customizado caso você queira que a aplicação seja na saída, na hora de montar o JSON que sai da sua aplicação.&lt;br&gt;
(Vou falar sobre o serializador no próximo artigo!)&lt;/p&gt;
&lt;h2&gt;
  
  
  Apresentando a aplicação:
&lt;/h2&gt;

&lt;p&gt;Para exemplificarmos, criei uma pequena aplicação que possui como porta de entrada o endpoint POST /students, que espera receber no corpo da requisição um JSON que será mapeado para um objeto da classe StudentRequest. Segue abaixo o código da classe StudentController.java&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RestController
public class StudentController {

    @Autowired
    private StudentService studentService;

    @PostMapping("/students")
    ResponseEntity&amp;lt;StudentResponse&amp;gt; createStudent(@RequestBody StudentRequest studentRequest) {
        return ResponseEntity.ok().body(studentService.createStudent(studentRequest));
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos ver que esta classe está recebendo injeção do service que irá tratar das regras de negócio.&lt;/p&gt;

&lt;p&gt;A nossa classe de Request está desta maneira (StudentRequest.java):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Data
public class StudentRequest {

    private Long registration;
    private String name;
    private String lastName;

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O response tem os mesmos campos, porém com nome de StudentResponse.&lt;/p&gt;

&lt;p&gt;Este service, o StudentService.java terá as regras de negócio para depois persistir as informações do Student no banco de dados. Não me importei em implementar a persistência no banco pois não é o foco aqui, mas basicamente teríamos um service desta forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Override
    public StudentResponse createStudent(StudentRequest studentRequest) {

        //some business logics

        return studentMapper.requestToResponse(studentRequest);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perceba que aqui, para encurtar transformações da classe de request para a classe de entidade ou response estou utilizando o MapStruct (escreverei sobre o MapStruct em outro artigo).&lt;/p&gt;

&lt;p&gt;Ao rodar o programa e chamar o endpoint, temos o seguinte resultado:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95u6qew97latvzfwekla.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95u6qew97latvzfwekla.png" alt="Postman imagem of the request with text field in lower case being returned as they were received, with no modifications" width="600" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Certo, o código escrito desta forma (que vocês podem ver na branch main do repositório) acaba persistindo as informações como elas chegam no request, sem nenhum tratamento ou conversão dos campos em upper case como queremos. Vamos, então, à implementação do nosso Deserializador, para que os campos String passem pelo toUpperCase() logo na entrada da requisição.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementando o Deserializador:
&lt;/h2&gt;

&lt;p&gt;Essa solução é incrivelmente simples, vamos apenas criar uma classe chamada ToUpperCaseConverter.java e faremos ela extender a classe StdConverter do Jackson.&lt;br&gt;
Dentro do operador diamante ( &amp;lt;&amp;gt; ) colocaremos dois tipos de objeto, um de entrada (IN) e outro de saída (OUT), ou seja, o StdConverter tem como "assinatura", StdConverter. Como vamos receber uma String e devolver uma String porém em upper case, vamos utilizar StdConverter.&lt;/p&gt;

&lt;p&gt;E então vamos sobrescrever o método convert(String value) colocando nosso toUpperCase() na implementação deste método. Ficando assim nossa classe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import com.fasterxml.jackson.databind.util.StdConverter;

public class ToUpperCaseConverter extends StdConverter&amp;lt;String, String&amp;gt; {

    @Override
    public String convert(String value) {
        return value.toUpperCase();
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essa classe, para fins de organização, coloquei dentro de um pacote chamado &lt;em&gt;utils&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Agora, como fazemos para marcar os campos que precisam passar por essa conversão? Para isso basta irmos na nossa classe de request e adicionar a anotação @JsonDeserialize e passar para ela qual classe que irá aplicar a deserialização customizada que criamos, ou seja, a classe ToUpperCaseConverter.java. Vamos adicionar essa anotação em todos os campos que precisamos fazer essa conversão, ficando, assim, nossa classe de Request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Data
public class StudentRequest {

    private Long registration;
    @JsonDeserialize(converter = ToUpperCaseConverter.class)
    private String name;
    @JsonDeserialize(converter = ToUpperCaseConverter.class)
    private String lastName;

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora, podemos colocar nossa API para rodar e vermos o resultado, como mostrado no print do POSTMAN abaixo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwlic2dxkek0jlxgk2vum.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwlic2dxkek0jlxgk2vum.png" alt="Imagem da aplicação Postman mostrando uma chamada com campos string em lower case e o response com os campos em upper case após conversão" width="758" height="691"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Você pode fazer muitas adaptações para esta lógica inclusive criando um Deserializador customizado para toda a classe de Request! Passando campo a campo instruindo que manipulação você quer fazer com o campo, mas isso fica para outra postagem.&lt;/p&gt;

&lt;p&gt;Espero que tenha conseguido contribuir e dúvidas e sugestões podem ser enviadas aqui, fico muito grato caso tenham melhorias a sugerir.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>programming</category>
      <category>jackson</category>
    </item>
  </channel>
</rss>
