Qué anotación debo usar: @IdClass o @EmbeddedId

128

La JPAespecificación (Java Persistence API) tiene 2 formas diferentes de especificar claves compuestas de entidad: @IdClassy @EmbeddedId.

Estoy usando ambas anotaciones en mis entidades mapeadas, pero resulta ser un gran desastre para las personas que no están muy familiarizadas JPA.

Quiero adoptar solo una forma de especificar claves compuestas. ¿Cuál es realmente el mejor? ¿Por qué?

sakana
fuente

Respuestas:

86

Considero que @EmbeddedIdprobablemente sea más detallado porque con @IdClassusted no puede acceder a todo el objeto de clave primaria utilizando ningún operador de acceso de campo. Usando el @EmbeddedIdpuede hacer así:

@Embeddable class EmployeeId { name, dataOfBirth }
@Entity class Employee {
  @EmbeddedId EmployeeId employeeId;
  ...
}

Esto proporciona una noción clara de los campos que forman la clave compuesta porque todos se agregan en una clase a la que se accede a través de un operador de acceso de campo.

Otra diferencia con @IdClassy @EmbeddedIdes cuando se trata de escribir HQL:

Contigo @IdClassescribes:

seleccione e.name de Employee e

y @EmbeddedIdcontigo tienes que escribir:

seleccione e.employeeId.name de Employee e

Tienes que escribir más texto para la misma consulta. Algunos pueden argumentar que esto difiere de un lenguaje más natural como el promovido por IdClass. Pero la mayoría de las veces, comprender desde la consulta que un campo dado es parte de la clave compuesta es de gran ayuda.

Jorge Ferreira
fuente
10
Aunque estoy de acuerdo con la explicación dada anteriormente, también me gustaría agregar un caso de uso único @IdClass, aunque lo prefiero @EmbeddedIden la mayoría de las situaciones (Tengo que saber esto de una sesión de Antonio Goncalves. Lo que sugirió es que podríamos usar el @IdClassen caso de que el compuesto No se puede acceder a la clase de clave o viene de otro módulo o código heredado donde no podemos agregar una anotación. En esos escenarios @IdClassnos dará una forma de hacerlo . ”
Gaurav Rawat
1
Creo que es posible que los casos para usar la @IdClassanotación dada por @Gaurav sea la razón por la cual la especificación JPA enumera ambos métodos para crear una clave compuesta ... @IdClassy@EmbeddidId
kapad
20

Hay tres estrategias para usar una clave primaria compuesta:

  • Márquelo como @Embeddabley agregue a su clase de entidad una propiedad normal, marcada con @Id.
  • Agregue a su clase de entidad una propiedad normal, marcada con @EmbeddedId.
  • Agregue propiedades a su clase de entidad para todos sus campos, márquelas con @Id, y marque su clase de entidad con @IdClass, proporcionando la clase de su clase de clave primaria.

El uso de @Idcon una clase marcada como @Embeddablees el enfoque más natural. La @Embeddableetiqueta se puede utilizar para valores incrustables de clave no primaria de todos modos. Le permite tratar la clave primaria compuesta como una sola propiedad, y permite la reutilización de la @Embeddableclase en otras tablas.

El siguiente enfoque más natural es el uso de la @EmbeddedIdetiqueta. Aquí, la clase de clave primaria no se puede usar en otras tablas ya que no es una @Embeddableentidad, pero nos permite tratar la clave como un atributo único de alguna clase.

Finalmente, el uso de las anotaciones @IdClassy @Idnos permite mapear la clase de clave primaria compuesta usando las propiedades de la propia entidad correspondiente a los nombres de las propiedades en la clase de clave primaria. Los nombres deben corresponder (no existe un mecanismo para anular esto), y la clase de clave primaria debe cumplir con las mismas obligaciones que con las otras dos técnicas. La única ventaja de este enfoque es su capacidad de "ocultar" el uso de la clase de clave primaria de la interfaz de la entidad que lo encierra. La @IdClassanotación toma un parámetro de valor de Tipo de clase, que debe ser la clase que se utilizará como clave primaria compuesta. Todos los campos que corresponden a las propiedades de la clase de clave primaria que se utilizarán deben estar anotados con @Id.

Referencia: http://www.apress.com/us/book/9781430228509

Adelin
fuente
17

Descubrí una instancia en la que tenía que usar EmbeddedId en lugar de IdClass. En este escenario, hay una tabla de unión que tiene columnas adicionales definidas. Intenté resolver este problema usando IdClass para representar la clave de una entidad que representa explícitamente filas en la tabla de unión. No pude hacerlo funcionar de esta manera. Afortunadamente, "Java Persistence With Hibernate" tiene una sección dedicada a este tema. Una solución propuesta era muy similar a la mía, pero en su lugar utilizaba EmbeddedId. Modelé mis objetos según los del libro, ahora se comporta correctamente.

perezoso
fuente
13

Hasta donde sé, si su PK compuesto contiene FK, es más fácil y sencillo de usar @IdClass

Con @EmbeddedIdusted debe definir el mapeo para su columna FK dos veces, @Embeddedableuna vez y una vez, es decir, @ManyToOnedónde @ManyToOnedebe ser de solo lectura ( @PrimaryKeyJoinColumn) porque no puede tener una columna configurada en dos variables (posibles conflictos).
Por lo tanto, debe configurar su FK con un tipo simple @Embeddedable.

En el otro sitio, el uso de @IdClassesta situación se puede manejar mucho más fácilmente, como se muestra en las claves principales a través de las relaciones OneToOne y ManyToOne :

Ejemplo de anotación de identificación ManyToOne JPA 2.0

...
@Entity
@IdClass(PhonePK.class)
public class Phone {

    @Id
    private String type;

    @ManyToOne
    @Id
    @JoinColumn(name="OWNER_ID", referencedColumnName="EMP_ID")
    private Employee owner;
    ...
}

Ejemplo de clase de id JPA 2.0

...
public class PhonePK {
    private String type;
    private long owner;

    public PhonePK() {}

    public PhonePK(String type, long owner) {
        this.type = type;
        this.owner = owner;
    }

    public boolean equals(Object object) {
        if (object instanceof PhonePK) {
            PhonePK pk = (PhonePK)object;
            return type.equals(pk.type) && owner == pk.owner;
        } else {
            return false;
        }
    }

    public int hashCode() {
        return type.hashCode() + owner;
    }
}
Ondrej Bozek
fuente
1
Simplemente no olvides agregar captadores a tu clase de PK
Sonata
@Sonata ¿por qué necesitamos los captadores? Lo intenté sin getters / setters y funciona bien
xagaffar
Gracias por el ejemplo de id class! Aunque terminé necesitando implementar Serializable también. También podría agregar getters y setters, especialmente si su IDE puede generarlos automáticamente.
Starwarswii
8

Creo que la principal ventaja es que podríamos usar @GeneratedValuela identificación al usar el @IdClass? Estoy seguro de que no podemos utilizar @GeneratedValuepara @EmbeddedId.

bertie
fuente
1
¿No es posible usar @GeneratedValue en Embeddedid?
Kayser
1
Lo he usado con éxito con EmbeddedId pero aparentemente su soporte varía según la base de datos. Esto también es cierto sobre su uso con IdClass. La especificación dice: "La anotación GeneratedValue solo se puede usar de forma portátil para claves primarias simples (es decir, no compuestas)".
BPS
4

La clave compuesta no debe tener una @Idpropiedad cuando @EmbeddedIdse usa.

BK
fuente
1

Con EmbeddedId puede usar la cláusula IN en HQL, por ejemplo: FROM Entity WHERE id IN :idsdonde id es un EmbeddedId mientras que es difícil lograr el mismo resultado con IdClass, querrá hacer algo comoFROM Entity WHERE idPartA = :idPartA0 AND idPartB = :idPartB0 .... OR idPartA = :idPartAN AND idPartB = :idPartBN

Aleks Ben Maza
fuente