JDBI — Uma alternativa além de JDBC e JPA/Hibernate

Janderson Rafael da Silva
CWI Software
Published in
6 min readAug 20, 2020

Recentemente, nos últimos anos, atuei como desenvolvedor backend em projetos de uma empresa do segmento varejista, no qual recebe um volume considerável de transações, principalmente em datas convenientes e que possui uma vasta diversidade de tecnologias, ao qual vão de projetos legados com tecnologias mais antigas e projetos mais novos com tecnologias mais atuais.

Dentro do ecossistema de projetos existentes, a linguagem que foi adotada como principal é o JAVA, e quando olhamos para o aspecto de conexão e interação com banco de dados, existiam duas vertentes implementadas e sendo seguidas, o uso da biblioteca JDBC e o uso do ORM Hibernate.

Além dessas duas opções de tecnologias sendo usadas também existia uma divisão muito clara na equipe entre desenvolvedores que defendiam o uso do JDBC e desenvolvedores que defendiam o uso do Hibernate.

JDBC

O JDBC está presente em sua maioria em projetos mais antigos dentro do sistema, porém ainda ocorre sua implementação em projetos mais recentes sob a alegação de alguns aspectos que buscam justificá-lo, como por exemplo, sua performance, que devido a ser uma biblioteca de baixo nível proporciona uma interação otimizada com o banco, o que se torna muito relevante em um cenário de um ecommerce. A certeza que o desenvolvedor possui de que será executado no banco exatamente o que se escreve, sem ter a ocorrência de queries extras desnecessárias ou não otimizadas que um ORM mal implementado pode facilmente causar, também e uma das justificativas.

Embora existam essas características que apoiem o JDBC, também existem pontos que o desfavorecem, como por exemplo sua sintaxe que em muitos cenários pode exigir a escrita de muito código para tarefas rotineiras e simples, causando dificuldade de manutenção e pouca produtividade.

ORM Hibernate

Diversos projetos foram criados, principalmente os mais recentes, utilizando o framework ORM Hibernate como implementação JPA, juntamente com a abstração Spring Data JPA. O Hibernate é muito popular e tem sido muito presente em projetos desenvolvidos atualmente pela comunidade de desenvolvedores, o que justificam essa adoção é principalmente pela sua promessa de facilidade de desenvolvimento de CRUDs de forma rápida e simples, visando apoiar na produtividade e diminuição de código escrito.

Olhando a primeira vista em tutoriais com exemplos simples dispostos na internet, fica evidente um código claro e simples de escrever para fazer implementações muito rápidas que auxiliam a fazer endpoints de tela de forma muito ágil, porém utilizando-o em um cenário mais complexo que exige queries complexas ou um grande volume de transações, começam a ocorrer problemas principalmente de performance que começam a tornar discutível o seu uso, como por exemplo problemas de N+1 e problemas de mapeamento que se tornam muito difíceis de resolver de forma simples e sem impactar em outros pontos do sistema. Ao utilizar esse ORM em uma equipe que possui diversos integrantes, com diferentes conhecimentos de hibernate é muito comum ocorrerem problemas de performance, de queries que estão sendo executadas a mais ou de forma muito pouco otimizada, devido a passar despercebido pelo desenvolvedor o resultado final das queries geradas automaticamente. E a solução para esses problemas podem não ser muito simples, ao ponto de a manutenção ter um impacto tao significado que acabe diminuindo ou até mesmo eliminando a produtividade que se buscava alcançar.

JDBI

Pensando nas desvantagens e vantagens que considero que ambas as opções, JDBC e Hibernate possuem, encontrei uma terceira opção que ao meu ver consegue unir a performance que o JDBC proporciona e a produtividade que o Hibernate busca oferecer, que é a biblioteca JDBI.

O JDBI é uma biblioteca open source que atua como uma camada de abstração que roda em cima do JDBC. O JDBI fornece uma api de alto nível que facilita a interação do desenvolvedor com o banco, de forma produtiva e performática, sem a realização de queries mágicas sem seu consentimento, ele não é um ORM.

O JDBI possui duas formas de utilização, a Fluent API a nível de serviço e a Declarative API a nível interface, com uso de anotações.

1- Fluent API

2 — Declarative API

JDBI vs JDBC

Conforme comentado, um dos problemas apontados no uso do JDBC, é o fato dele ser baixo nível e trabalhoso para escrever instruções comuns de banco.

Segue abaixo exemplos de escrita de select e insert, utilizando JDBC com a sintaxe da classe NamedParameterJdbcTemplate, e JDBI nos modos interativo e fluente.

1 — Exemplo de select com múltiplas cláusulas:

1.1 — JDBC

1.2 — JDBI

JDBI Fluent API

JDBI Declarative API

2 — Exemplo de insert com múltiplos campos:

2.1 — JDBC

2.2 — JDBI

JDBI Fluent API

JDBI Declarative API

Nesses dois exemplos o JDBI demonstra uma sintaxe mais clara e fluída se comparada ao JDBC.

JDBI vs JPA/Hibernate

Conforme comentado, um dos problemas apontados no uso do JPA/Hibernate é o fato dele acabar executando muitas coisas de forma não muito clara, e que quando o desenvolvedor não possui absoluto conhecimento pode se tornar um problema.

Segue abaixo exemplos da forma simplificada de uso do Hibernate, a abstração Spring Data Jpa comparando com o uso JDBI nos modos interativo e fluente, em uma listagem de uma entidade que contém um relacionamento de 1 para N com outra entidade.

Exemplo de select com relacionamento de 1 x N

Spring data JPA Query Methods

Parece ótimo e bem conveniente, pois com uma linha esta pronta a busca de uma lista de pedidos, porém esse é um cenário clássico de N +1, onde para cada order encontrada sera feita uma segunda query para buscar os itens. Um problema grande onde um programador pouco atento pode não notar.

Então como corrigi-lo?

Alterar o mapeamento para buscar itens de forma LAZY pode parecer resolver para esse caso, mas devemos considerar que alterar o mapeamento de uma model significa impactar em outros pontos que utilizam, onde podem não precisar dos dados de itens e que mudar esse comportamento iria resolver aqui e trazer problemas em outros locais, então que opções temos?

Dentre as opções para contornar esse problema, podemos fazer a consulta utilizando uma query nativa, porém perderíamos a característica importante do hibernate de ser independente de banco de dados.

Outra alternativa mais adequada, seria mapear todas os relacionamentos das entidades com carregamento LAZY e utilizar as consultas na forma JPQL, que é uma linguagem própria do hibernate, ao qual permite sobrescrever o carregamento de items para o modo EAGER, e resolver o problema de N + 1, trazendo junto os itens sem impactar no mapeamento. Porém, será que ainda se torna conveniente e produtivo essa escrita, considerando que não podemos mais usar a sintaxe de query methods do Spring Data Jpa em casos que precisamos trazer não apenas a entidade, mas também alguma entidade relacionada? O que acontece se alguém da equipe alterar o mapeamento de items e configurar outros objetos sendo carregados de forma LAZY que não desejamos? Entramos em um loop de problemas de carregamentos indesejados que o tornam difícil de resolver, isso sem falar em problemas para escrever queries complexas e buscar atributos específicos de objetos.

JDBI Fluent

JDBI Declarative

O JDBI oferece formatos que possibilitam trazer em uma única ida ao banco relacionamento de 1 x N de forma clara, não tao fácil como JPA, mas que proporciona uma confiança muito maior de que o que esta sendo executado é realmente a sua necessidade, sem acontecer surpresas não desejáveis, que implicam em problemas sérios de performance.

Benchmark

Segue abaixo resultados obtidos através de um benchmark feito com a ferramenta JMH, onde foi feito uma série de chamadas testando as 4 operações básicas de forma indivivual: insert, select, update e delete.

Com base nesses resultados, podemos observar que obviamente o JDBC possui uma melhor performance em todas as operações, considerando ter uma escrita em baixo nível e ser a base para os outros dois. Também é possível observar que JPA foi superior nas operações de insert e update, e que JDBI foi superior nas operações de select e delete.

Embora tenhamos a constatação de que JPA/Hibernate demonstrou-se ser mais rápido nas operações de insert e update, devemos considerar os pontos já comentados onde o problema do Hibernate não é visível nesses cenários que são simples, onde não envolvem relacionamentos e montagem de queries complexas.

Conclusão

O objetivo desse artigo foi demonstrar problemas enfrentados com as duas populares opções JAVA de comunicação com o banco existentes no mercado, JDBC e Hibernate e apresentar o JDBI como uma possível opção de estudo para utilização em projetos, visando atender sob a perspectiva de performance e produtividade, resolvendo alguns problemas de suas concorrentes.

O código fonte utilizado para os testes apresentados nesse artigo encontram-se em https://github.com/jandersonrafa/jdbi-benchmark.

Referências

--

--