Many To Many Self Join with extra columns

Hi there!
We have an entity with self referencing mapping (like followers and following).

@Entity
public class User {

    @Id
    @GeneratedValue
    private UUID id;
    
    @ManyToMany(cascade = CascadeType.ALL, mappedBy = "following")
    @Fetch(FetchMode.SUBSELECT)
    private Set<User> followers = new HashSet<>();
    
    @JoinTable(name = "user_relation",
            joinColumns = {@JoinColumn(name = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "user_relation_id")})
    @ManyToMany(cascade = CascadeType.ALL)
    @Fetch(FetchMode.SUBSELECT)
    private Set<User> following = new HashSet<>();

There’s a new requirement - add extra columns to the map table.
I’ve created extra entity for that and composite Primary Key:

@Embeddable
public class Id implements Serializable {

    @Column(name = "user_id")
    private UUID userId;

    @Column(name = "user_relation_id")
    private UUID userRelationId;

    public Id() {}

    public Id(UUID userId, UUID userRelationId) {
        this.userId = userId;
        this.userRelationId = userRelationId;
    }
}

@Entity
@Table(name = "user_relation")
public class UserRelation {

    @EmbeddedId
    private Id id;

    @MapsId("userId")
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    protected User user;

    @MapsId("relationId")
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_relation_id", referencedColumnName = "id")
    protected User relation;

    public UserRelation(User user, User relation) {
        this.user = user;
        this.relation = relation;
        this.id = new Id(user.getId(), relation.getId());
    }
}

But how to configure mapping on the User entity to support following and followers?

You will have to map UserRelation:

@OneToMany(cascade = CascadeType.ALL, mappedBy = "relation")
@Fetch(FetchMode.SUBSELECT)
private Set<UserRelation> followers = new HashSet<>();

@OneToMany(cascade = CascadeType.ALL, mappedBy = "relation")
@Fetch(FetchMode.SUBSELECT)
private Set<UserRelation> following = new HashSet<>();
1 Like

Yep, thanks, it works. But I guess following should be mapped by user.

@OneToMany(cascade = CascadeType.ALL, mappedBy = "relation")
@Fetch(FetchMode.SUBSELECT)
private Set<UserRelation> followers = new HashSet<>();

@OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
@Fetch(FetchMode.SUBSELECT)
private Set<UserRelation> following = new HashSet<>();