Reputation: 393
I have a Profile class which holds user's information. One of the features of this application is to "follow" another profiles of your choice. I'm having trouble designing the DB and making the link happen, both writing the model code and the repository (is it just a matter of a save?).
My Profile class looks like this: (irrelevant fields to the question and getters/setters are omitted for the sake of simplicity)
import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.*;
@Entity
@Table(name = "USERS")
public class Profile implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Size(max = 32)
@Column(nullable = false)
private String name;
@Size(max = 16)
@Column(unique=true, nullable = false)
private String username;
@Email
@Column(unique=true)
private String email;
@ManyToOne
@JoinColumn(name="profile_id")
private Profile profile;
@OneToMany(mappedBy = "profile")
private List<Profile> friends = new ArrayList<>();
public Profile() {
}
// other getters/setters
public List<Profile> getFriends() {
return friends;
}
public void setFriends(List<Profile> friends) {
this.friends = friends;
}
public void addFriend(Profile friend) {
this.friends.add(friend);
}
}
This creates a null
field called profile_id
in the users' table. I've done some research and it seems like creating another table using @SecondaryTable
annotation may do the trick. The tutorials I've looked into did not solve my doubts. I'd like some light into the matter, and if possible avoid creating a different entity.
Ideally, I'd have a table with its own id, the owner's id (the person who is following) and the target's id (the person who is being followed).
Thanks in advance!
Upvotes: 0
Views: 478
Reputation: 617
Well, implementing a @ManyToMany
relationship has two approaches. First is to create a separate table only with the respective Id's of each entity. And that thing, might be easy, but it does not cover all the cases. For example in your case you are implementing a @ManyToMany
for user and followers. You also might need a date for that,to show in which date they created that relationship. And that is the case where the things get a little bit complicated(not much), but it results in a piece of beauty code. Anyways let's go for both approaches:
`
@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(
name = "profile_follower",
joinColumns = @JoinColumn(name = "profile_id", referencedColumnName="id"),
inverseJoinColumns = @JoinColumn(name = "following_id", referencedColumnName="id")
private Set<Profile> profiles;
@ManyToMany(mappedBy = "profiles")
private Set<Profile> following= new HashSet<>();
`
The other approach is creating a separate class for the relationship. `
@Entity
public class ProfilesFollowedByUser{
@EmbeddedId
UserFollower id;//this is a separate class
@ManyToOne
@MapsId("id")
@JoinColumn(name="profile_id", referencedColumnName="id")
private Profile profile;
@ManyToOne
@MapsId("id")
@JoinColumn(name="following_id", referencedColumnName="id")
private Profile profile;
private Date createdAt;}
` And here is the embeddable class:
`
@Embeddable
public class UserFollower implements Serializable{
private static final long serialVersionUID = 819015237657421374L;
@Column(name="profile_id")
private Long profileId;
@Column(name="following_id")
private Long followingId;
}
` The second one is probably a little bit more complicated but in vast majority of scenarios at least in the real world apps, when you have to deal with a relationship created a date at least is always needed. Hope that helps with your problem.
Upvotes: 1
Reputation: 90417
What you need is a separate table to store this following relationship (many-to-many). The most simplest way is to use @ManyToMany
:
@Entity
@Table(name = "USERS")
public class Profile implements Serializable {
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="following",
joinColumns={@JoinColumn(name="user_id")},
inverseJoinColumns={@JoinColumn(name="follower_id")})
private Set<Profile> followers = new HashSet<Profile>();
@ManyToMany(mappedBy = "followers", cascade = CascadeType.ALL)
private Set<Profile> followBy = new HashSet<Profile>();
}
It will be mapped to a relationship table called following
which has the following schema :
---------------------------------
| column | FK |
=================================
| user_id | FK to users.id |
---------------------------------
| follower_id | FK to users.id |
---------------------------------
So given an user A , the field followers
is all users that A is following to. And the field followBy
is all users the are following to A.
Also , note that the mappedBy
setting .It means you have to use the field followers
to maintain this following relationship but not the field followBy
, which basically means that you have to insert /remove instances to the followers
Set to change the following relationship for a given profile.
Also see this if you want the add additional columns to the relationship table (i.e. following
table)
Upvotes: 2