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


La especificación JPA (Java Persistence API) tiene 2 formas diferentes de especificar claves compuestas de entidad: @IdClass y @EmbeddedId.

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

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

Author: Harshal Patil, 2008-10-17

7 answers

Considero que @EmbeddedId es probablemente más detallado porque con @IdClass no puede acceder a todo el objeto de clave primaria utilizando ningún operador de acceso a campos. Usando el @EmbeddedId puedes hacer así:

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

Esto da 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 a campos.

Otra diferencia con @IdClass y @EmbeddedId es cuando se trata de escribir HQL :

Con @IdClass usted escribe:

select e.name from Employee e

Y con @EmbeddedId tienes que escribir:

select e.employeeId.name from 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 entender directamente desde la consulta que un campo dado es parte de la clave compuesta es de una ayuda invaluable.

 72
Author: Jorge Ferreira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-01 11:35:41

Descubrí una instancia donde tuve 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 combinación. No podía hacer que funcionara así. 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 utilizaba EmbeddedId en su lugar. Me modelado mis objetos después de los del libro que ahora se comporta correctamente.

 16
Author: laz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-09 19:49:19

Hay tres estrategias para usar una clave primaria compuesta:

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

El uso de @Id con una clase marcada como @Embeddable es el enfoque más natural. La etiqueta @Embeddable se puede usar 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 clase @Embeddable en otras tablas.

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

Finalmente, el uso de las anotaciones @IdClass y @Id nos permite mapear la clase de clave primaria compuesta usando propiedades de la propia entidad correspondientes a los nombres de las propiedades en la clase de clave primaria. Los nombres deben corresponder (no hay ningún mecanismo para anular esto), y la clase de clave primaria debe cumplir 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 anotación @IdClass toma un parámetro de valor de tipo Clase, que debe ser la clase que se utilizará como clave primaria compuesta. Los campos que corresponden a las propiedades de la clase de clave primaria a utilizar deben estar anotados con @Id.

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

 11
Author: Adelin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-25 13:05:59

Por lo que sé si su compuesto PK contiene FK es más fácil y más sencillo de usar @IdClass

Con @EmbeddedId tiene que definir la asignación para su columna FK dos veces, una en @Embeddedable y una para como, es decir, @ManyToOne donde @ManyToOne tiene que ser de solo lectura(@PrimaryKeyJoinColumn) porque no puede tener una columna establecida en dos variables (posibles conflictos).
Así que tienes que configurar tu FK usando simple type en @Embeddedable.

En el otro sitio usando @IdClass esta situación se puede manejar mucho más fácil como se muestra en Claves primarias a través de Relaciones OneToOne y ManyToOne :

Ejemplo JPA 2.0 ManyToOne id anotación

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

    @Id
    private String type;

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

Ejemplo JPA 2.0 id class

...
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;
    }
}
 11
Author: Ondrej Bozek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-02 14:02:02

Creo que la principal ventaja es que podríamos usar @GeneratedValue para el id al usar el @IdClass? Estoy seguro de que no podemos usar @GeneratedValue para @EmbeddedId.

 6
Author: bertie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-10 08:22:42

La clave compuesta no debe tener una propiedad @Id cuando se usa @EmbeddedId.

 4
Author: B.K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-10 08:23:05

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

 0
Author: Aleks Ben Maza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-02 17:20:35