Dejen JPA/Hibernate
(stemlaur.com)Resumen
JPA/Hibernate se ha convertido en un framework muy usado porque ya no hace falta escribir SQL directamente en código Java. Sin embargo, quiero argumentar que no debería usarse en proyectos nuevos.
Razones
Documentación oficial demasiado larga
Si conviertes la documentación oficial a PDF, tiene nada menos que 406 páginas, más que El Señor de los Anillos (231 páginas) y que el documento del estándar SQL (288 páginas). No hace falta cursar una maestría para aprender consultas de base de datos.
Mutabilidad
- Aunque una entidad necesite cierto elemento, te obliga a tener un constructor sin argumentos.
- No se puede impedir la herencia agregando las palabras clave
finaloabstracta la clase de entidad. - Reflection/Introspection ignora el principio de encapsulación de la POO.
- Alguien puede insertar código malicioso y borrar por completo los datos.
Lazy loading y caché
- La anotación
@Lazyes una de las peores tecnologías para principiantes. Pero es difícil evitarla cuando el diseño del dominio no encaja con Hibernate o cuando no puedes escribir consultas. - El mecanismo de caché es difícil de entender. Y aun si lo entiendes, tienes que guardar en caché la entidad en sí, no el resultado de la consulta.
Sincronización memoria-base de datos (Flush)
La técnica llamada Flush sincroniza los objetos almacenados en memoria con la base de datos. Esto provoca dos problemas.
- Cuando Flush actúa, las ediciones en memoria se dan por terminadas, así que ni pensar en usar otra herramienta de persistencia que no sea Hibernate, y
- Si ocurre un conflicto durante Flush, pueden aparecer errores de Stack Trace que no tienen relación con el código.
Obtener solo ciertas columnas de una tabla
Si quieres acceder a una sola columna de una entidad, el enfoque con SQL es así de simple.
select url from image
where id = 'F462E8D9-9DF7-4A58-9112-EDE0434B4ACE';
Pero Hibernate siempre consulta todas las columnas de la entidad. Para evitarlo, hay que pasar por un proceso complejo.
Definir restricciones (Constraint) para una columna
Para definir restricciones sobre una columna, hay que poner varias anotaciones como estas.
...
@NotNull
@NotEmpty
@Email
private String email;
...
Este enfoque provoca los siguientes problemas.
- No se pueden hacer pruebas unitarias para estas condiciones.
- Para entender este proceso, ya es demasiado tarde si estás en medio de una operación de Flush.
- Las excepciones que pueden salir son genéricas e inútiles.
- Trata las reglas de negocio solo como reglas técnicas.
Problemas de estrategia
- Las actualizaciones del framework son pésimas, ignoran la compatibilidad hacia atrás y te fuerzan a depender de él sin alternativa. La empresa que lo crea termina viendo como natural que uses su propio framework para mantener el control. Hay que romper ese círculo vicioso.
- Obtener una prueba de concepto (Proof of Concept) únicamente a través de un framework solo termina estrechando tu visión, y en el caso de JPA/Hibernate eso es todavía peor. No debería tolerarse ni un poco.
¿Qué hacer entonces?
Usa SQL
Con SQL se puede hacer todo. Todos los programadores lo conocen, las consultas son intuitivas y no hace falta ningún framework.
¿Y si el gerente dice que hay que usar Hibernate?
Renuncia, o trabaja en separar tu código del framework.
Si ya lo estás usando...
- No hagas
publicel constructor por defecto ni los setters. - Usa cadenas como UUID en lugar de IDs generados por SQL.
- Usa el nombre
XXXDaoen vez deXXXRepository. - No uses la anotación
@SequenceGenerator. - Separa las clases de dominio y las clases DAO con
interface. - No uses relaciones uno a muchos (
@OneToMany, etc.); mejor evita el mapeo de entidades.
Conclusión
JPA/Hibernate: déjenlo.
- Hay documentación más corta y mejor para resolver problemas de negocio.
- No se aferren a un diseño solo por ir demasiado rápido.
- Tengan consideración con el próximo desarrollador que tendrá que mantener su código.
¿Qué usan ustedes?
- JPA/Hibernate
- Otra tecnología ORM
22 comentarios
No estoy de acuerdo con la opinión de que hay que abandonar JPA/Hibernate.
"La documentación oficial es muy larga"
SQL también es difícil cuando se aprende por primera vez. ¿Es fácil entender perfectamente joins complejos, subconsultas y funciones de procedimientos?
Con JPA basta con entender los conceptos clave al principio para empezar. Los temas más profundos se pueden buscar cuando haga falta.
Y además están los LLM.
"El problema de la mutabilidad y Reflection"
Esa preocupación surge por no entender cómo funciona el framework.
En la práctica, casi nunca se presentan problemas reales por esto.
Al contrario, gracias a Reflection el mapeo de objetos se automatiza y la productividad mejora mucho.
"Lazy loading y caché"
¿@Lazy es "la peor tecnología"? Es una función muy útil para resolver el problema de N+1 y optimizar el rendimiento.
El mecanismo de caché, de hecho, ayuda bastante a mejorar el rendimiento.
"Obtener solo ciertas columnas de una tabla"
Si usas JPQL o Projection, puedes consultar fácilmente solo las columnas que necesitas.
Y se puede usar junto con QueryDSL.
Creo que el objetivo de un ORM no es reemplazar completamente SQL, sino ayudar a que el desarrollador pueda concentrarse más en la lógica de negocio..
Soy pesimista con respecto al ORM, pero me parece que no logró presentar suficientes alternativas.
Si te vas muy fuerte por el lado de ORM, de verdad no tiene fin, y como se menciona arriba, hasta podrías terminar ahogándote y secándote vivo dentro de una documentación de alcance aún más amplio que la de SQL.
Últimamente estoy desarrollando un proyecto personal sin usar ORM, pero al intentar hacer un diseño pensando en la reutilización, a veces termino desarrollando en una dirección que me hace sentir que igual estoy creando un ORM, jaja.
Parece que siempre se ignora que, al usar un framework, puedes compartir un paradigma común con otros desarrolladores que también usan comercialmente ese mismo framework, en este tipo de textos que dicen que no deberías usar algo así.
Cuando hay muchas tablas y muchas columnas (por ejemplo, 50 tablas y más de 100 columnas por tabla), si usas SQL directamente, de verdad se vuelve un infierno.
Aun así, creo que usar JPA/Hibernate para hacer un servicio pequeño es un desperdicio enorme.
Al final, parece que este tipo de opiniones dependen mucho del caso.
(Además, el ejemplo que pusieron aquí también es de columnas de 3 o 4 campos...)
Creo que la última pregunta del texto de arriba debería ajustarse un poco.
En el ecosistema de Java, se podría organizar como 1. ORM vs 2. No ORM.
Tanto 1 como 2 tienen ventajas y desventajas muy claras, así que no es adecuado llegar a una conclusión tan extrema como en el texto de arriba.
En nuestro caso,
usamos ORM con JPA/Hibernate/QueryDSL y al mismo tiempo también usamos MyBatis.
Aprovechamos el ORM para maximizar la productividad,
y para las consultas que son difíciles de cubrir con ORM usamos MyBatis.
Y, elijas 1 o 2 de arriba, de todos modos hay que conocer bien SQL.
Yo también quisiera editarlo, pero el sitio no tiene una función para eso...
Creo que están ignorando por qué el ORM se volvió popular en primer lugar.
Aunque requiere algo de curva de aprendizaje, una vez que te acostumbras, claramente mejora la productividad.
SQL parece sencillo, pero el cansancio de ir escribiendo SQL consulta por consulta... además, cuando cambia una tabla, también hay que ir modificando una por una todas las consultas relacionadas, así que el mantenimiento de SQL tampoco es nada fácil; como es algo pequeño y simple, la cantidad de trabajo aumenta (por eso siempre termina apareciendo el tema de la productividad).
Además, los errores que ocurren en SQL explotan en tiempo de ejecución y son difíciles de detectar, y si te pones a implementar una por una las defensas contra ataques como SQL injection, al final terminas agregando código que genera consultas (normalmente empezando con una forma simple de plantilla...), y si sigues por ese camino, al final vuelve a aparecer algo parecido a un ORM; si va a ser así, ¿no sería mejor simplemente usar un ORM?
Me hizo acordar a un post que subieron hace unos días
https://es.news.hada.io/topic?id=17955
Estoy de acuerdo.
Creo que muchas veces no se entiende del todo por qué se usa un ORM ni cuáles son sus ventajas.
Además, parece que no hay mucha gente que intente analizar o entender el SQL que realmente se ejecuta a través del ORM.
Ojalá se difunda más que, más allá de la simple comodidad, también puede ayudar a comprender a fondo la optimización de SQL y el funcionamiento de la base de datos.
Creo que no hace falta irse a ninguno de los dos extremos de “hay que usarlo” o “no hay que usarlo” jaja;;
Yo uso un ORM cuando necesito productividad,
y para consultas complejas que no se pueden cubrir con ORM, o consultas que necesitan más optimización, las manejo con consultas directas.
Pienso que lo de usar ORM o
raw queryse puede elegir adecuadamente según la situación y según qué se vaya a construir y cómo.En general, creo que eso puede aplicar a datos con una buena normalización en la base de datos y que no requieren hacer joins grandes,
pero si no se puede gestionar correctamente todo, empezando por la normalización de la base de datos, a través de un DBA, entonces también creo que un ORM puede ser una buena opción. En especial, las ventajas que surgen al traer datos mediante relaciones del lado que los obtiene a través de joins me parecen un muy buen ejemplo de por qué se termina usando un ORM.
Por supuesto, estoy de acuerdo con la opinión de que los frameworks limitan el crecimiento del desarrollador y de que habría que reducir la dependencia de ellos, pero
no me resulta fácil estar de acuerdo con la postura de que simplemente no se debería usar ORM bajo ninguna circunstancia.
Siento que parte de la premisa de que todas las empresas tienen un DBA y que desarrollan usando metodologías bien establecidas como DDD o TDD,
y si en la práctica real se hiciera así, no sé qué tanto más caótico podría terminar siendo el código.
Cuando hago backends con PYTHON, casi siempre uso algo como SQLALCHEMY o DJANGO ORM.
Como ya me acostumbré tanto a escribir SQL directo como a usar un ORM, no sentía que hubiera mucha diferencia y no le daba muchas vueltas. Pero resulta que también había opiniones a favor de no usar ORM.
Estoy de acuerdo con la idea de reducir la dependencia del framework. No estaría bien saber usar solo DJANGO ORM y no saber manejar SQL...
Mmm, no lo sé, yo estoy en desacuerdo. Actualmente opero un servicio que tiene alrededor de 3 mil tablas y, como el dominio es tan complejo, para crear una sola consulta básicamente tenía que escribir varias decenas de líneas. Si además piensas en las consultas dinámicas, de verdad te explota la cabeza. Como era complejo, también aparecían muchos bugs y el mantenimiento era difícil. Yo creo que en dominios complejos un ORM es más ventajoso.
En mi caso, tengo experiencia manteniendo una base de datos que no estaba normalizada.
En ese momento escribíamos las consultas dinámicas sin usar un ORM, con SQL común,
y en consecuencia hubo casos en los que el código terminó siendo más difícil de entender.
Creo que no solo en dominios complejos, sino también en dominios con poca normalización, hay suficiente margen como para considerar su adopción.
Oh, entonces no habría por qué verlo de forma negativa.
Personalmente, yo también recomendaría usar SQL directamente. En el mundo de JS se usan mucho herramientas como Prisma, pero SQL tampoco es un lenguaje tan difícil, y me da algo de reparo porque parece exigir demasiada abstracción innecesaria para la E/S de base de datos.
Supongo que también se debe a que en el lado de js/ts hay muchísimos ORM particularmente mal hechos.
¿Con algo como Jdbc bastaría, no? Me hace pensar en que alguien con quien trabajé antes me dijo: "JPA es lento, así que usa otra cosa".
Parece una historia de leyenda.
Parece que la tendencia es dejar de lado los frameworks y volver a lo fundamental.
HTMX, SQL, etc.
Aunque tiene la desventaja de que hay que reinventar la rueda.
Sí tiene algunas ventajas también..
2. MyBatis (aunque no es un ORM, jaja)
Debí haber cambiado a DAO en lugar de usar ORM.