Anatoly
Anatoly

Reputation: 1591

Hibernate self join many to many with extra column

Help me to understand how I can realize many to many self join with extra column using hibernate? Should I map join table too? Or may be exists another way? I can not find anything usefull for me...

Upvotes: 0

Views: 2222

Answers (2)

handsome16
handsome16

Reputation: 61

I've been struggling with this question for a long time, perhaps someone will find Wolfram's wording difficult, so I'll explain it in more detail: Imagine, what we need map something like that uml_diagramm

I needed to set up links between any two users, and the links were used to chat between them. So our user class will be :

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
@Entity
public class User  implements Serializable {
    private String email;
    private String password;
    private Long id;
    private String nickname;
    private String phone;
    private Collection<Chats> chatsById;
    private Collection<Chats> chatsById_0;


@Basic
@Column(name = "email", nullable = false, length = -1)
public String getEmail() {
    return email;
}

public void setEmail(String email) {
    this.email = email;
}

@Basic
@Column(name = "password", nullable = false, length = -1)
public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}

@Id
@Column(name = "id", nullable = false)
public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

@Basic
@Column(name = "nickname", nullable = true, length = -1)
public String getNickname() {
    return nickname;
}

public void setNickname(String nickname) {
    this.nickname = nickname;
}

@Basic
@Column(name = "phone", nullable = true, length = -1)
public String getPhone() {
    return phone;
}

public void setPhone(String phone) {
    this.phone = phone;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    User user = (User) o;
    return Objects.equals(email, user.email) && Objects.equals(password, user.password) && Objects.equals(id, user.id) && Objects.equals(nickname, user.nickname) && Objects.equals(phone, user.phone);
}

@Override
public int hashCode() {
    return Objects.hash(email, password, id, nickname, phone);
}


@OneToMany(mappedBy = "userByIdFrom")
public Collection<Chats> getChatsById() {
    return chatsById;
}

public void setChatsById(Collection<Chats> chatsById) {
    this.chatsById = chatsById;
}

@OneToMany(mappedBy = "userByIdTo")
public Collection<Chats> getChatsById_0() {
    return chatsById_0;
}

public void setChatsById_0(Collection<Chats> chatsById_0) {
    this.chatsById_0 = chatsById_0;
}

}

Please, pay attention to two @OneToMany annotations - getChatsById and getChatsById_0

Now our Chat class:

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
@Entity
@IdClass(ChatsPK.class)
public class Chats implements Serializable {
    private Long idFrom;
    private Long idTo;
    private Object uuid;
    private User userByIdFrom;
    private User userByIdTo;

@Id
@Column(name = "id_from", nullable = false)
public Long getIdFrom() {
    return idFrom;
}

public void setIdFrom(Long idFrom) {
    this.idFrom = idFrom;
}

@Id
@Column(name = "id_to", nullable = false)
public Long getIdTo() {
    return idTo;
}

public void setIdTo(Long idTo) {
    this.idTo = idTo;
}

@Basic
@Column(name = "uuid", nullable = false)
public Object getUuid() {
    return uuid;
}

public void setUuid(Object uuid) {
    this.uuid = uuid;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Chats chats = (Chats) o;
    return Objects.equals(idFrom, chats.idFrom) && Objects.equals(idTo, chats.idTo) && Objects.equals(uuid, chats.uuid);
}

@Override
public int hashCode() {
    return Objects.hash(idFrom, idTo, uuid);
}

@ManyToOne
@JoinColumn(name = "id_from", referencedColumnName = "id", nullable = false)
public User getUserByIdFrom() {
    return userByIdFrom;
}

public void setUserByIdFrom(User userByIdFrom) {
    this.userByIdFrom = userByIdFrom;
}

@ManyToOne
@JoinColumn(name = "id_to", referencedColumnName = "id", nullable = false)
public User getUserByIdTo() {
    return userByIdTo;
}

public void setUserByIdTo(User userByIdTo) {
    this.userByIdTo = userByIdTo;
    }
}

To be honest, I haven't figured out why it works yet myself but, there should be more details

Meanwhile, I know for a fact that intelijIdea can generate you models from the database and this may be help you Generate Persistence Mapping by Database Schema - Detail settings for Entity Class. (This code was generated by it )

Upvotes: 0

Wolfram
Wolfram

Reputation: 8052

Map the join table as a dedicated entity and then link it via two OneToMany relationships. This often is the right way as it becomes more than just a technical detail as soon as you add more columns.

This should work the same way for a self join, you just have two fields on that model that are associated to the joining entity.

See this answer, where this is described in more detail.

Upvotes: 1

Related Questions