Issue
I want to create a composite key in follower table, only two columns in the table with the Id's of the users. Right now I have my follower table with the two foreign keys and the id of the table
How can I make this possible since all the examples that I find are from two different tables?
[This is the approach that I have right now]
[This is the one that I want]
Followers table
public class Followers {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private long id;
@ManyToOne
@JoinColumn(name="from_user_fk")
private User from;
@ManyToOne
@JoinColumn(name="to_user_fk")
private User to;
public Followers() {};
public Followers(User from, User to) {
this.from = from;
this.to = to;
}
}
User table
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String lastname;
public User() {
}
@OneToMany(mappedBy="to")
private List<Followers> followers;
@OneToMany(mappedBy="from")
private List<Followers> following;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
Solution
There are 2 ways by which you can make Composite Primary key :
- By using @Embeddable class annotation along with @EmbeddedId and @MapsId field annotations
- 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
Answered By - Ashwini Karnale
Answer Checked By - David Goodson (JavaFixing Volunteer)