26 de marzo de 2020

ORM por XML con relaciones @OneToMany

Los dos casos vistos hasta ahora son entidades que sólo tienen campos básicos de tipo String, pero nuestros objetos de negocio muchas veces tienen relaciones con otros objetos con un campo de su tipo o incluso contienen una lista de ellos.

En esta entrada nos vamos a centrar en objetos que tengan una lista de entidades lo que forma una relación "Uno a Muchos" que en JPA se conoce con la anotación @OneToMany o la etiqueta one-to-many (y viceversa @ManyToOne).

La relación con @OneToMany puede hacerse con una Join Table o con una clave ajena en la tabla de entidades del tipo contenido en la lista (Join Column). En esta entrada se va a ver esto último.

Para verlo voy a usar la clase Partido de datos-deportivos, ya que tiene una Collection<Suceso> que hereda de EventoImpl.

Lo primero que tenemos que hacer es implementar un código necesario previo para centrarnos después en el objeto de esta entrada. El código está en el commit previo a empezar con la definición de estas relaciones y contiene:
  1. Creación de sendas clases entidad para Partido (PartidoConId) y Suceso (SucesoConId) ya que estos tipos no tienen ningún campo que pueda usarse como Id y todas las entidades lo necesitan.
  2. En este caso los Ids serán autogenerados por la base de datos con @GeneratedValue e IDENTITY.
  3. Como todos los tipos de los que heredan Partido y Suceso son de una librería de terceros tengo que usar la configuración XML porque no puedo anotar los campos.
  4. Una vez creados los ficheros ORM hay que añadirlos a la configuración.
Estos puntos es lo que hemos visto en las entradas anteriores y veremos que se parece bastante. Viendo el vídeo hay una explicación de este código.

Usando nuestros conocimientos de modelado de datos vamos a representar la relación PartidoConId-SucesoConId añadiendo a la tabla de Sucesos la clave ajena (FK) de PartidoConId al que pertenece ese Suceso.

Para ello definimos con la etiqueta one-to-many el nombre de nuestro campo con la colección de Sucesos, el tipo de entidad destino (SucesoConId) y con qué campo se mapea (con partido - veremos qué significa esto más abajo). Usamos la siguiente etiqueta dentro de los atributos de EventoImpl:
<one-to-many name="sucesos" target-entity="es.lanyu.eventos.repositorios.SucesoConId" mapped-by="partido"/>
NOTA: Es importante destacar que el tipo objetivo debe ser una entidad, no vale con la interface Suceso, debe ser la entidad SucesoConId.

Al hacerlo de esta forma (usando una FK en una Join Column) la relación debe ser bidireccional: se puede navegar de Partido a Suceso y viceversa. Esto implica que nuestra clase SucesoConId tiene que añadir un campo partido para saber cómo se mapea esa FK. Este campo debe tener el nombre que hemos puesto en mapped-by="partido" y ser del tipo de la entidad que contiene la colección (PartidoConId).
PartidoConId partido;

public PartidoConId getPartido() {
 return partido;
}

public void setPartido(PartidoConId partido) {
 this.partido = partido;
}
Además se deben añadir los respectivos accesores (getter/setter) para este campo. Esto se debe a que hay que mantener sincronizadas las dos entidades cuando se modifique (añadir en PartidoConId otro Suceso):
public void addSucesoConId(SucesoConId suceso) {
    super.addSuceso(suceso);
    suceso.setPartido(this);
}
Por último, debemos añadir en nuestro ORM para SucesoConId la etiqueta many-to-one para cerrar la relación en los dos sentidos e indicar la columna que se usa para hacer el join:
<many-to-one name="partido" optional="false"><!-- fetch="LAZY">-->
    <join-column name="ID_PARTIDO" referencedColumnName="ID"/>
</many-to-one>
NOTA: Dejo comentado el atributo fetch="LAZY" para ver cómo se pondría si se quisiera que la relación no se traiga los datos hasta que no se necesiten.

En el código hasta aquí se puede encontrar comentadas las anotaciones que corresponden a esta configuración XML en SucesoConId. Sin embargo, podemos ver que seríamos incapaces de añadírselo a EventoImpl al no ser código que gestionemos nosotros.

Todo esto puede verse funcionando en el video anterior con un main modificado que irá insertando partidos con un suceso y lo imprime para ver el resultado.

Compárteme

Entradas populares