miércoles, 9 de junio de 2010

ManyToMany en Hibernate con anotaciones

Muy buenas a todos, ya se que me echabais de menos, así que he decicido volver a escribir por aquí. En esta ocasión os voy a contar como representar con Hiberante una tabla Many To Many, pues ha sido una de las cosas que más me han dado la lata en el nuevo curro.
Para entrar en situación voy a exponer un pequeño ejemplo con el que todos los labos y ex-labos estaremos familiarizados:
Supongamos que por una extraña razón a Geno se le juntan muchos marrones encima (cosa que no pasa casi nunca ;)) y por otro lado están (estabamos) todos los precarios, entonces le encarga a Rafa que los reparta de una forma sencilla. Rafa como está apuntito de casarse piensa que lo mejor es hacer una aplicación sencilla para que Geno la pueda utilizar y le deje escribir su tesis, que aunque él cree que la lleva bien aún le queda por escribir unas 100 páginas.
La aplicación será llamada Brown Dispatching y su modelo de datos generaría unas 3 tablas:
  • Una tabla para cada miembro del labo, donde podríamos incluir el nombre del precario, el nombre de la máquina, las horas que está o cualquier otra información que pudiese resultar útil para esta situación. En el caso del ejemplo, creo que con el nombre bastaría, además que somos pocos y nos conocemos perfectamente.
  • Otra tabla para cada marrón, indicando el nombre, una descripción y una fecha límite por poner unos campos de ejemplo.
  • La tercera tabla sería la que finalmente enlazaría a los marrones con los precarios. Por lo que tendría unicamente el identificador único de marrón y el identificador único de usuario.
Gracias a Hibernate, esta tercera tabla no habría que implementarla, pero si que deberíamos implementar las otras 2 entidades. A partir de la versión 1.5 existe una opción para meter anotaciones en el propio código del Bean y éste ha sido el método que he elegido yo para realizar el ejemplo.
imports ...
@Entity
@Table(name = "T_PRECARIOS")
public class Precario implements IGwtSerializableParameter {

 private Long id;
 private String name;
 private Set browns;

 // ID SETTER AND GETTER
 @Id
 @GeneratedValue(strategy=GenerationType.AUTO)
 @Column(name="ID")
 public Long getId() {
  return id;
 }
 public void setId(Long id) {
  this.id = id;
 }

 // NAME SETTER AND GETTER
 @Column(name = "NAME", nullable = false)
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }

 // BROWNS SETTER AND GETTER
 @ManyToMany(
   cascade = { CascadeType.MERGE,
      CascadeType.PERSIST },
   fetch = FetchType.EAGER
   )
 @JoinTable(
   name = "T_PRECARIOS_BROWNS",
   joinColumns = { @JoinColumn(name = "PRECARIO_ID") },
   inverseJoinColumns = { @JoinColumn(name = "BROWN_ID") }
   )
 public Set getBrowns() {
  return browns;
 }
 public void setBrowns(Set browns) {
  this.browns = browns;
 }
 ...
}
import ...
@Entity
@Table(name = "T_BROWNS")
public class Brown implements IGwtSerializableParameter {

 private Long id; // Unique identifier for brown
 private String name; // Brown's name
 private String description; // Brown's description
 private Date finalDate; // Brown's final date
 private Set precarios; // Set with the "precarios" associated

 // SETTER AND GETTER FOR ID
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 @Column(name = "ID")
 public Long getId() {
  return id;
 }
 public void setId(Long id) {
  this.id = id;
 }

 // SETTER AND GETTER FOR NAME
 @Column(name = "NAME", nullable = false)
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }

 // SETTER AND GETTER FOR DESCRIPTION
 @Column(name = "DESCRIPTION", nullable = false)
 public String getDescription() {
  return description;
 }
 public void setDescription(String description) {
  this.description = description;
 }

 // SETTER AND GETTER FOR FINAL DATE
 @Column(name = "FINAL_DATE", nullable = false)
 @Temporal(TemporalType.DATE)
 public Date getFinalDate() {
  return finalDate;
 }
 public void setFinalDate(Date finalDate) {
  this.finalDate = finalDate;
 }

 // SETTER AND GETTER FOR "PRECARIOS"
 @ManyToMany(
   mappedBy = "browns",
   cascade = { CascadeType.MERGE,
      CascadeType.PERSIST },
   fetch = FetchType.EAGER
   )
 public Set getPrecarios() {
  return precarios;
 }
 public void setPrecarios(Set precarios) {
  this.precarios = precarios;
 }
 ...
}
En modo de anotaciones de Hibernate, las notaciones se pueden poner en la definición del atributo o en el Getter del mismo, como se puede ver yo he elegido ponerlas en el getter.
Las anotaciones más importantes para el ejemplo que he traído son las asociadas a los Set (de java.util). En estas se representa que tipo de unión se quiere entre las entidades.
En la entidad Precario, se define por un lado el tipo de unión que se va hacer entre las tablas, con los atributos cascade y fetch en la anotación ManyToMany, mientras que en la anotación JoinTable se indica el nombre de la tabla resultante de hacer el join de las entidades y el nombre de las columnas. En la entidad Brown, se define simplemente el ManyToMany indicando que atributo de la otra clase modela está relación, y se indica el tipo de unión que se realiza.
Creo que esto es suficiente clase sobre Hibernate de momento, pero si alguien tiene alguna duda/sugerencia al respecto soy todo oídos. Por cierto, puede que haya alguna manera más eficiente para modelar esto, pero no se me ha ocurrido un ejemplo que modele mejor las necesidades de crear un ManyToMany, y también puede ser que haya forma mejor de realizar el ManyToMany dado que apenas llevo unos meses con Hibernate y no soy ni mucho menos un experto. Aqui solo he tratado de plasmar mi experiencia con esto. Un saludo!

2 comentarios:

  1. Muy bien traido el ejemplo... La verdad es que esto con Django es trivial :D Utilizaré el ejemplo para ver como hacer un ManyToMany con atributos en django, que tiene algo más de chicha.

    ResponderEliminar
  2. El ManyToMany con atributos en Hibernate, tiene también un poquito más de chicha, ya que hay que definir la clase intermedia, y para cada foreign key definir la relación con la entidad que lo representa.
    Se que en django con el models los apañas en un periquete, pero esto es java y tooodo es más "burocrático". La ventaja es la cantidad de documentación que encuentras para casi todas las cosas.

    Un saludo

    ResponderEliminar