简体   繁体   中英

hibernate secondary table for same entity relationship

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!

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 (ie following table)

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM