本章主要是描述幾種經典映射關系,順帶比較Hibernate4.x和Hibernate5.x之間的區別。
一、建立測試工程目錄
有關實體類之間的相互映射關系,Hibernate官方文檔其實描述的非常詳細,這里只提供幾種常見映射。(推薦4.3.11版本的 hibernate-release-4.3.11.Final\documentation\manual)
二、編寫映射關系
(1)one2one單表內嵌映射:


package model.family;import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id;@Entity public class Husband {private int id;private String husbandName;private Wife wife;@Id@GeneratedValuepublic int getId() {return id;}public void setId(int id) {this.id = id;}public String getHusbandName() {return husbandName;}public void setHusbandName(String husbandName) {this.husbandName = husbandName;}// 兩個實體對象共用一張數據表,提高查詢速度 @Embeddedpublic Wife getWife() {return wife;}public void setWife(Wife wife) {this.wife = wife;}}


package model.family;//不用添加任何注解,持久化過程通過主表完成 public class Wife {private String wifeName;public String getWifeName() {return wifeName;}public void setWifeName(String wifeName) {this.wifeName = wifeName;} }
(2)one2one外鍵映射:


package model.userinfo;import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.Transient;@Entity public class User {private int id;private String username;private String password;private String confirm;private Information info;@Id@GeneratedValuepublic int getId() {return id;}public void setId(int id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}/** 延遲加載,級聯操作。 * 刪除開啟了級聯的一方,被級聯的一方也會被刪除* 注意:如果session的操作是通過hibernate控制,延遲加載不會出問題。如果是通過手工開啟實物,操作不當延遲加載可能拋出懶加載異常*/@OneToOne(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL)public Information getInfo() {return info;}public void setInfo(Information info) {this.info = info;}// 本字段不參與持久化過程 @Transientpublic String getConfirm() {return confirm;}public void setConfirm(String confirm) {this.confirm = confirm;} }


package model.userinfo;import java.util.Date;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.Temporal; import javax.persistence.TemporalType;//注解也可以直接配置在字段上,但是不推薦。據說原因是可能破壞oop封裝。但是我覺得有時這樣配置可以讓代碼顯得更加整潔,特別是在Spring中。 @Entity public class Information {@Id@GeneratedValueprivate int id;@OneToOneprivate User user;@Temporal(TemporalType.DATE)private Date resgisterDate;private String address;public int getId() {return id;}public void setId(int id) {this.id = id;}public User getUser() {return user;}public void setUser(User user) {this.user = user;}public Date getResgisterDate() {return resgisterDate;}public void setResgisterDate(Date resgisterDate) {this.resgisterDate = resgisterDate;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}}
(3)many2many多表映射:
場景描述:學校里有多個老師,每個老師教授多個學生,每個學生每一門課程會有一個得分。


package model.school;import java.util.HashSet; import java.util.Set;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToMany;@Entity public class Teacher {private int id;private String tchName;private Set<Student> students = new HashSet<Student>();@Id@GeneratedValuepublic int getId() {return id;}public void setId(int id) {this.id = id;}public String getTchName() {return tchName;}public void setTchName(String tchName) {this.tchName = tchName;}//老師和學生的對應表由學生一方負責維護@ManyToMany(mappedBy = "teachers")public Set<Student> getStudents() {return students;}public void setStudents(Set<Student> students) {this.students = students;}}


package model.school;import java.util.HashSet; import java.util.Set;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OneToMany;@Entity public class Student {private int id;private String stuName;private Set<Teacher> teachers = new HashSet<Teacher>();private Set<Score> scores = new HashSet<Score>();@Id@GeneratedValuepublic int getId() {return id;}public void setId(int id) {this.id = id;}public String getStuName() {return stuName;}public void setStuName(String stuName) {this.stuName = stuName;}/** many2many必須使用中間表,配置中間表的表明和列名*/@ManyToMany@JoinTable(name = "student_teacher", joinColumns = { @JoinColumn(name = "studentId") }, inverseJoinColumns = {@JoinColumn(name = "teacherId") })public Set<Teacher> getTeachers() {return teachers;}public void setTeachers(Set<Teacher> teachers) {this.teachers = teachers;}// 學生同分數之間的關系同樣交給多的一方負責維護@OneToMany(mappedBy = "student")public Set<Score> getScores() {return scores;}public void setScores(Set<Score> scores) {this.scores = scores;}}


package model.school;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToOne;@Entity public class Score {private int id;private int courseScore;private Teacher teacher;private Student student;@Id@GeneratedValuepublic int getId() {return id;}public void setId(int id) {this.id = id;}public int getCourseScore() {return courseScore;}public void setCourseScore(int courseScore) {this.courseScore = courseScore;}@ManyToOnepublic Teacher getTeacher() {return teacher;}public void setTeacher(Teacher teacher) {this.teacher = teacher;}@ManyToOnepublic Student getStudent() {return student;}public void setStudent(Student student) {this.student = student;}}
按照以上的映射關系生成數據表以后會注意到,其實老師和學生之間的關系表純粹多余,分數表已經維護了雙方的關系。重新優化他們之間的映射關系:


package model.school;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id;@Entity public class Teacher {private int id;private String tchName;@Id@GeneratedValuepublic int getId() {return id;}public void setId(int id) {this.id = id;}public String getTchName() {return tchName;}public void setTchName(String tchName) {this.tchName = tchName;}}


package model.school;import java.util.HashSet; import java.util.Set;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany;@Entity public class Student {private int id;private String stuName;private Set<Score> scores = new HashSet<Score>();@Id@GeneratedValuepublic int getId() {return id;}public void setId(int id) {this.id = id;}public String getStuName() {return stuName;}public void setStuName(String stuName) {this.stuName = stuName;}@OneToMany(mappedBy = "student")public Set<Score> getScores() {return scores;}public void setScores(Set<Score> scores) {this.scores = scores;}}


package model.school;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToOne;@Entity public class Score {private int id;private int courseScore;private Teacher teacher;private Student student;@Id@GeneratedValuepublic int getId() {return id;}public void setId(int id) {this.id = id;}public int getCourseScore() {return courseScore;}public void setCourseScore(int courseScore) {this.courseScore = courseScore;}@ManyToOnepublic Teacher getTeacher() {return teacher;}public void setTeacher(Teacher teacher) {this.teacher = teacher;}@ManyToOnepublic Student getStudent() {return student;}public void setStudent(Student student) {this.student = student;}}
由此可見,即使是一個相對復雜的映射關系也可以通過優化得到一個相對簡單的數據模型。
(4)many2one和one2many單表樹形映射:
場景描述:地圖,一個國家包含多個省份,每個省份又包含多個城市...


package model.tree;import java.util.HashSet; import java.util.Set;import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.Table;@Entity @Table(name = "_tree") public class Tree {private int id;private String name;private Tree parent;private Set<Tree> children = new HashSet<Tree>();@Id@GeneratedValuepublic int getId() {return id;}public void setId(int id) {this.id = id;}@Column(name = "t_name", unique = true)public String getName() {return name;}public void setName(String name) {this.name = name;}@ManyToOnepublic Tree getParent() {return parent;}public void setParent(Tree parent) {this.parent = parent;}//刪除根節點,與它相關的所有子節點全部刪除@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)public Set<Tree> getChildren() {return children;}public void setChildren(Set<Tree> children) {this.children = children;} }
注意:以上4種映射關系在4.3.11版本中正常。但在5.0.6版本中id字段被系統強制指定為了@GeneratedValue(strategy=GenerationType.TABLE)的方式。我曾經嘗試手工指定生成策略為auto或者identity均無效。如果是通過xml的方式配置是正常的,目前我還不清楚是什么原因導致的上述異常。這個問題造成了下面的映射關系目前只能在4.x版本中正常使用:
(5)one2one主鍵映射


package model.personaddr;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.PrimaryKeyJoinColumn;@Entity public class Person {private int id;private String name;private Address address;@Id@GeneratedValuepublic int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}// 兩張表通過主鍵關聯@OneToOne(optional = true)@PrimaryKeyJoinColumnpublic Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;} }


package model.personaddr;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToOne;@Entity public class Address {private int id;private String local;private Person person;@Id@GeneratedValuepublic int getId() {return id;}public void setId(int id) {this.id = id;}public String getLocal() {return local;}public void setLocal(String local) {this.local = local;}@OneToOne(mappedBy = "address")public Person getPerson() {return person;}public void setPerson(Person person) {this.person = person;}}
ps:好在主鍵映射在實際使用中并不常見。
?
最后按照慣例,提供整個項目的完整目錄結構和IDE版本信息