Which Annotation Should I Use: @Idclass or @Embeddedid

Which annotation should I use: @IdClass or @EmbeddedId

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.

JPA/Hibernate: What's better for composite primary keys, @IdClass or @EmbeddedId implementations and why?

As Pascal wrote here's part of the answer:

Which annotation should I use: @IdClass or @EmbeddedId

In the end, I believe using @IdClass is much easier in practice, because you have to add the embeddedId property name to dereference PK properties, while these aren't written for all non-PK properties.

You always have to remember exactly which properties are part of a PK and those which are not. That complicates writing JPQL queries unneccessarily.

Also, AFAIK the JPA 2.0 spec allows you to put @Id onto @XToX/@JoinColumn/s properties and it introduces the @MapsId annotation, so that mapping identifying relationships (a.k.a. derived identifiers in JPA) are more natural to implement.

Difference using @Id and @EmbeddedId for a compound key

I'm actually surprised the "before" version is working. According to the specification, the correct way to map your Embeddable compound key is the "after" version. Quoting the JPA 1.0 specification:

2.1.4 Primary Keys and Entity Identity


Every entity must have a primary key.

The primary key must be defined on the
entity that is the root of the entity
hierarchy or on a mapped superclass of
the entity hierarchy. The primary key
must be defined exactly once in an
entity hierarchy.

A simple (i.e., non-composite) primary
key must correspond to a single
persistent field or property of the
entity class. The Id annotation is
used to denote a simple primary key.

See section 9.1.8.

A composite primary key must
correspond to either a single
persistent field or property or to a
set of such fields or properties as
described below. A primary key class
must be defined to represent a
composite primary key. Composite
primary keys typically arise when
mapping from legacy databases when the
database key is comprised of several
columns. The EmbeddedId and and
IdClass annotations are used to
denote composite primary keys. See
sections 9.1.14 and 9.1.15.

The primary key (or field or property
of a composite primary key) should be
one of the following types: any Java
primitive type; any primitive wrapper
type; java.lang.String;
java.util.Date; java.sql.Date. In
general, however, approximate numeric
types (e.g., floating point types)
should never be used in primary keys.
Entities whose primary keys use types
other than these will not be portable.
If generated primary keys are
used, only integral types will be
portable. If java.util.Date is used as
a primary key field or property, the
temporal type should be specified as
DATE.

...

And later:

9.1.14 EmbeddedId Annotation


The EmbeddedId annotation is applied
to a persistent field or property of
an entity class or mapped superclass
to denote a composite primary key that
is an embeddable class. The embeddable
class must be annotated as
Embeddable.

There must be only one EmbeddedId
annotation and no Id annotation when
the EmbeddedId annotation is used.

Composite Primary Key usage in REST Spring Boot with follower table

There are 2 ways by which you can make Composite Primary key :

  1. By using @Embeddable class annotation along with @EmbeddedId and
    @MapsId field annotations
  2. Use @IdClass class annotation

I will be explaining the solution by using the first way which is using the @Embeddalbe class annotation followed by @EmbeddedId and @MapsId

Steps:

Step 1: Use @Embeddable to define the composite primary key class

@Embeddable
public class FollowersId implements Serializable {

@Column(name = "from_user_fk")
private Integer userFromId;

@Column(name = "to_user_fk")
private Integer userToId;

public FollowersId (){
}

public Followers(Integer userFromId, Integer userToId) {
this.userFromId = userFromId;
this.userToId = userToId;
}
}

2) The @Embeddable class should meet the following requirements

  • Implements Serializable

  • Implements no-arguments constructor

  • Implements equals and hashCode

3) Embed the composite primary key class into the Followers class with @EmbeddedId and MapsId

@Entity

public class Followers implements Serializable  {
@EmbeddedId
private FollowersId id;

@ManyToOne
@JoinColumn(name="from_user_fk")
@MapsId("userFromId")
private User from;

@ManyToOne
@JoinColumn(name="to_user_fk")
@MapsId("userToId")
private User to;

public Followers() {};

public Followers(User from, User to) {
this.id = new FollowersId(from.getId(), to.getId())
this.from = from;
this.to = to;
}
}

When creating a new instance for the joined entity, the @EmbeddedId composite primary key field should be initialized manually as Hibernate would not be able to set the value via reflection

Otherwise, when saving the entity, you would get the following error in the console

Caused by: org.hibernate.PropertyAccessException: Could not set field value by reflection

For more information

@IdClass JPA Annotation

It is not a good idea you store entities as primary key. There is some limitations when using query language and JPA 1.0 does not support. Besides it there is no need to use entities as primary key. Think about it.If you want, take a special look at the following question

A class that behaves like @Entity and @Embeddable

Answer one

Comment about answer one

You will see that using a entity as primary key is not necessary.

Instead of

public class GroupMembershipPK implements Serializable {

private User user;
private Group group;

}

Use

public class GroupMembershipPK implements Serializable {

private Integer userId;
private Integer groupId;

}

equals implementation is important because JPA compares two entities by using it (JPA checks whether an entity is in persistence context by using equals implementation). So you can implement according to

public boolean equals(Object o) {
if(o == null)
return false;

if(!(o instanceof GroupMembershipPK))
return false;

GroupMembershipPK other = (GroupMembershipPK) o;
if(!(getUserId().equals(other.getUserId()))
return false;

if(!(getGroupId().equals(other.getGroupId()))
return false;

return true;
}

Advice: it is a good idea you use property access instead of field access because, at some times, JPA implementation uses a proxy object because of performance issues. A proxy object makes use of property access because it allows JPA implementation hits the database when necessary.

How to save an object that uses a composite primary key ?

User user = new user();
Group group = new Group();

entityManager.save(user);
entityManager.save(group);

entityManager.flush();

UserGroup userGroup = new UserGroup();

userGroup.setId(new UserGroup.UserGroupId(user.getId(), group.getId()));

entityManager.save(userGroup);

Do you want to know how to implement UserGroup ?

public class UserGroup {

private UserGroupId id;

// You can create UserGroupId outside UserGroup class
// Feel free to choice your best approach
@Embeddable
public static class UserGroupId implements Serializable {

private Integer userId;
private Integer groupId;

// required no-arg constructor
public UserGroupId() {}

public UserGroupId(Integer userId, Integer groupId) {
this.userId = userId;
this.roupId = groupId;
}

// getter's and setter's

// equals and hashcode as shown above

}

@EmbeddedId
public UserGroupId getId() {
return this.id;
}

public setId(UserGroupId id) {
this.id = id;
}
}

Another approach to use composite primary key is IdClass. See IdClass

regards,

Super simple composite key without @EmbeddedId or @IdClass

JPA requires a primary key class of some sort be defined for composite primary keys. In practice though, this seems required mostly for the EntityManager find and getReference calls that require a pk class instance. Implementations may have their own pk representation that you can use for these calls, but will not be portable.

Embedded id and repeated column in mapping for entity... exception

Your two variables match and player in your Performance class are mapped to the same columns as matchId and playerId in the embedded ID. As the error says, they "should be mapped with insert="false" update="false"".

@ManyToOne
@JoinColumn(name="MATCH_ID", insertable = false, updatable = false)
private Match match;

@ManyToOne
@JoinColumn(name="PLAYER_ID", insertable = false, updatable = false)
private Player player;

This essentially makes those fields readonly, so Hibernate knows only to change the MATCH_ID and PLAYER_ID columns if the values in the embedded ID are changed, but not if the values of match or player are changed.

How to expose fields of and @EmbeddedId

In MyClass, add Lombok @Delegate annotation to your PrimaryKey. It should look like:

@Entity
@Data
public class MyClass {
@Delegate
@EmbeddedId
private PrimaryKey id;
}

Then you can set/get PrimaryKey fields directly from MyClass. Here is a link for you to read more about it.



Related Topics



Leave a reply



Submit