Ejemplo: Padre/Hijo Una de las primerísimas cosas que los usuarios nuevos intentan hacer con Hibernate es modelar una relación de tipo padre / hijo. Para esto hay dos enfoques diferentes. Por varias razones, el enfoque más conveniente, especialmente para usuarios nuevos, es modelar tanto Parent como Child como clases de entidad con una asociación <one-to-many> desde Parent a Child. (El enfoque alternativo es declarar el Child como un <composite-element>.) Ahora, resulta que la semántica por defecto de una asociación uno a muchos (en Hibernate) es mucho menos cercana a la semántica usual de una relación padre / hijo que aquellas de un mapeo de elementos compuestos. Explicaremos cómo usar una asociación uno a muchos bidireccional con tratamiento en cascada para modelar una relación padre / hijo eficiente y elegantemente. ¡No es para nada difícil! Una nota sobre las colecciones Se considera que las colecciones de Hibernate son una parte lógica de la entidad que las posee; nunca de las entidades contenidas. ¡Esta es una distinción crucial! Esto tiene las siguientes consecuencias: Cuando se quita / añade un objeto desde / a una colección, se incrementa el número de versión del dueño de la colección. Si un objeto que fue quitado de una colección es una instancia de un tipo de valor (por ejemplo, un elemento compuesto), ese objeta cesará de ser persistente y su estado será completamente quitado de la base de datos. Asimismo, añadir una instancia de tipo de valor a la colección causará que su estado sea inmediatamente persistente. Por otro lado, si se quita una entidad de una colección (una asociación uno-a-muchos o muchos-a-muchos), no será borrado, por defecto. Este comportamiento es completamente consistente. ¡Un cambio en el estado interno de otra entidad no hace desaparecer la entidad asociada! Asimismo, añadir una entidad a una colección no causa que la entidad se vuelva persistente, por defecto. En cambio, el comportamiento por defecto es que al añadir una entidad a una colección se crea meramente un enlace entre las dos entidades, mientras que al quitarla se quita el enlace. Esto es muy apropiado para todos los tipos de casos. Donde no es para nada apropiado es en el caso de una relación padre / hijo. donde la vida del hijo está ligada al ciclo de vida del padre. Uno-a-muchos bidirectional Supón que empezamos con una asociación simple <one-to-many> desde Parent a Child. ]]> Si ejecutásemos el siguiente código Hibernate publicaría dos sentencias SQL: un INSERT para crear el registro de c un UPDATE para crear el enlace desde p a c Esto no es sólo ineficiente, sino que además viola cualquier restricción NOT NULL en la columna parent_id. Podemos reparar la violación de restricción de nulabilidad especificando not-null="true" en el mapeo de la colección: ]]> Sin embargo, esta no es la solución recomendada. El caso subyacente de este comportamiento es que el enlace (la clave foránea parent_id) de p a c no es considerado parte del estado del objeto Child y por lo tanto no es creada en el INSERT. De modo que la solución es hacer el enlace parte del mapeo del Child. ]]> (Necesitamos además añadir la propiedad parent a la clase Child.) Ahora que la entidad Child está gestionando el estado del enlace, le decimos a la colección que no actualice el enlace. Usamos el atributo inverse. ]]> El siguiente código podría ser usado para añadir un nuevo Child Y ahora, ¡Sólo se publicaría un INSERT de SQL! Para ajustar un poco más las cosas, podríamos crear un método addChild() en Parent. Ahora, el código para añadir un Child se ve así Ciclo de vida en cascada La llamada explícita a save() es aún molesta. Apuntaremos a esto usando tratamientos en cascada. ]]> Esto simplifica el código anterior a Similarmente, no necesitamos iterar los hijos al salvar o borrar un Parent. Lo siguiente quita p y todos sus hijos de la base de datos. Sin embargo, este código no quitará c de la base de datos; sólo quitará el enlace a p (y causará una violación a una restricción NOT NULL). Necesitas borrar el hijo explícitamente llamando a delete(). Ahora, en nuestro caso, un Child no puede existir realmente sin su padre. De modo que si quitamos un Child de la colección, realmente queremos que sea borrado. Para esto, debemos usar cascade="all-delete-orphan". ]]> Nota: aunque el mapeo de la colección especifique inverse="true", el tratamiento en cascada se procesa aún al iterar los elementos de colección. De modo que si requieres que un objeto sea salvado, borrado o actualizado en cascada, debes añadirlo a la colección. No es suficiente con simplemente llamar a setParent(). Tratamiento en cascada y <literal>unsaved-value</literal> Supón que hemos cargado un Parent en una Session, hemos hecho algunos cambios en una acción de UI y deseamos hacer persistentes estos cambios en una nueva sesión llamando a update(). El Parent contendrá una colección de hijos y, ya que está habilitado el tratamiento en cascada, Hibernate necesita saber qué hijos están recién instanciados y cuáles representan filas existentes en la base de datos. Asumamos que tanto Parent como Child tienen propiedades identificadoras generadas de tipo Long. Hibernate usará el identificador y el valor de la propiedad de versión/timestamp para determinar cuáles de los hijos son nuevos. (Ver .) En Hibernate3, no es más necesario especificar un unsaved-value explícitamente. The following code will update parent and child and insert newChild. Bueno, todo eso está muy bien para el caso de un identificador generado, pero ¿qué de los identificadores asignados y de los identificadores compuestos? Esto es más difícil, ya que Hibernate no puede usar la propiedad identificadora para distinguir entre un objeto recién instanciado (con un identificador asignado por el usuario) y un objeto cargado en una sesión previa. En este caso, Hibernate bien usará la propiedad de versión o timestamp, o bien consultará realmente el caché de segundo nivel, o bien, en el peor de los casos, la base de datos, para ver si existe la fila. Conclusión Hay que resumir un poco aquí y podría parecer confuso a la primera vez. Sin embargo, en la práctica, todo funciona muy agradablemente. La mayoría de las aplicaciones de Hibernate usan el patrón padre / hijo en muchos sitios. Hemos mencionado una alternativa en el primer párrafo. Ninguno de los temas anteriores existe en el caso de los mapeos <composite-element>, que tienen exactamente la semántica de una relación padre / hijo. Desafortunadamente, hay dos grandes limitaciones para las clases de elementos compuestos: los elementos compuestos no pueden poseer sus propias colecciones, y no deben ser el hijo de cualquier otra entidad que no sea su padre único.