JPA實體關係對映
實體關係是指實體與實體之間的關係,從方向上分為單向關聯和雙向關聯,從實體數量上分為一對一、一對多、多對多等。對於任何兩個實體,都要從這兩個方面區分它們之間的關係。
單向關聯是一個實體中引用了另外一個實體,也即通過一個實體可以獲取另一個實體物件的引用;雙向關聯是兩個實體之間可以相互獲取對方物件的引用。
1、一對一
學生和身份證是一對一的關係,下面將從單向關聯和雙向關聯來分析。
1.1、單向關聯
假設在學生實體中可以獲取身份證物件的引用;反之,不能在身份證實體中獲取學生物件的引用,則學生和身份證是一對一的單向關聯關係,關係對映如下:
@Entity
@Table(name="t_IDCard")
public
{
private static final long serialVersionUID = 1L;
private Stringnum;
public IDCard()
{
}
@Id
@GeneratedValue
public String getNum()
{
return num;
}
public void setNum(String num)
{
this.num =num;
}
/*
* ....
* */
}
@Entity
@Table(name="t_student")
public class Student implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
private IDCardidCard;
public Student(){}
@Id
@GeneratedValue
public int getId()
{
return id
}
public void setId(intid)
{
this.id =id;
}
@OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name="IDCrad_id")
public IDCardgetIdCard()
{
return idCard;
}
public void setIdCard(IDCard idCard)
{
this.idCard =idCard;
}
/*
* ....
* */
}
@OneToOne只能確定實體與實體的關係是一對一的關係,不能指定資料庫表中的儲存的關聯欄位,所以要結合@JoinColumn來指定關聯欄位。
在預設情況下,關聯實體(IDCard)的主鍵一般是用來做外來鍵的。但如果此時不想將關聯實體的主鍵作為外來鍵,而是將其他欄位作為關聯欄位(外來鍵可以關聯到其他表的主鍵上,也可以關聯到其他表的非空欄位上),此時需要設定@JoinColumn的referencedColumnName屬性。
1.2、雙向關聯
@Entity
@Table(name="T_IDCARD")
public class IDCard implements Serializable
{
private static final long serialVersionUID = 1L;
private Stringnum;
private Studentstudent;
public IDCard()
{
}
@Id
@GeneratedValue
public String getNum()
{
return num;
}
public void setNum(String num)
{
this.num =num;
}
@OneToOne(mappedBy="idCard")
public StudentgetStudent()
{
return student;
}
public void setStudent(Student student)
{
this.student =student;
}
/*
* ....
* */
}
@Entity
@Table(name="T_STUDENT")
public class Student implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
private IDCardidCard;
public Student(){}
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(intid)
{
this.id =id;
}
@OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name="idcard_id")
public IDCardgetIdCard()
{
return idCard;
}
public void setIdCard(IDCard idCard)
{
this.idCard =idCard;
}
/*
* ...
**/
}
1.3、主鍵關聯
既讓兩個實體物件具有相同的主鍵值,以表明它們之間的一一對應的關係;而資料庫不會有額外的欄位來維護它們之間的關係,僅僅通過表的主鍵來關聯,主鍵的值需要程式來顯示維護,對映如下
@Entity
@Table(name="T_IDCARD")
public class IDCard implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
private Stringnum;
private Studentstudent;
public IDCard()
{
}
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(intid)
{
this.id =id;
}
public String getNum()
{
return num;
}
public void setNum(String num)
{
this.num =num;
}
@OneToOne
@PrimaryKeyJoinColumn
public StudentgetStudent()
{
return student;
}
public void setStudent(Student student)
{
this.student =student;
}
/*
* ....
* */
}
@Entity
@Table(name="T_STUDENT")
public class Student implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
private IDCardidCard;
public Student(){}
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(intid)
{
this.id =id;
}
@OneToOne
@PrimaryKeyJoinColumn
public IDCardgetIdCard()
{
return idCard;
}
public void setIdCard(IDCard idCard)
{
this.idCard =idCard;
}
/*
* ...
**/
}
1.4、總結
1) 確定實體與實體之間的關係
如果是一對一的關係,則使用@OneToOne
2) 確定表結構的設計
l 如果是外來鍵關聯,在關係維護端考慮預設的實體關係對映或配合@JoinColumn;
l 如果表位於不同的資料中,可以採用主鍵關聯,使用@PrimaryKeyJoinColumn;
3) 確定實體關係的方向
l 單向關聯
在儲存關聯關係的實體中,使用@JoinColumn;
l 雙向關聯
若為雙向關聯,則在儲存關聯關係(也即存在外來鍵)的實體中,要配合@JoinColumn;在沒有儲存關聯關係的實體中,要使用mappedBy屬性明確所關聯的實體。
mappedBy用在雙向關聯中,mappedBy所在的實體為關係被維護端,而另一個實體為關係維護端(也即儲存關聯關係的一端)。
2、一對多
2.1、單向關聯
@Entity
@Table(name="T_SCHOOL")
public class School implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
private Stringname;
public School()
{
}
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(intid)
{
this.id =id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name =name;
}
/*
* ...
**/
}
@Entity
@Table(name="T_STUDENT")
public class Student implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
private Schoolschool;
public Student(){}
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(intid)
{
this.id =id;
}
@ManyToOne
@JoinColumn(name="school_id")
public SchoolgetSchool()
{
return school;
}
public void setSchool(School school)
{
this.school =school;
}
/*
* ...
**/
}
2.2、雙向關聯
@Entity
@Table(name="T_SCHOOL")
public class School implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
private Stringname;
privateSet<Student>students=new HashSet<Student>();
public School()
{
}
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(intid)
{
this.id =id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name =name;
}
@OneToMany(mappedBy="school", cascade=CascadeType.ALL)
publicSet<Student> getStudents()
{
return students;
}
public void setStudents(Set<Student> students)
{
this.students =students;
}
/*
* ...
**/
}
@Entity
@Table(name="T_STUDENT")
public class Student implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
private Schoolschool;
public Student(){}
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(intid)
{
this.id =id;
}
@ManyToOne(optional=false)
@JoinColumn(name="school_id")
public SchoolgetSchool()
{
return school;
}
public void setSchool(School school)
{
this.school =school;
}
/*
* ...
**/
}
l cascade:定義類和類之間的級聯關係。級聯關係定義對當前物件的操作將波及到關聯類的物件,而且這種關係是遞迴呼叫的。如School與Student有級聯刪除關係,那麼刪除School時將同時刪除關聯的Student物件。如果Student還和其他物件之間有級聯刪除關係,那麼這樣的操作會一直遞迴執行下去。級聯操作只有在persist、remove、refresh、merge方法呼叫時才會發生。
l option:指定級聯方是否可以為空,預設為true,允許外來鍵欄位為空。若將其設定為false,則要求雙發必須存在,也即外來鍵欄位不能為空。optional屬性將影響關聯查詢的形式。例如通過find、getReference方法查詢Student實體,當optional=false時,Student與School關聯為innerjoin;當optional=true時,Student與School關聯形式為leftjoin。
2.3、表關聯(雙向)
在一對多或多對一的關聯關係中,除了使用預設的外來鍵關聯外;還可以使用表關聯,多的一方可以作為關係表的主鍵(唯一性約束),而一的一方可以作為關係表另一個欄位,如下
@Entity
@Table(name="T_SCHOOL")
public class School implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
private Stringname;
privateSet<Student>students=new HashSet<Student>();
public School()
{
}
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(intid)
{
this.id =id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name =name;
}
@OneToMany(mappedBy="school")
public Set<Student>getStudents()
{
return students;
}
public void setStudents(Set<Student> students)
{
this.students =students;
}
/*
* ...
**/
}
@Entity
@Table(name="T_STUDENT")
public class Student implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
private Schoolschool;
public Student(){}
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(intid)
{
this.id =id;
}
@ManyToOne
@JoinTable(name="SCHOOL_STUDENT",
joinColumns={
@JoinColumn(name="student_id",referencedColumnName="id")
},
inverseJoinColumns={
@JoinColumn(name="school_id",referencedColumnName="id")
})
public SchoolgetSchool()
{
return school;
}
public void setSchool(School school)
{
this.school =school;
}
/*
* ...
**/
}
l name:中間表的名稱;
l joinColumns:中間表中,指向關係維護端的外來鍵;
l inverseJoinColumns:與joinColumns相似,指向中間表中關係被維護段的外來鍵;
2.4、總結
1)確定實體與實體之間的關係
如果是一對多的關係,則使用@OneToMany;如果是多對一的關係,使用@manyToOne。
2)確定表結構的設計
l 如果是外來鍵關聯,在關係維護端考慮預設的實體關係對映或配合@JoinColumn;
l 如果是表關聯,則在關係維護端使用@JoinTable
3)確定實體關係的方向
l 單向關聯
一般情況下,多的一方為關係維護端;在儲存關聯關係的實體中,使用@JoinColumn或@JoinTable;
l 雙向關聯
若為雙向關聯,則在關係維護端,配合@JoinColumn或@JoinTable;在關係被維護端,要使用mappedBy屬性明確所關聯的實體。
3、多對多
3.1、單向關聯
@Entity
@Table(name="T_TEACHER")
public class Teacher implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
private Set<Student>students=new HashSet<Student>();
public Teacher()
{
}
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(intid)
{
this.id =id;
}
@ManyToMany(fetch=FetchType.LAZY)
@JoinTable(name="TEACHER_STUDENT",
joinColumns={@JoinColumn(name="teacher_id")},
inverseJoinColumns={@JoinColumn(name="student_id")})
publicSet<Student> getStudents()
{
return students;
}
public void setStudents(Set<Student> students)
{
this.students =students;
}
/*
* ...
**/
}
@Entity
@Table(name="T_STUDENT")
public class Student implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
public Student(){}
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(intid)
{
this.id =id;
}
/*
* ...
**/
}
3.2、雙向關聯
@Entity
@Table(name="T_TEACHER")
public class Teacher implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
private Set<Student>students=new HashSet<Student>();
public Teacher()
{
}
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(intid)
{
this.id =id;
}
@ManyToMany(fetch=FetchType.LAZY)
@JoinTable(name="TEACHER_STUDENT",
joinColumns={@JoinColumn(name="teacher_id")},
inverseJoinColumns={@JoinColumn(name="student_id")})
publicSet<Student> getStudents()
{
return students;
}
public void setStudents(Set<Student> students)
{
this.students =students;
}
/*
* ...
**/
}
@Entity
@Table(name="T_STUDENT")
public class Student implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
privateHashSet<Teacher>teachers=new HashSet<Teacher>();
public Student(){}
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(intid)
{
this.id =id;
}
@ManyToMany(mappedBy="students",fetch=FetchType.LAZY)
publicHashSet<Teacher> getTeachers()
{
return teachers;
}
public void setTeachers(HashSet<Teacher> teachers)
{
this.teachers =teachers;
}
/*
* ...
**/
}
3.3、總結
確定實體關係的方向
l 單向關聯
在關係維護端使用@JoinTable;
l 雙向關聯
若為雙向關聯,則在關係維護端,使用@JoinTable;在關係被維護端,要使用mappedBy屬性明確所關聯的實體。
4、集合類
多的一方,在選用集合類時,需要注意一下幾點:
1) 在未確定使用Set還是List時,可以採用Collection來宣告;
2) Set集合中的物件不能有重複,並且是無序的;
3) List集合中的物件可以有重複,並且可以排序,可以結合@OrderBy;
4) Map集合是key和value的集合,如果使用Map集合需要配合@MapKey來指定map中的key值。