1. 程式人生 > >JPA實體關係對映

JPA實體關係對映

實體關係是指實體與實體之間的關係,從方向上分為單向關聯和雙向關聯,從實體數量上分為一對一、一對多、多對多等。對於任何兩個實體,都要從這兩個方面區分它們之間的關係。

單向關聯是一個實體中引用了另外一個實體,也即通過一個實體可以獲取另一個實體物件的引用;雙向關聯是兩個實體之間可以相互獲取對方物件的引用。

1、一對一

學生和身份證是一對一的關係,下面將從單向關聯和雙向關聯來分析。

1.1、單向關聯

假設在學生實體中可以獲取身份證物件的引用;反之,不能在身份證實體中獲取學生物件的引用,則學生和身份證是一對一的單向關聯關係,關係對映如下:

@Entity

@Table(name="t_IDCard")

public

class IDCard implements Serializable

{

   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值。