Rafael

Engenheiro de Software

Olá, Eu sou Rafael. Estou construindo coisas para a web e mobile.

Um desenvolvedor dedicado a criar experiências digitais de alto desempenho, acessíveis e limpas. Eu me especializo em transformar problemas complexos em soluções de engenharia elegantes.

Paginação em spring boot, jpa e kotlin

Ao trabalhar com grandes volumes de dados, retornar todos os registros de uma única vez pode causar problemas de desempenho e aumentar o consumo de memória. Para resolver esse problema, o Spring Data JPA oferece suporte nativo à paginação através das interfaces Pageable e Page.

Configurando o Controller

No endpoint de listagem, utilizamos o parâmetro Pageable para receber automaticamente as informações de paginação enviadas na URL.

KOTLIN
@GetMapping
fun findAll(@PageableDefault(page = 0, size = 10) pageable: Pageable):Page<BookResponse>{
    return bookService.findAll(pageable).map { it.toResponse() }
}

O que significa cada parâmetro?

  • page: página inicial (começa em 0).
  • size: quantidade de registros por página.
  • Pageable: objeto criado automaticamente pelo Spring contendo as informações de paginação e ordenação.

Exemplo de requisição:

HTTP
GET /book

Service

A camada de serviço apenas encaminha o objeto Pageable para o repositório.

KOTLIN
fun findAll(pageable: Pageable): Page<BookModel> {
    return bookRepository.findAll(pageable)
}

Repository

Para utilizar paginação é necessário que o repositório herde de JpaRepository, pois esse método já disponibiliza a função findAll(Pageable).

KOTLIN
interface BookRepository : JpaRepository<BookModel, String> {}

Model

KOTLIN
@Entity(name = "book")
data class BookModel(
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    @Column(length = 36, nullable = false, updatable = false)
    @JdbcTypeCode(Types.VARCHAR)
    var id: String? = null,
    @Column(nullable = false)
    var name:String,

    @Column(nullable = false, precision = 10, scale = 2)
    var price: BigDecimal,
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(
        name = "customer_id",
        nullable = false,
        foreignKey = ForeignKey(name = "fk_book_customer")
    )
    @OnDelete(action = OnDeleteAction.RESTRICT)
    var customer: CustomerModel?=null
){
    @Column(nullable = false)
    @Enumerated(EnumType.STRING)
    var status:BookStatus?=null
        set(value){
            if (field==BookStatus.CANCELED || field == BookStatus.DELETED){
                throw Exception("Não é possível alterar um livro com status $field")
            }

            field=value
        }
    constructor(
        id: String?=null,
        name: String,
        price: BigDecimal,
        customer: CustomerModel?=null,
        status: BookStatus?=null):this(id,name,price,customer){
            this.status=status
        }

}

Conversão para Response

Para não expor nosso model na camada de controller, passamos um response no controller como função de extensão:

KOTLIN
data class BookResponse(
    var id: String? = null,
    var name:String,
    var price: BigDecimal,
    var customer: CustomerModel?=null,
    var status: BookStatus?=null
)
KOTLIN
fun BookModel.toResponse(): BookResponse {
    return BookResponse(
        id = this.id,
        name = this.name,
        status = this.status,
        price = this.price,
        customer = this.customer
    )
}

Podemos alterar o size e o page na url:

HTTP
http://localhost:8080/book?size=1&page=0

Exemplo de saída:

POSTMAN
{
    "content": [
        {
            "id": "2618f213-233e-405f-9216-da4496be0d1e",
            "name": "livro0",
            "price": 90.00,
            "customer": {
                "id": "a47504bb-f054-46dc-a687-5e8bcb221af8",
                "name": "rafael",
                "email": "email2",
                "status": "ACTIVE"
            },
            "status": "ACTIVE"
        }
    ],
    "pageable": {
        "pageNumber": 0,
        "pageSize": 1,
        "sort": {
            "empty": true,
            "unsorted": true,
            "sorted": false
        },
        "offset": 0,
        "unpaged": false,
        "paged": true
    },
    "totalPages": 5,
    "totalElements": 5,
    "last": false,
    "size": 1,
    "number": 0,
    "sort": {
        "empty": true,
        "unsorted": true,
        "sorted": false
    },
    "numberOfElements": 1,
    "first": true,
    "empty": false
}

0 comentários

Deixe seu comentário