使用persistance.ManyToMany 產出composite key的作法
參考網址
The best way to use the @ManyToMany annotation with JPA and Hibernate
情境 :
原本公司內技術組組長寫好的 entity ,
是使用 @ManyToMany 的方式產生兩張table之間的關聯表,
但因為新的需求,必須在自動產生的表中加一個欄位 ( isMain那個欄位)
修改前 | 修改後 |
原始情況 :
Skill.java & User.java 兩隻檔案,
並且使用hibernate在db產生 at_Skill , at_User , at_Skill_User 三個table
其中 at_Skill_User 的結構如下
Skill.java 裡面的部分code 為
@ManyToMany
@JoinTable(name = "at_Skill_User", indexes = { @Index(name = "idx_skills_id", columnList = "skills_id"),
@Index(name = "idx_users_id", columnList = "users_id") })
private Set<User> users = new HashSet<>();
@ManyToMany
@JoinTable(name = "at_Skill_User", indexes = { @Index(name = "idx_skills_id", columnList = "skills_id"),
@Index(name = "idx_users_id", columnList = "users_id") })
private Set<User> users = new HashSet<>();
User.java 裡面的部分code 為
@ManyToMany(mappedBy = "users")
private Set<Skill> skills = new HashSet<>();
@ManyToMany(mappedBy = "users")
private Set<Skill> skills = new HashSet<>();
修改後 :
會有四隻檔案,分別為
Skill.java 、 User.java 、 SkillUser.java 、 SkillUserId.java
Skill.java 把原本的code 改為
@OneToMany(mappedBy = "skills", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<SkillUser> skillUser = new HashSet<>();
@OneToMany(mappedBy = "skills", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<SkillUser> skillUser = new HashSet<>();
User.java 把原本的code 改為
@OneToMany(mappedBy = "users", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<SkillUser> skillUser = new HashSet<>();
@OneToMany(mappedBy = "users", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<SkillUser> skillUser = new HashSet<>();
SkillUser.java
@Entity
@Table(name = "at_Skill_User", indexes = { @Index(name = "idx_skills_id", columnList = "skills_Id"),
@Index(name = "idx_users_id", columnList = "users_Id") })
public class SkillUser implements Serializable{
private static final long serialVersionUID = -8189271280419140508L;
@EmbeddedId
private SkillUserId id;
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("skillId") //這邊要對應到 SkillUserId class 裡面的變數名稱
private Skill skills; //hibernate 會用這個名稱在table中產生一個 skills_id 作為 FK
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("userId") //這邊要對應到 SkillUserId class 裡面的變數名稱
private User users; //hibernate 會用這個名稱在table中產生一個 users_id 作為 FK
@Column(name = "isMain")
private boolean isMain;
private SkillUser(){}
public SkillUser(Skill skill, User user){
this.skills = skill;
this.users = user;
this.id = new SkillUserId(skill.getId(),user.getId());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass())
return false;
SkillUser that = (SkillUser) o;
return Objects.equals(skills, that.skills) &&
Objects.equals(users, that.users);
}
@Override
public int hashCode() {
return Objects.hash(skills, users);
}
...(一些setter and getter)
}
@Entity
@Table(name = "at_Skill_User", indexes = { @Index(name = "idx_skills_id", columnList = "skills_Id"),
@Index(name = "idx_users_id", columnList = "users_Id") })
public class SkillUser implements Serializable{
private static final long serialVersionUID = -8189271280419140508L;
@EmbeddedId
private SkillUserId id;
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("skillId") //這邊要對應到 SkillUserId class 裡面的變數名稱
private Skill skills; //hibernate 會用這個名稱在table中產生一個 skills_id 作為 FK
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("userId") //這邊要對應到 SkillUserId class 裡面的變數名稱
private User users; //hibernate 會用這個名稱在table中產生一個 users_id 作為 FK
@Column(name = "isMain")
private boolean isMain;
private SkillUser(){}
public SkillUser(Skill skill, User user){
this.skills = skill;
this.users = user;
this.id = new SkillUserId(skill.getId(),user.getId());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass())
return false;
SkillUser that = (SkillUser) o;
return Objects.equals(skills, that.skills) &&
Objects.equals(users, that.users);
}
@Override
public int hashCode() {
return Objects.hash(skills, users);
}
...(一些setter and getter)
}
SkillUserId.java
@Embeddable
public class SkillUserId implements Serializable {
@Column(name = "skills_id") //hibernate產生的table 會用這個名稱產生一個欄位當成PK
private Integer skillId;
@Column(name = "users_id") //hibernate產生的table 會用這個名稱產生一個欄位當成PK
private Integer userId;
private SkillUserId() {}
public SkillUserId(Integer skillId, Integer userId) {
this.skillId = skillId;
this.userId = userId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass())
return false;
SkillUserId that = (SkillUserId) o;
return Objects.equals(skillId, that.skillId) &&
Objects.equals(userId, that.userId);
}
@Override
public int hashCode() {
return Objects.hash(skillId, userId);
}
...(一些setter and getter)
}
@Embeddable
public class SkillUserId implements Serializable {
@Column(name = "skills_id") //hibernate產生的table 會用這個名稱產生一個欄位當成PK
private Integer skillId;
@Column(name = "users_id") //hibernate產生的table 會用這個名稱產生一個欄位當成PK
private Integer userId;
private SkillUserId() {}
public SkillUserId(Integer skillId, Integer userId) {
this.skillId = skillId;
this.userId = userId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass())
return false;
SkillUserId that = (SkillUserId) o;
return Objects.equals(skillId, that.skillId) &&
Objects.equals(userId, that.userId);
}
@Override
public int hashCode() {
return Objects.hash(skillId, userId);
}
...(一些setter and getter)
}
其中如果hibernate產出的 PK 與 FK 欄位名稱不同的話,會變成兩個欄位,一個欄位為PK,另一個為FK
(我在這一步卡超久)
感謝,看到這邊,如果有什麼地方寫錯歡迎指出
By Carl