什麼是Hibernate中的關聯映射?
簡單來說Hibernate是ORM映射的持久層框架,全稱是(Object Relational Mapping),即對象關系映射。
它將數據庫中的表映射成對應的對象,以對象的形式展現,這樣我們就可以通過映射的對象來對數據庫中的數據進行間接的操作。
關聯映射是將數據庫中的表映射成與之相對應的對象,當你對這個對象進行操作的時候,Hibernate會對數據庫中對應的表執行相應的操作,你對該實體的操作實際上就是在間接的操作數據庫中與之相對應的表。
Hibernate正是實現了這種思想,達到了方便開發人員以面向對象的思想來實現對數據庫的操作。
Hibernate主要實現的映射關系:
Hibernate映射的基本結構
hibernate在實現ORM功能的時候主要用到的文件有:映射類(*.Java)、映射文件(*.hbm.xml)和數據庫配置文件(*.properties/*.cfg.xml),它們各自的作用如下。
映射類(*.java):它是描述數據庫表的結構,表中的字段在類中被描述成屬性,將來就可以實現把表中的記錄映射成為該類的對象了。
映射文件(*.hbm.xml):它是指定數據庫表和映射類之間的關系,包括映射類和數據庫表的對應關系、表字段和類屬性類型的對應關系以及表字段和類屬性名稱的對應關系等。
數據庫配置文件(*.properties/*.cfg.xml):它是指定與數據庫連接時需要的連接信息,比如連接哪種數據庫、登錄數據庫的用戶名、登錄密碼以及連接字符串等。當然還可以把映射類的地址映射信息放在這裡。
hibernate中的關聯關系有四種:一對一、一對多、多對一、多對多。
關聯關系中又分為單向關聯與雙向關聯
單向關聯:單向關聯是指只有一方有另一方的關聯信息而另一方沒有關聯信息
例:
A——>B
A對象中有B對象的關聯信息
B對象中沒有A對象的關聯信息
我們可以通過A對象中B的關聯信息查詢或修改B對象的信息但無法通過B對象來查詢修改A對象的信息
同理A<——B也是單向關聯
這種只是單方面的關聯我們稱為單向關聯
雙向關聯:雙向關聯是指兩方都有另一方的關聯信息
例:
A<——>B
A對象中有B對象的關聯信息
B對象中也有A對象的關聯信息
我們可以通過A對象中B的關聯信息查詢或修改B對象的信息也可以通過B對象來查詢修改A對象的信息
這種兩方都有另一方的關聯信息我們稱為雙向關聯
單向關聯一般在一方配置多方不進行配置
如:一對多 單向關聯在“一”的一方配置文件裡進行配置,"多"的一方不進行配置
雙向關聯兩方都要配置
如:一對多 雙向關聯在“一”的一方配置文件裡需要配置,“多”的一方也需要進行配置
通過下面的代碼會慢慢了解
下面我們來詳細了解一下一對一、一對多、多對一、多對多的單向和雙向關聯:
一對一關聯映射
一對一關聯:一對一是指一個對象對應一個對象 如:一個人只有一個身份證。
在兩個數據表之間的一對一關系可以有兩種實現方法,其中一種就是通過兩個表的主鍵相關聯,另一種是通過外鍵相關聯
如:一個人(Person)對應一個地址(Address)代碼如下。
一對一主鍵單向關聯:
Person——>Address
public class Person { private int personid; private String name; private int age; //在Person對象中有Address對象的關聯信息 private Address address; public class Address{ //Address對象中沒有Person對象的關聯信息 private int addressid; private String addressdetail;
這種單方面有另一個對象的關聯信息的時候我們稱為單向關聯,再來看一下兩個表中的映射hbm.xml文件:
Person.hbm.xml
<hibernate-mapping> <class name="com.entity.Person" table="PERSON"> <id name="personid" column="presonid"> <!--基於主鍵關聯時,主鍵生成策略是foreign,表明根據關聯類生成主鍵--> <generator class="foreign"> <!--關聯持久化類的屬性名--> <param name="property">address</param> </generator> </id> <property name="name"/> <property name="age"/> <!--constrained設定為true,表示的主鍵必須與Person中對應資料的主鍵相同。--> <one-to-one name="address" constrained="true"/> </class> </hibernate-mapping>
單向關聯和雙向關聯的區別主要在於單向只在一方配置而雙向兩方都要配置
Address.hbm.xml
因為是單方面關聯所以只在Person.hbm.xml中配置了關聯信息而Address.hbm.xml中不做任何配置
所以我們省略Address.hbm.xml
…………
知道了一對一的單向關聯,我們再來了解一下一對一的雙向關聯,雙向關聯結合上面的知識可能聰明的小伙伴已經想到了。下面我們來看一下一對一的雙向關聯
一對一主鍵雙向關聯:
Person<——>Address
public class Person implements java.io.Serializable { private Long id; private String name; //雙向關聯中Person對象中有Adderss對象的關聯信息 private Address address; public class Address implements java.io.Serializable { private Long id; //Adderss對象中也有Person對象的關聯信息 private Person person; private String detail;
這種兩方面都有另一個對象的關聯信息的時候我們稱為雙向關聯,再來看一下兩個表中的映射hbm.xml文件:
Person.hbm.xml
<hibernate-mapping> <class name="entity.Person" table="person"> <id name="id" type="java.lang.Long"> <column name="id" /> <generator class="identity" /> </id> <property name="name" type="java.lang.String"> <column name="name" length="24" not-null="true"> <comment>姓名</comment> </column> </property> <one-to-one name="address"/> </class> </hibernate-mapping>
單向關聯和雙向關聯的區別主要在於單向只在一方配置而雙向兩方都要配置
Address.hbm.xml
<hibernate-mapping> <class name="entity.Address" table="address" catalog="mydb"> <id name="id" type="java.lang.Long"> <column name="id" /> <!-- class="foreign": 一對一主鍵映射中,使用另外一個相關聯的對象的標識符 --> <generator class="foreign"> <param name="property">person</param> </generator> </id> <property name="detail" type="java.lang.String"> <column name="detail" length="120" not-null="true"> <comment>詳細地址</comment> </column> </property> <!-- 表示在address表存在一個外鍵約束,外鍵參考相關聯的表person --> <one-to-one name="person" constrained="true" /> </class> </hibernate-mapping>
當我們操作Person對象時,可以對Address對象進行操作,也可以操作Address對象時對Person對象進行操作這樣就形成了雙向的關聯
雙向關聯還需要在hibernate.cfg.xml中進行配置
<hibernate-configuration> <session-factory> <property name="connection.username">root</property> <property name="connection.url"> jdbc:mysql://localhost:3306/testdb </property> <property name="dialect"> org.hibernate.dialect.MySQLDialect </property> <property name="connection.password">xiaohui</property> <property name="connection.driver_class"> com.mysql.jdbc.Driver </property> <property name="show_sql">true</property> <property name="format_sql">true</property>
<!--在hibernate.cfg.xml中配置hbm.xml文件--> <mapping resource="com/entity/Person.hbm.xml" /> <mapping resource="com/entity/Address.hbm.xml" /> </session-factory> </hibernate-configuration>
我們再來看一下一對一的外鍵關聯
一對一外鍵單向關聯:
Person——>Address
public class Person { private int personid; private String name; private int age; private Address address; public class Address{ private int addressid; private String addressdetail;
雙向和單向關聯大家應該已經了解了 這裡就不多做介紹了直接上代碼:
Address.hbm.xml
<!--address中不做任何配置所以我們省略-->
…………………………
<!--單向關聯和雙向關聯的區別在於單向關聯只在一方配置雙向關聯兩方都要配置--> Person.hbm.xml <hibernate-mapping> <class name="com.entity.Person" table="PERSON"> <id name="personid"> <generator class="identity"/> </id> <property name="name"/> <property name="age"/> <!--用來映射關聯PO column是Address在該表中的外鍵列名,增加unique變成唯一的--> <many-to-one name="address" unique="true"/> </class> </hibernate-mapping>
外鍵關聯和主鍵關聯不同的地方是采用<many-to-one>標簽來映射,一對一唯一外鍵關聯映射其實是多對一的特例。<many-to-one>指定多的一端unique為true,這樣就限制了多的一端的多重性為一,就是這樣來映射的。
一對一外鍵雙向關聯:
Person<——>Address
public class Person implements java.io.Serializable { private Long id; private String name; private Address address; public class Address implements java.io.Serializable { private Long id; private Person person; private String detail;
Person.hbm.xml <hibernate-mapping> <class name="com.entity.Person" table="person"> <id name="personid" type="java.lang.Long"> <column name="personid" /> <generator class="identity" /> </id> <property name="name" type="java.lang.String"> <column name="name" length="24" not-null="true"> <comment>姓名</comment> </column> </property>
<!--雙向關聯配置--> <one-to-one name="address" /> </class> </hibernate-mapping> Address.hbm.xml <hibernate-mapping> <class name="com.entity.Address" table="address" catalog="testdb"> <id name="addressid" type="java.lang.Long"> <column name="addressid" /> <generator class="identity" /> </id> <property name="detail" type="java.lang.String"> <column name="detail" length="120" not-null="true"> <comment>詳細地址</comment> </column> </property> <many-to-one name="person" class="entity.Person" unique="true"> <column name="personid"> <comment>人的ID</comment> </column> </many-to-one> </class> </hibernate-mapping>
單向關聯和雙向關聯的區別主要在於單向只在一方配置而雙向兩方都要配置所以一對一雙向關聯比單向關聯多了一個在Person.hbm.xml文件中配置<one-to-one name="address" />
雙向關聯還需要在hibernate.cfg.xml中進行配置
<hibernate-configuration> <session-factory> <property name="connection.username">root</property> <property name="connection.url"> jdbc:mysql://localhost:3306/testdb </property> <property name="dialect"> org.hibernate.dialect.MySQLDialect </property> <property name="connection.password">xiaohui</property> <property name="connection.driver_class"> com.mysql.jdbc.Driver </property> <property name="show_sql">true</property> <property name="format_sql">true</property> <!--在hibernate.cfg.xml中配置hbm.xml文件--> <mapping resource="com/entity/Person.hbm.xml" /> <mapping resource="com/entity/Address.hbm.xml" /> </session-factory> </hibernate-configuration>
注意:因為一對一的主鍵關聯映射擴展性不好,當我們的需要發生改變想要將其變為一對多的時候變無法操作了,所以我們遇到一對一關聯的時候經常會采用唯一外鍵關聯來解決問題,而很少使用一對一主鍵關聯。
一對多關聯映射
一對多關聯:一對多是指一個對象對應多個對象 同樣也分為單向關聯和雙向關聯 如:一個教室可以有多個學生
一對多單向關聯:
Classes——>Student
public class Classes { private int id; private String name; //Set支持延遲加載因為多個學生所以我們用Set集合關聯 private Set students; }
public class Student { private int id; private String name; }
單向關聯只需在一方配置hbm.xml文件Student不需要配置所以就省略了
Classes對象中使用了set屬性,但是只是說明了延遲加載的屬性,並沒有為屬性配置對應的對象,屬性的對象是要在映射文件中來配置的,需要添加set標簽,並在set標簽中添加<one-to-many>標簽,具體如下代碼:
Classes.hbm.xml
<hibernate-mapping> <class name="com.hibernate.Classes" table="t_classes"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <set name="students"> <key column="classesid"></key> <one-to-many class="com.hibernate.Student"></one-to-many> </set> </class> </hibernate-mapping>
因為Classes一方是一方對應的Student是多方 所以我們要用<set>來關聯一方
Student.hbm.xml不做任何改變
省略………………
一對多雙向關聯:
Classes<——>Student
public class Classes { private int id; private String name; //Set支持延遲加載 private Set<Student> students; } public class Student { private int id; private String name; //添加class對象關聯信息因為是一方所以我們用一個對象關聯 private Classes classes; }
Classes.hbm.xml
因為與單向一對多配置一樣所以就省略了 可以參考上面單向一對多的代碼
Student.hbm.xml
雙向我們需要兩方都要配置代碼如下:
<hibernate-mapping> <class name="com.hibernate.Student" table="t_student"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <!-- 在多的一端Student中添加一行新的Classes列 ,並且列的名稱要和Classes.hbm.xml的列明相同--> <many-to-one name="classes" column="classesid"></many-to-one> </class> </hibernate-mapping>
因為Student一方是多方對應的Classes是一方 所以我們要用<many-to-one>來關聯一方
多對多關聯映射
多對多關聯:多對多關聯是指多個對象對應多個對象 如:老師可以有多個學生,學生也可以有多個老師
多對多單向關聯:
Teacher——>Student
public class Teacher { private int id; private String name; private Set<Student> students = new HashSet<Student>(); } public class Student { private int id; private String name; private String title; }
Teacher.hbm.xml
<hibernate-mapping> <class name="com.hibernate.Teacher" table="t_teacher"> <id name="id"> <generator class="native"/> </id> <property name="name"/>
<!--生成一張新表存放兩個關聯對象的ID--> <set name="students" table="Teacher_Sutdent">
<!--將Teacher表的外鍵關聯 注意不是對象的屬性是表中的字段--> <key column="teacher_id"></key>
<!--將Student表的外鍵關聯 注意不是對象的屬性是表中的字段-->
<many-to-many class="com.hibernate.Student" column="student_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
文件中要使用<many-to-many>標簽,並且在標簽中添加上對應的列關系,因為你要讓兩個對象中都要清楚它們之間的映射是如何使用的,並且在生成的關系表中哪一列是對應的自己的外鍵,所以要在該標簽中指明,另外在<set>標簽中添加table屬性會指明要生成新表,下面的示例中添加了t_user_role,所以會生成新的關聯表。
Student.hbm.xml不做任何配置所以省略
…………
多對多雙向關聯:
Teacher<——>Student
public class Teacher { private int id; private String name; private Set<Student> students = new HashSet<Student>(); } public class Student { private int id; private String name; private String title; private Set<Teacher> teachers = new HashSet<Teacher>(); }
Teacher.hbm.xml同單向多對多一樣故省略
…………
Student.hbm.xml
<hibernate-mapping> <class name="com.hibernate.Student" table="t_student"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <!--生成一張新表存放兩個表的Id--> <set name="teachers" table="Teacher_Student"> <!--將Teacher表的外鍵關聯 注意不是對象的屬性是表中的字段--> <key column="student_id"></key> <!--將Student表的外鍵關聯 注意不是對象的屬性是表中的字段--> <many-to-many class="com.hibernate.Teacher" column="teacher_id"></many-to-many> </set> </class> </hibernate-mapping>
多對一關聯
對比一對一關聯映射和多對一唯一外鍵關聯映射,其實它們兩個都是使用了<many-to-one>本質上都是外鍵約束,只不過一對一的是唯一映射,需要添加unique="true"的屬性,其它的它們兩個是相同的。
多對一關聯:多對一關聯是指多個對象對應一個對象 如:多個員工對應一個部門
多對一單向關聯:
public class Department { private int id; private String name; } public class Employee { private int id; private String name; private Department depart;//注意這裡是以部門的對象來作為員工的屬性的,這個思想很關鍵,是建立起部門和員工關聯的關鍵 }
Department.hbm.xml不做任何配置故省略
…………
Employee.hbm.xml
<hibernate-mapping package="com.suo.domain"> <class name="Employee"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <many-to-one name="depart"></many-to-one> <!-- many-to-one指明了外鍵 ,會根據反射機制,找到要和Employee建立多對一關系的類,該列默認的是可以為空的--> </class> </hibernate-mapping>
多對一雙向關聯:
public class Department { private int id; private String name; private Set<Employee> emps;//用集合來存儲員工 } public class Employee { private int id; private String name; private Department depart;//注意這裡是以部門的對象來作為員工的屬性的,這個思想很關鍵,是建立起部門和員工關聯的關鍵 }
Departement .hbm.xml
<hibernate-mapping package="com.suo.domain"> <class name="Department"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <set name="emps"> <key column="depart_id"/><!-- key指明了員工表中的外鍵--> <one-to-many class="Employee"/><!-- one-to-many指明了和哪個類進行一對多的映射 --> </set> <!-- 用set標簽表示Department中的員工集合的屬性,這個屬性並沒有映射到數據庫中的部門表中, 即部門表中,並沒有emps這樣的一個列。 --> </class> </hibernate-mapping>
Employee.hbm.xml同單向關聯配置相同故省略
…………
級聯操作 Cascade:
一.簡單的介紹
cascade和inverse (Employee – Department)
l Casade用來說明當對主對象進行某種操作時是否對其關聯的從對象也作類似的操作,常用的cascade:
none,all,save-update,delete, lock,refresh,evict,replicate,persist,
merge,delete-orphan(one-to-many)。
一般對many-to-one,many-to-many不設置級聯,
在<one-to-one>和<one-to-many>中設置級聯。
l inverse表“是否放棄維護關聯關系”(在Java裡兩個對象產生關聯時,對數據庫表的影響),在one-to-many和many-to-many的集合定義中使用,inverse=”true”表示該對象不維護關聯關系;
該屬性的值一般在使用有序集合時設置成false(注意hibernate的缺省值是false)。
one-to-many維護關聯關系就是更新外鍵。many-to-many維護關聯關系就是在中間表增減記錄。
注: 配置成one-to-one的對象不維護關聯關系
二,屬性的解析
class元素的lazy屬性設定為true,表示延遲加載,如果lazy設為false,則表示立即加載。以下對這二點進行說明。
立即加載:表示Hibernate在從數據庫中取得數據組裝好一個對象(如學生1)後, 會立即再從數據庫取得數據組裝此對象所關聯的對象(如學生證1)。
延遲加載:表示Hibernate在從數據庫中取得數據組裝好一個對象(如學生1)後,不會立即再從數據庫中取得數據組裝此對象所關聯的對象(如學生1),而是等到需要時,才會從數據庫取得數據組裝此關聯對象。
<one-to-one>元素的cascade屬性表明操作是否從父對象級聯到被關聯的對象,
它的取得可以是以下幾種:
none:在保存,刪除或修改當前對象時,不對其附屬對象(關聯對象)進行級聯操作。它是默認值。
save-update:在保存,更新當前對象時,級聯保存,更新附屬對象(臨時對象,游離對象)。
delete:在刪除當前對象時,級聯刪除附屬對象。
all:所有情況下均進行級聯操作,即包含save-update和delete操作。
delete-orphan:刪除和當前對象解除關系的附屬對象。
<one-to-one>元素的fetch屬性的可選值是join和select,默認是select。當fetch屬性設定為join時,表示連接抓取(Join fetching):Hibernate通過在Select語句中使用outer join(外連接)來獲得對象的關聯實例或者關聯集合。
當fetch屬性設定為select時,表示查詢抓取(Select fetching):需要另外發送一條Select語句抓取當前對象的關聯實體或集合。
三。代碼練習
<set name="emps" cascade="save-update">
<key column="depart_id"/>
<one-to-many class="Employee"/>
</set>
<set name="students" table="taacher_student" inverse="true"><!-- table是用來指定中間表的屬性 -->
<key column="teacher_id"></key><!-- 查找教師id時,鏈接中間表表的teacher_id -->
<many-to-many class="Student" column="student_id"></many-to-many>
</set>
關系映射總結:
單向關聯
一對一主鍵關聯:單向關聯時我們需要在有關聯信息一方的配置文件裡加入<one-to-one constrained="true">並且將constrained屬性設置為true 表示的主鍵必須與這個對象中對應資料的主鍵相同
一對一外鍵關聯:單向關聯時我們需要在有關聯信息一方的配置文件裡加入<many-to-one unique="true">並且將unique屬性設置為true 表示這個主鍵是唯一的
一對多單向關聯:單向關聯時我們需要在有關聯信息一方的配置文件裡加入<set>在<set>中加入<one-to-many/>
<set>
<key column="關聯的外鍵">
<one-to-many/>
</set>
多對多單向關聯:單向關聯時我們需要在有關聯信息一方的配置文件裡加入<set>在<set 中生成一張新表用來存放兩個表的外鍵table="">中加入<key column="當前表的外鍵ID"><many-to-many clasee="關聯對象路徑" column="關聯對象表的Id">
<set table="">
<key column=""/>
<many-to-many class="" column="">
</set>
多對一單向關聯:單向關聯時我們需要在有關聯信息一方的配置文件裡加入<many-to-one>
雙向關聯
一對一主鍵關聯:在從表的一方加入<one-to-one constrained="true">還需要在主表加入<one-to-one>
一對一外鍵關聯:除了在從表中加入<many-to-one unique="true">也需要在主表加入<one-to-one>
一對多雙向關聯:除了在一方中加入<set><one-to-many></set>還需要在多放加入<many-to-one>
多對多雙向關聯:需要在兩方都加入<set><many-to-many></set> 注:<set>中的table="表名" 表明兩方的配置要一樣
<set name="關聯對象的屬性名" table="生成一張新表">
<key column="當前對象數據庫表的外鍵"/>
<many-to-many class="關聯對象的類路徑" column="關聯對象數據庫表的外鍵">
</set>
多對一雙向關聯:要在多方中加入<many-to-one>還要在一方中加入<set>
<set>
<key column="關聯外鍵"/>
<one-to-many>
</set>