leanmind logo leanmind text logo

Blog

TDD Avanzado

Las herramientas que se necesitan para aplicar TDD en el mundo real en cualquier proyecto.

Conoce las magias de Hibernate antes de usarlo

Por Jorge Aguiar Martín

Desde que ví las ventajas de usar un ORM me encantó la idea, pero el tiempo y las malas experiencias, poco a poco me hacen cambiar de opinión. Veamos por qué 🤣

Los ORM como todos sabemos, hacen muchas magias negras sin que nos demos cuenta, y curiosamente esas magias negras que antes me gustaban porque no tenía que hacerlas yo, cada vez me gustan menos. Últimamente, hemos estado trabajando con Hibernate, y claro, cuanto más trabajas con la librería, más cosas descubres.

💡 Para trabajar correctamente con la herramienta, es necesario conocerla en profundidad y no confiar en los automatismos y convenciones de la misma - Adrián Ferrera

En Hibernate, una instancia de una entidad de base de datos puede tener 3 estados distintos:

Una vez descrito esto, vamos a lo que puede ser un problema. Imaginémonos que en un momento dado, tenemos como entrada dos entidades que queremos modificar. Como estamos haciendo uso del mismo modelo de base de datos, al hacer un get de estos objetos e instanciarlos, tendríamos dichas instancias en el estado Persistent. Una vez modificadas las instancias, podremos hacer efectivo el cambio en base de datos por medio del método entity.save().

Hasta ahora no debería haber ningún problema, sin embargo, si en el método especificamos la opción de flush a true, pasará un comportamiento que bien es el que describe Hibernate, pero a ojos de la persona que desarrolla, no es precisamente algo esperado. Veamos que dice hibernate sobre el flush, y lo que implica.

Flushing is the process of synchronizing the state of the persistence context with the underlying database.

Esto viene a decir que, todo aquello que esté en el estado de persistence, se insertará a la base de datos en el momento en el que se haga flush, en vez de esperar a que el ORM decida cuando se debe hacer dicho cambio.

Esto suena bien, decidir en qué momento se hacen efectivos los cambios en base de datos, salvo que el save no solo afectará a la entidad a la que hayamos hecho el save(). Veamos un ejemplo:

  User user1 = User.get(id1)
  user1.name = "modificado1"

  user2.name = "modificado2"
  User user2 = User.get(id2)

  user1.save(flush: true)

Cabría esperar que tras haber hecho el save del user1, el flush sólo afectara al user1. Pues curiosamente no es así, como hemos hecho el flush, éste hace efectivos todos los cambios que se hayan hecho a todas las entidades.

Obviamente, esto no pasaría si no se usaran las entidades de base de datos en otros sitios, ni se usara el flush como opción en el save(). Entonces, como formas de evitar este comportamiento poco intuitivo (aunque técnicamente esperado según la documentación), podemos separar las entidades de dominio (aquellas que van a tener los cambios que realicemos a base de operaciones), de las de base de datos, para que sólo aquellas que realmente queramos que se modifiquen, lo hagan, y no por culpa de efectos colaterales poco intuitivos.

Publicado el 01/02/2024 por
Jorge image

Jorge Aguiar Martín

¿Quieres más? te invitamos a suscribirte a nuestro boletín para avisarte cada vez que recopilemos contenido de calidad que compartir.

Si disfrutas leyendo nuestro blog, ¿imaginas lo divertido que sería trabajar con nosotros? ¿te gustaría?

Impulsamos el crecimiento profesional de tu equipo de developers