20 pontos por carnoxen 2024-12-05 | 22 comentários | Compartilhar no WhatsApp

Resumo

JPA/Hibernate se tornou um framework muito usado porque, em código Java, ele elimina a necessidade de escrever SQL diretamente. Mas eu gostaria de defender que ele não deve ser usado em projetos novos.

Motivos

Documentação oficial excessivamente longa

Se você converter a documentação oficial em PDF, ela chega a impressionantes 406 páginas, mais do que O Senhor dos Anéis (231 páginas) e o documento padrão do SQL (288 páginas). Não é preciso fazer um mestrado para aprender consultas a banco de dados.

Mutabilidade

  1. Mesmo que uma entidade precise de um determinado elemento, ela é obrigada a ter um construtor sem argumentos.
  2. Não é possível impedir herança apenas colocando as palavras-chave final ou abstract na classe da entidade.
  3. Reflection/Introspection ignora o princípio de encapsulamento da OOP.
  4. Alguém pode inserir código malicioso e apagar completamente os dados.

Lazy loading e cache

  1. A anotação @Lazy é uma das piores tecnologias para iniciantes. Ainda assim, é difícil evitá-la quando o design de domínio não se encaixa bem no Hibernate ou quando não se consegue escrever a consulta necessária.
  2. O mecanismo de cache é difícil de entender. E, mesmo entendendo, é preciso armazenar em cache a própria entidade, e não o resultado da consulta.

Sincronização entre memória e banco de dados (Flush)

A técnica chamada Flush sincroniza os objetos armazenados em memória com o banco de dados. Isso causa dois problemas.

  • Quando o Flush entra em ação, as edições em memória acabam sendo concluídas, o que praticamente inviabiliza o uso de qualquer outra ferramenta de persistência além do Hibernate, e
  • se ocorrer um conflito durante o Flush, pode surgir um erro de Stack Trace sem relação direta com o código.

Obter apenas uma coluna específica de uma tabela

Se você quiser acessar apenas uma coluna de uma entidade, a abordagem em SQL é simples assim.

select url from image   
where id = 'F462E8D9-9DF7-4A58-9112-EDE0434B4ACE';  

Mas o Hibernate sempre busca todas as colunas da entidade. Para evitar isso, é preciso passar por um processo complexo.

Definição de restrições (Constraint) em colunas

Para definir restrições em uma coluna, é preciso adicionar várias anotações como abaixo.

...  
    @NotNull  
    @NotEmpty  
    @Email  
    private String email;  
...  

Esse método gera os seguintes problemas.

  • Não é possível fazer testes unitários para essas condições.
  • Durante a operação de Flush, já é tarde demais para entender esse processo de validação.
  • As exceções possíveis são genéricas e pouco úteis.
  • As regras de negócio acabam sendo tratadas apenas como regras técnicas.

Problemas de estratégia

  1. As atualizações do framework são péssimas, ignoram compatibilidade retroativa e forçam uma dependência incondicional. A empresa que o cria passa a agir como se fosse natural usar o próprio framework para manter o domínio. Esse ciclo vicioso precisa ser interrompido.
  2. Achar que um Proof of Concept só pode ser obtido por meio de um framework apenas estreita sua visão, e isso é ainda mais verdadeiro no caso de JPA/Hibernate. Não se deve tolerar isso nem minimamente.

O que fazer?

Use SQL

Tudo pode ser feito apenas com SQL. Todo programador conhece, as consultas são intuitivas e não é necessário nenhum framework.

E se o gerente mandar usar Hibernate?

Peça demissão ou trabalhe para separar o código do framework.

Se você já usa...

  1. Não torne public o nível de acesso do construtor padrão nem dos setters.
  2. Use strings como UUID em vez de IDs gerados por SQL.
  3. Em vez de usar o nome XXXRepository, use XXXDao.
  4. Não use a anotação @SequenceGenerator.
  5. Separe as classes de domínio e as classes DAO com intarface.
  6. Não use relacionamentos muitos-para-muitos (@OneToMany etc.); se possível, evite até mesmo o mapeamento de entidades.

Conclusão

JPA/Hibernate, abandone.

  • Existem documentos mais curtos e melhores para resolver problemas de negócio.
  • Não insista apenas em formas de design rápidas demais.
  • Tenha consideração pelo próximo desenvolvedor que terá de manter seu código.

O que vocês usam?

  1. JPA/Hibernate
  2. Outra tecnologia ORM

22 comentários

 
askaskm 2024-12-16

Discordo da opinião de que devemos abandonar JPA/Hibernate.

"Documentação oficial muito longa"
Até SQL é difícil quando aprendemos no começo. É fácil entender perfeitamente joins complexos, subqueries, funções de procedure e afins?
Com JPA, basta começar entendendo apenas os conceitos centrais. O conteúdo mais profundo pode ser consultado quando surgir a necessidade.
E existem os LLMs.

"Problemas de mutabilidade e Reflection"
Essa é uma preocupação que surge por não entender como o framework funciona.
Na prática, quase nunca aparecem problemas reais por causa disso no trabalho.
Pelo contrário, o mapeamento de objetos é automatizado via Reflection, o que aumenta bastante a produtividade.

"Lazy loading e cache"
@Lazy é a "pior tecnologia"? É um recurso muito útil para resolver o problema de N+1 e otimizar o desempenho.
O mecanismo de cache, ao contrário, ajuda muito a melhorar o desempenho.

"Obter apenas colunas específicas de uma tabela"
Usando JPQL ou Projection, dá para consultar facilmente só as colunas necessárias.
E também dá para usar junto com QueryDSL.

Acho que o objetivo do ORM não é substituir completamente o SQL, mas ajudar o desenvolvedor a se concentrar mais na lógica de negócio..

 
bbulbum 2024-12-09

Sou meio pessimista em relação a ORM, mas acho que ele não conseguiu apresentar alternativas suficientes.

Quando você vai por um caminho muito carregado de ORM, isso realmente parece não ter fim e, como foi mencionado acima, você pode acabar se debatendo em uma documentação com um escopo ainda maior do que a própria documentação de SQL e morrer ressecado no processo.

Recentemente, estou desenvolvendo um projeto pessoal sem usar ORM, mas, ao tentar fazer um design pensando em reutilização, às vezes acabo indo numa direção que me faz pensar que, no fim das contas, estou simplesmente criando um ORM. rsrs

 
ilbanin00 2024-12-07

Parece que o fato de usar um framework permitir compartilhar um paradigma comum com outros desenvolvedores que também o utilizam comercialmente é sempre ignorado em textos que dizem para não usar esse tipo de coisa.

 
dothx 2024-12-06

Quando há muitas tabelas e muitas colunas (por exemplo, 50 tabelas, com mais de 100 colunas por tabela), usar SQL puro acaba virando um verdadeiro inferno.

Por outro lado, acho um enorme desperdício usar JPA/Hibernate para criar um serviço pequeno.

No fim das contas, esse tipo de opinião realmente depende muito do caso.

(O exemplo usado aqui também é de colunas com só 3 ou 4 campos...)

 
jpumpkin94 2024-12-06

A última pergunta no texto acima talvez precise ser ajustada um pouco.

No ecossistema Java, dá para organizar isso como 1. ORM vs 2. Non-ORM.

  1. Na prática, quando se fala em ORM, quase sempre estamos falando da combinação JPA/Hibernate.
  2. Há opções como MyBatis, JOOQ e SpringDataJDBC. Em geral, nelas você acaba lidando diretamente com SQL.

Tanto 1 quanto 2 têm vantagens e desvantagens bem claras, então não é adequado chegar a uma conclusão tão extrema como a do texto acima.

No nosso caso,
usamos JPA/Hibernate/QueryDSL como ORM e, ao mesmo tempo, também usamos MyBatis.

Usamos ORM para maximizar a produtividade o máximo possível e,
para consultas que são difíceis de cobrir com ORM, usamos MyBatis.

E, independentemente de escolher 1 ou 2 acima, é preciso conhecer bem SQL.

 
carnoxen 2024-12-06

Eu também queria editar, mas como o site não tem esse recurso...

 
kallare 2024-12-06

Acho que estão meio que fingindo não saber por que ORM ficou popular em primeiro lugar.

Tem um certo custo de aprendizado, mas depois que você se acostuma, claramente há ganho de produtividade.

SQL parece simples, mas o cansaço de codificar SQL detalhe por detalhe... além disso, quando a tabela muda, também é preciso alterar manualmente todas as queries relacionadas, então manter SQL está longe de ser uma tarefa fácil; por ser pequeno e simples, a quantidade de trabalho aumenta (por isso a discussão sobre produtividade continua aparecendo).

Além disso, erros que acontecem em SQL estouram em tempo de execução e são difíceis de capturar, e quando você vai implementando defesa contra ataques como SQL injection detalhe por detalhe, no fim acaba adicionando código para gerar queries (geralmente começando por algo simples, em formato de template...). E, se continuar nesse caminho, no fim vai surgir de novo algo parecido com um ORM, então não seria melhor simplesmente usar ORM?

Isso me lembrou um texto que apareceu dias atrás
https://pt.news.hada.io/topic?id=17955

 
laracool 2024-12-06

Concordo.
Acho que muitas vezes não se entende plenamente por que se usa ORM e quais são as suas vantagens.

Além disso, também não parece haver muitas pessoas tentando analisar ou entender o SQL que de fato é executado por meio do ORM.
Seria bom se fosse mais difundida a ideia de que isso pode ajudar não apenas em termos de conveniência, mas também a compreender mais profundamente a otimização de SQL e o funcionamento do banco de dados.

 
jamsya 2024-12-06

Acho que não há necessidade de pender para os extremos de “tem que usar” ou “não pode usar” haha;;
Se produtividade é importante, eu uso ORM,
mas consultas complexas que o ORM não consegue cobrir, ou consultas que precisam de mais otimização, eu trato com query bruta.
Acho que a escolha entre ORM e raw query deve ser feita de forma adequada conforme a situação e o que se quer construir.

 
xhfleodhkd 2024-12-06

De modo geral, acho que isso pode valer para dados em que a normalização do banco está bem feita e não há muita necessidade de fazer join,
mas, se não for possível gerenciar tudo corretamente por meio de um DBA, começando pela própria normalização do banco, então ORM também pode ser uma boa escolha. Especialmente, considero um ótimo exemplo de por que se usa ORM o benefício que surge ao buscar via relationship no lado que recupera dados por meio de join.
Claro, concordo com a opinião de que frameworks limitam o crescimento do desenvolvedor e que devemos reduzir a dependência deles,
mas não consigo concordar facilmente com a ideia de simplesmente não usar ORM em hipótese alguma.
Parece partir da premissa de que todas as empresas têm DBA e desenvolvem usando metodologias adequadas como DDD ou TDD,
e, na prática, se isso fosse feito daquele jeito no trabalho real, nem sei o quanto o código poderia virar uma bagunça ainda maior.

 
aer0700 2024-12-06

Quando faço backend em PYTHON, costumo usar sempre algo como SQLALCHEMY ou DJANGO ORM.
Acabei me acostumando tanto com escrever SQL diretamente quanto com usar ORM que nem sentia muita diferença entre os dois, então não estava pensando muito sobre isso. Mas aparentemente também existe a opinião de que é melhor não usar ORM.
Concordo com a ideia de reduzir a dependência de frameworks. Não dá para saber usar só o DJANGO ORM e não saber lidar com SQL...

 
beoks 2024-12-06

Hum, não sei... eu discordo. Atualmente opero um serviço que tem cerca de 3 mil tabelas e, como o domínio é extremamente complexo, para montar uma única query eu basicamente acabava escrevendo facilmente dezenas de linhas. Se pensar ainda em queries dinâmicas, aí a cabeça realmente dói. Como tudo é complexo, também surgiam muitos bugs e a manutenção era difícil. Eu acho que, em domínios complexos, ORM é mais vantajoso.

 
xhfleodhkd 2024-12-06

No meu caso, já tive experiência mantendo um banco de dados sem normalização adequada.
Na época, eu escrevia consultas dinâmicas em SQL comum, sem usar ORM,
e, por causa disso, houve situações em que o código acabava ficando mais difícil de entender.
Acho que há espaço suficiente para adotar isso não só em domínios complexos, mas também em domínios com normalização insuficiente.

 
carnoxen 2024-12-06

Ah, então talvez não precise ser visto de forma negativa.

 
tsboard 2024-12-06

Pessoalmente, eu também recomendaria simplesmente usar SQL. No mundo de JS, muita gente usa ferramentas como o Prisma, mas SQL nem é uma linguagem tão difícil assim, e isso acaba exigindo abstrações desnecessárias demais para I/O de banco de dados, então eu fico um pouco relutante com isso.

 
znjadong 2024-12-06

Acho que isso também pode ser porque muitos ORMs do lado de js/ts são excepcionalmente malfeitos.

 
carnoxen 2024-12-06

Algo como JDBC já serve, certo? Lembrei de alguém que já trabalhou comigo dizer antes: "JPA é lento, então use outra coisa".

 
roxie 2024-12-06

Parece história de lenda.

 
caniel 2024-12-06

Parece que a tendência é abandonar frameworks e voltar ao básico. HTMX, SQL e por aí vai..

 
carnoxen 2024-12-06

Mas tem a desvantagem de precisar reinventar a roda.

 
misolab 2024-12-06

Tem suas vantagens também..
2. MyBatis (embora não seja um ORM rs)

 
carnoxen 2024-12-06

Era melhor ter trocado para DAO em vez de ORM.