martes, 12 de marzo de 2013

OneToMany en JPA

Hay varías formas de realizar las relaciones 'uno a muchos' a través de JPA, aquí te mostraremos dos de las opciones.

- Relación bidireccional.

Es aquella relación en la que ambos objetos tienen una referencia del otro objeto. Esta opción es la que genera por defecto cuando por ejemplo usas hibernate tools y en principio es la más sencilla de crear.

En la clase Padre tenemos una referencia al conjunto de hijos que tiene. Un Padre puede tener de 0 a N hijos.
@Entity
@Table(name = "parent", catalog = "family")
public class Parent implements java.io.Serializable {

 private Integer ident;
 private String nombre;
 private Set childs= new HashSet(0);

 public Parent() {
 }

 @Id
 @GeneratedValue(strategy = IDENTITY)
 @Column(name = "IDENT", unique = true, nullable = false)
 public Integer getIdent() {
  return this.ident;
 }

 public void setIdent(Integer ident) {
  this.ident = ident;
 }

 @Column(name = "NOMBRE", length = 45)
 public String getNombre() {
  return this.nombre;
 }

 public void setNombre(String nombre) {
  this.nombre = nombre;
 }

 @OneToMany(fetch = FetchType.LAZY, mappedBy = "parent")
 public Set getChilds() {
  return this.childs;
 }

 public void setChilds(Set childs) {
  this.childs= childs;
 }
}
La clase Hijo tiene referencia a un padre. Un hijo solo puede tener un único padre.
@Entity
@Table(name = "child", catalog = "family")
public class Child implements java.io.Serializable {

 private Integer ident;
 private Parent parent;
 private String nombre;

 public Cancion() {
 }

 @Id
 @GeneratedValue(strategy = IDENTITY)
 @Column(name = "IDENT", unique = true, nullable = false)
 public Integer getIdent() {
  return this.ident;
 }

 public void setIdent(Integer ident) {
  this.ident = ident;
 }

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "IDPARENT", nullable = false)
 public Parent getParent() {
  return this.parent;
 }

 public void setParent(Parent parent) {
  this.parent = parent;
 }

 @Column(name = "NOMBRE", length = 45)
 public String getNombre() {
  return this.nombre;
 }

 public void setNombre(String nombre) {
  this.nombre = nombre;
 }
}

- Pero hay otra forma de realizar la relación Uno a muchos y es hacerla de manera unidireccional. Es muy posible que se necesite saber que hijos tiene un padre pero no se suele usar tanto al revés. Por lo que vamos ha ver que cambios debemos realizar para esta otra forma. Atención esta segunda forma solo es valida a partir de JPA 2.0.

En la clase Padre seguimos teniendo una relación de los posibles hijos, pero en vez de utilizar el atributo 'mappedBy' de la anotación @OneToMany que indica con que otro objeto lo tenemos mapeado. Indicamos la anotación @JoinColumn que indica la relación existente en la BBDD. El resto sigue igual.

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "IDPARENT", referencedColumnName = "IDENT")
public Set getChilds() {
 return childs;
}

Y en la clase hijo quitamos toda referencia a la clase padre. Y ya estará realizada la unión de forma unidireccional.

7 comentarios:

  1. Muchas Gracias por el Post!
    Me ha ayudado a aclararme muchas cosas! :)

    Sólo que tengo otra duda (igual estaría bien que lo pongas para aquellas personas que somos nuevas en este mundillo xD). Qué es lo que dice las siguientes líneas?

    Clase PADRE
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "parent")

    Clase HIJA
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "IDPARENT", nullable = false)
    ¿Para qué es la anotación @JoinColumn?

    qué diferencia hay entre:

    Clase PADRE
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "parent")


    Clase PADRE
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "IDPARENT", referencedColumnName = "IDENT")

    ¿Puedo poner también lo del CASCADE en la primera opción?

    Muchas gracias!

    ResponderEliminar
    Respuestas
    1. @JoinColumn es una anotacion que se puede usar en ambos lados de la relación y permite indicar en Java el tipo de relación que tienen las tablas. En este caso Se indica en la clase Hija hay una columna denominada 'IDPARENT' y es la que esta relacionada con otra tabla, en este claso el Padre.

      Respecto a las diferencias de un ejmplo u otro en principio no son significativas, simplemente son opciones que se pueden indicar en la anotacion.

      Cascade hace referencia a los momentos en los que se aplica los cambios. Es decir si ponemos Cascade.REMOVE al borrar la entidad padre, borraremos los hijos. Si ponemos CASCADE.PERSIST al guardar la clase padre, guardaremos también la clase hija. Las distintas opciones son: PERSIST, REMOVE, REFRESH, MERGE, DETACH y ALL.

      Fetch hace referencia en cuando obtendremos los datos. LAZY no obtendrá verdaderamente los datos de los objetos 'hijos', solo los obtendrá cuando se haga un 'get' sobre la propiedad que los contiene. Cuidado con esto que puede llevar a problemas si la sesión ya se ha cerrado o problemas de objetos 'detached'. EAGER permite obtener los datos de los objetos 'hijo' a la vez que se obtiene los datos de la clase padre, no dará problema de sesión o objetos 'detached' pero es posible que traigamos demasiada información de BBDD y sean consultas pesadas.

      Eliminar
  2. Mil gracias!!!
    Estoy deseando que hagas otro post!

    ResponderEliminar
  3. Uy! olvidabe preguntarte xD
    cuando tú pones:
    mappedBy = "parent"
    ese "parente" es el nombre del atributo que tiene en la clase hijo? o es el nombre de @Table(name = "parent") en la clase padre?

    ResponderEliminar
    Respuestas
    1. Buenas.

      Cuando dice mappedBy="parent", 'parent' es la foreign key dentro de la tabla hijo que hace referencia a la tabla padre. Que en este caso se denomina igual que el nombre de la clase padre pero no tiene porque ser así.

      Eliminar
    2. Este comentario ha sido eliminado por el autor.

      Eliminar
    3. disculpa una pregunta entonses podriamos decir que parent hace referencia al nombre que se le de al atributo en la clase hija que hace referencia a la foreign key

      Eliminar