how to make a composite primary key (java persistence annotation)

b0x0rz picture b0x0rz · Jul 31, 2009 · Viewed 49.8k times · Source

How to make it so that the table user_roles defines the two columns (userID, roleID) as a composite primary key. should be easy, just can't remember/find.

In user entity:

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_roles")
public List<RoleDAO> getRoles() {
    return roles;
}

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer getUserID() {
    return userID;
}

In roles entity:

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_roles")
public List<UserDAO> getUsers() {
    return users;
}

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer getRoleID() {
    return roleID;
}

Thank you.

** MORE INFO

So there is a third table user_roles (auto generated by above) that takes userID from user entity and roleID from roles entity. Now I need those two columns in the generated table (user_roles) to be a composite primary key.

Answer

Tim picture Tim · Aug 6, 2009

You've already had a few good answers here on how to do exactly as you ask..

For reference let me just mention the recommended way to do this in Hibernate instead, which is to use a surrogate key as primary key, and to mark business keys as NaturalId's:

Although we recommend the use of surrogate keys as primary keys, you should try to identify natural keys for all entities. A natural key is a property or combination of properties that is unique and non-null. It is also immutable. Map the properties of the natural key inside the element. Hibernate will generate the necessary unique key and nullability constraints and, as a result, your mapping will be more self-documenting.

It is recommended that you implement equals() and hashCode() to compare the natural key properties of the entity.

In code, using annotations, this would look something like this:

@Entity
public class UserRole {
  @Id
  @GeneratedValue
  private long id;

  @NaturalId
  private User user;
  @NaturalId
  private Role role;
}

Using this will save you a lot of headaches down the road, as you'll find out when you frequently have to reference / map the composed primary key.

I found this out the hard way, and in the end just gave up fighting against Hibernate and instead decided to go with the flow. I fully understand that this might not be possible in your case, as you might be dealing with legacy software or dependencies, but I just wanted to mention it for future reference. (if you can't use it maybe someone else can!)