Qual anotação devo usar: @IdClass ou @EmbeddedId

128

A JPAespecificação (Java Persistence API) possui 2 maneiras diferentes de especificar chaves compostas de entidade: @IdClasse @EmbeddedId.

Estou usando as duas anotações nas minhas entidades mapeadas, mas acaba sendo uma grande bagunça para as pessoas que não estão muito familiarizadas JPA.

Quero adotar apenas uma maneira de especificar chaves compostas. Qual é realmente o melhor? Por quê?

sakana
fonte

Respostas:

86

Considero que isso @EmbeddedIdprovavelmente é mais detalhado, porque @IdClassvocê não pode acessar todo o objeto de chave primária usando qualquer operador de acesso ao campo. Usando o que @EmbeddedIdvocê pode fazer assim:

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

Isso fornece uma noção clara dos campos que compõem a chave composta, porque todos eles são agregados em uma classe que é acessada através de um operador de acesso a campos.

Outra diferença com @IdClasse @EmbeddedIdé quando se trata de escrever HQL:

Com @IdClassvocê escreve:

selecione e.name em Funcionário e

e com @EmbeddedIdvocê tem que escrever:

selecione e.employeeId.name em Funcionário e

Você precisa escrever mais texto para a mesma consulta. Alguns podem argumentar que isso difere de uma linguagem mais natural como a promovida por IdClass. Mas na maioria das vezes, entender diretamente a partir da consulta que um determinado campo faz parte da chave composta é uma ajuda inestimável.

Jorge Ferreira
fonte
10
Embora eu concorde com a explicação dada acima, também gostaria de adicionar um caso de uso exclusivo, @IdClassmesmo que prefira @EmbeddedIdna maioria das situações (soube disso em uma sessão de Antonio Goncalves. O que ele sugeriu é que poderíamos usar o @IdClasscaso de o composto classe de chave não é acessível ou vem de outro módulo ou legado código onde não podemos adicionar uma anotação .Em esses cenários @IdClassnos dará uma forma como a nossa.
Gaurav Rawat
1
Eu acho que é possível que os casos para a utilização da @IdClassanotação dada pelo @Gaurav é a razão que as listas de especificação JPA ambos os métodos de criação de uma chave composta .. @IdClasse@EmbeddidId
kapad
20

Existem três estratégias para usar uma chave primária composta:

  • Marque-o como @Embeddablee adicione à sua classe de entidade uma propriedade normal para ele, marcada com @Id.
  • Adicione à sua classe de entidade uma propriedade normal, marcada com @EmbeddedId.
  • Adicione propriedades à sua classe de entidade para todos os seus campos, marque-as com @Ide marque sua classe de entidade @IdClass, fornecendo a classe de sua classe de chave primária.

O uso de @Idcom uma classe marcada como @Embeddableé a abordagem mais natural. A @Embeddabletag pode ser usada para valores incorporáveis ​​de chave não primária de qualquer maneira. Ele permite que você trate a chave primária composta como uma propriedade única e permite a reutilização da @Embeddableclasse em outras tabelas.

A próxima abordagem mais natural é o uso da @EmbeddedIdtag. Aqui, a classe de chave primária não pode ser usada em outras tabelas, pois não é uma @Embeddableentidade, mas nos permite tratar a chave como um atributo único de alguma classe.

Por fim, o uso das anotações @IdClasse @Idpermite mapear a classe de chave primária composta usando propriedades da própria entidade correspondentes aos nomes das propriedades na classe de chave primária. Os nomes devem corresponder (não há mecanismo para substituir isso) e a classe de chave primária deve respeitar as mesmas obrigações que as outras duas técnicas. A única vantagem dessa abordagem é sua capacidade de "ocultar" o uso da classe de chave primária da interface da entidade envolvente. A @IdClassanotação usa um parâmetro de valor do tipo Classe, que deve ser a classe a ser usada como chave primária composta. Todos os campos que correspondem às propriedades da classe de chave primária a serem usados ​​devem ser anotados @Id.

Referência: http://www.apress.com/us/book/9781430228509

Adelin
fonte
17

Eu descobri uma instância em que eu tinha que usar EmbeddedId em vez de IdClass. Nesse cenário, há uma tabela de junção que possui colunas adicionais definidas. Tentei resolver esse problema usando o IdClass para representar a chave de uma entidade que representa explicitamente as linhas na tabela de junção. Não consegui fazê-lo funcionar dessa maneira. Felizmente, "Java Persistence With Hibernate" possui uma seção dedicada a este tópico. Uma solução proposta era muito semelhante à minha, mas utilizava o EmbeddedId. Modelei meus objetos após os do livro que agora se comportam corretamente.

Laz
fonte
13

Tanto quanto eu sei se o seu PK composto contém FK, é mais fácil e mais simples de usar @IdClass

Com @EmbeddedIdvocê tem que definir o mapeamento para sua coluna FK duas vezes, @Embeddedableuma vez e outra como, por exemplo, @ManyToOneonde @ManyToOnedeve ser somente leitura ( @PrimaryKeyJoinColumn) porque você não pode ter uma coluna definida em duas variáveis ​​(possíveis conflitos).
Então você precisa definir seu FK usando o tipo simples @Embeddedable.

No outro site @IdClass, é possível lidar com essa situação com muito mais facilidade, conforme mostrado em Chaves Primárias por meio dos Relacionamentos OneToOne e ManyToOne :

Exemplo de anotação de identificação JPA 2.0 ManyToOne

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

    @Id
    private String type;

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

Exemplo de classe de identificação 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
fonte
1
Só não se esqueça de adicionar getters a sua classe PK
Sonata
@ Sonata Por que precisamos dos getters? Tentei-lo sem getters / setters e funciona bem
xagaffar
Obrigado pelo exemplo da classe id! Embora eu acabasse precisando implementar o Serializable também. Também é possível adicionar getters e setters, especialmente se o seu IDE puder gerá-los automaticamente.
Starwarswii
8

Eu acho que a principal vantagem é que poderíamos usar @GeneratedValueo ID ao usar o @IdClass? Tenho certeza de que não podemos usar @GeneratedValuepara @EmbeddedId.

Bertie
fonte
1
Não é possível usar @GeneratedValue no Embeddedid?
Kayser #
1
Eu usei com sucesso com o EmbeddedId, mas aparentemente o suporte varia de acordo com o banco de dados. Isso também é verdade sobre o uso com o IdClass. A especificação diz: "A anotação GeneratedValue pode ser usada de forma portável apenas para chaves primárias simples (ou seja, não compostas)".
BPS
4

Chave composta não deve ter uma @Idpropriedade quando @EmbeddedIdé usada.

BK
fonte
1

Com o EmbeddedId, você pode usar a cláusula IN no HQL, por exemplo: FROM Entity WHERE id IN :idsonde id é um EmbeddedId, ao passo que é difícil obter o mesmo resultado com o IdClass, você deve fazer algo comoFROM Entity WHERE idPartA = :idPartA0 AND idPartB = :idPartB0 .... OR idPartA = :idPartAN AND idPartB = :idPartBN

Aleks Ben Maza
fonte