— PEDRO DELGADO YARZA 2014/01/29 13:50
Cuando nuestras tablas contienen una clave primara formada por varias columnas, necesitamos realizar un mapeo especial en nuestros bean de entidad para que sea reconocida por el motor de persistencia. Para ello necesitamos crear una nueva clase que contenga todos los campos que componen dicha clave primaria.
Esta clase solo puede contener tipos primitivos de Java, sustituye al valor de la propiedad marcada con @Id y aparecerá anotada en nuestro bean con la anotación @EmbeddedId.
Junto a está anotación, utilizaremos @AttributeOverrides que permite indicar un conjunto de propiedades que se van a contener en la clase que contiene la clave primaria. Dentro de @AttributeOverrides, utilizaremos la anotación @AttributeOverride para identificar las propiedades que se contendran en dicha clase. @AttributeOverride tiene varias propiedades:
Es imprescindible que tanto en la clase que representa la clave, como en la clase que representa la entidad, se implementen los métodos equals y hashCode para asegurar que las búsquedas Java devuelven correctamente los objetos que buscamos y se hagan correctamente las comparaciones.
@Entity public class Entidad { private EntidadId id; ... public Entidad() { } public Entidad(EntidadId id) { this.id = id; } // Getters y Setters @EmbeddedId @AttributeOverrides({ @AttributeOverride(name="id", column=@Column(name="ID")), @AttributeOverride(name="nombre", column=@Column(name="NOMBRE")) }) public EntidadId getId() { return this.id; } public void setId(EntidadId id) { this.id = id; } public boolean equals(Object o) { .... .... } public int hashCode() { .... .... } } @Embeddable public class EntidadId { int id; String nombre; public EntidadId() { } public EntidadId(int id, String nombre) { this.id = id; this.nonmbre = nombre; } public boolean equals(Object o) { .... .... } public int hashCode() { .... .... } }
Hay casos en los que existen relaciones entre entidades donde la clave primaria compuesta de una de las entidades incluye una referencia al objeto de la otra. Como en las claves primarias compuestas sólo pueden aparecer tipos primitivos tendremos que tratar este caso de forma particular. Veámoslo con un ejemplo:
Imaginemos que tenemos una entidad grupo relacionada con N proyectos, y que la clave primaria del proyecto incluye el identificador del grupo. La entidad grupo
@Entity @Table(name = "GRUPOS_CL") public class GruposCl implements java.io.Serializable { private String codGrupo; private String desGrupo; private Set<ProyectosCl> proyectosCls = new HashSet<ProyectosCl>(0); ... @Id @Column(name = "COD_GRUPO", unique = true, nullable = false, length = 3) @NotNull @Length(max = 3) public String getCodGrupo() { return this.codGrupo; }
La clase de la clave primaria compuesta En esta clase incluimos los identificadores del proyecto y del grupo, no las referencias a los objetos de la clase.
@Embeddable public class ProyectosClId implements java.io.Serializable { private String codGrupo; private String codProyecto; ...
La entidad Proyectos Contiene la referencia a la clase que es la clave primaria y además contiene una referencia a la clase GruposCl, especificacndo el tipo de relación que hay entre las entidades.
@Entity @Table(name = "PROYECTOS_CL") public class ProyectosCl implements java.io.Serializable { private ProyectosClId id; private GruposCl gruposCl; private String desProyecto; ... @EmbeddedId @AttributeOverrides({ @AttributeOverride(name = "codGrupo", column = @Column(name = "COD_GRUPO", nullable = false, length = 3)), @AttributeOverride(name = "codProyecto", column = @Column(name = "COD_PROYECTO", nullable = false, length = 3))}) @NotNull public ProyectosClId getId() { return this.id; } ... @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "COD_GRUPO", nullable = false, insertable = false, updatable = false) @NotNull public GruposCl getGruposCl() { return this.gruposCl; } public void setGruposCl(GruposCl gruposCl) { this.gruposCl = gruposCl; }