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.
¿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?
Pero espera 🖐 que tenemos un conflicto interno. A nosotros las newsletter nos parecen 💩👎👹 Por eso hemos creado la LEAN LISTA, la primera lista zen, disfrutona y que suena a rock y reggaeton del sector de la programación. Todos hemos recibido newsletters por encima de nuestras posibilidades 😅 por eso este es el compromiso de la Lean Lista