ChatGPT解决这个技术问题 Extra ChatGPT

Which annotation should I use: @IdClass or @EmbeddedId

The JPA (Java Persistence API) specification has 2 different ways to specify entity composite keys: @IdClass and @EmbeddedId.

I'm using both annotations on my mapped entities, but it turns out to be a big mess to people who aren't very familiar with JPA.

I want to adopt only one way to specify composite keys. Which one is really the best? Why?


K
Kurt Du Bois

I consider that @EmbeddedId is probably more verbose because with @IdClass you cannot access the entire primary key object using any field access operator. Using the @EmbeddedId you can do like this:

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

This gives a clear notion of the fields that make the composite key because they are all aggregated in a class that is accessed trough a field access operator.

Another difference with @IdClass and @EmbeddedId is when it comes to write HQL :

With @IdClass you write:

select e.name from Employee e

and with @EmbeddedId you have to write:

select e.employeeId.name from Employee e

You have to write more text for the same query. Some may argue that this differs from a more natural language like the one promoted by IdClass. But most of the times understanding right from the query that a given field is part of the composite key is of invaluable help.


Though I agree with the explanation given above I would also like to add one unique use case for @IdClass even though I prefer @EmbeddedId in most situations (Got to know this from a session by Antonio Goncalves .What he suggested is we could use the @IdClass in case the composite key class is not accessible or comes in from another module or legacy code where we can't add a annotation .In those scenarios @IdClass will give us a way our .
I think it's possible that the cases for using the @IdClass annotation given by @Gaurav is the very reason that the JPA specification lists both methods of creating a composite key.. @IdClass and @EmbeddidId
j
jumping_monkey

There are three strategies to use a compound primary key:

Mark it as @Embeddable and add to your entity class a normal property for it, marked with @Id.

Add to your entity class a normal property for it, marked with @EmbeddedId.

Add properties to your entity class for all of its fields, mark them with @Id,and mark your entity class with @IdClass, supplying the class of your primary key class.

The use of @Id with a class marked as @Embeddable is the most natural approach. The @Embeddable tag can be used for non-primary key embeddable values anyway. It allows you to treat the compound primary key as a single property, and it permits the reuse of the @Embeddable class in other tables.

The next most natural approach is the use of the @EmbeddedId tag. Here, the primary key class cannot be used in other tables since it is not an @Embeddable entity, but it does allow us to treat the key as a single attribute of some class.

Finally, the use of the @IdClass and @Id annotations allows us to map the compound primary key class using properties of the entity itself corresponding to the names of the properties in the primary key class. The names must correspond (there is no mechanism for overriding this), and the primary key class must honor the same obligations as with the other two techniques. The only advantage to this approach is its ability to “hide” the use of the primary key class from the interface of the enclosing entity. The @IdClass annotation takes a value parameter of Class type, which must be the class to be used as the compound primary key. The fields that correspond to the properties of the primary key class to be used must all be annotated with @Id.

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


l
laz

I discovered an instance where I had to use EmbeddedId instead of IdClass. In this scenario there is a join table that has additional columns defined. I attempted to solve this problem using IdClass to represent the key of an entity that explicitly represents rows in the join table. I couldn't get it working this way. Thankfully "Java Persistence With Hibernate" has a section dedicated to this topic. One proposed solution was very similar to mine but it used EmbeddedId instead. I modeled my objects after those in the book it now behaves correctly.


O
Ondrej Bozek

As far as I know, if your composite PK contains FK it's easier and more straightforward to use @IdClass

With @EmbeddedId you have to define a mapping for your FK column twice, once in @Embeddedable and once for as i.e. @ManyToOne where @ManyToOne has to be read-only(@PrimaryKeyJoinColumn) because you can't have one column set in two variables (possible conflicts).
So you have to set your FK using a simple type in @Embeddedable.

On the other site using @IdClass this situation can be handled much easier as shown in Primary Keys through OneToOne and ManyToOne Relationships:

Example JPA 2.0 ManyToOne id annotation

...
@Entity
@IdClass(PhonePK.class)
public class Phone {
 
    @Id
    private String type;
 
    @ManyToOne
    @Id
    @JoinColumn(name="OWNER_ID", referencedColumnName="EMP_ID")
    private Employee owner;
    ...
}

Example 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;
    }
}

Just don't forget to add getters to your PK class
@Sonata why do we need the getters? I tried it without any getters/setters and it works fine
Thanks for the id class example! Although I ended up needing to implement Serializable as well. Also might as well add getters and setters, especially if your IDE can automatically generate them.
b
bluish

I think the main advantage is that we could use @GeneratedValue for the id when using the @IdClass? I'm sure we can't use @GeneratedValue for @EmbeddedId.


Is it not possible to use @GeneratedValue in Embeddedid??
I have used it successfully with EmbeddedId but apparently it's support varies by DB. This is also true about using it with IdClass. The spec says: "The GeneratedValue annotation may only be portably used for simple (i.e., non-composite) primary keys."
b
bluish

Composite Key must not have an @Id property when @EmbeddedId is used.


A
Aleks Ben Maza

With EmbeddedId you can use the IN clause in HQL, for example : FROM Entity WHERE id IN :ids where id is an EmbeddedId whereas it's pain to achieve the same result with IdClass you will want to do something like FROM Entity WHERE idPartA = :idPartA0 AND idPartB = :idPartB0 .... OR idPartA = :idPartAN AND idPartB = :idPartBN