關係映射:即需要映射的屬性,是一個實例對象.如員工的部門屬性.
單向多對一(常用)
MySQL的表格
員工類中的屬性
private Department dept;//dept_id
- 屬性的映射
- 由員工管理,在員工的映射文件設置
<many-to-one name="dept" column="dept_id"/>
案例1
有一個員工A屬於部門B,兩者都沒有存入數據庫
情況1:session.save(員工);
- 結果:報錯
- 原因:保存了員工後,沒有找到外鍵對應的部門
情況2:session.save(先部門,再員工)
- 結果:正常發送兩個insert的SQL
情況3:session.save(先員工,再部門)
- 結果:1.保存員工;2.保存部門,3.更新員工
- 原因:保存員工爲持久化後,但其dept屬性爲臨時狀態,不受保存.保存部門後,dept又變成了持久化狀態,導致員工狀態變成髒的持久化狀態,需要更新.
案例2
Employee e = (Employee)session.get(Employee.class, 1L);
Department dept = e.getDept();
System.out.println("_____________");
System.out.println(e); //使用了dept屬性
結果:
- 查員工的SQL
- 橫線
- 查部門
- 輸出員工
原因:e.getDept();
取外鍵的對象,返回的是代理對象(延遲加載);使用前纔會實例化.
外鍵的代理對比load()的代理
- load();返回的代理對象絕對不爲null,不可以用代理對象==null來判斷
- 外鍵的代理:返回的代理對象,可能爲空,因爲查詢e時候,如果dept_id爲null,就返回null,非null,就返回代理對象.代理對象==null可以用來判斷
單向多對多(常用)
MySQL的表格(必有中間表)
Teacher類中的屬性
- 由teacher管理student
private Set<Student> stus;//Teacher_Student Teacher_id/Student_id
- 屬性的映射
<set name="stus" table="Teacher_Student" order-by="Student_id">
<key column="Teacher_id"/>
<many-to-many class="Student" column="Student_id"/>
</set>
<set name="屬性名" table="中間表名">
<key column="對應己方的外鍵列"/>
<many-to-many class="他方的類" column="對應他方的外鍵列"/>
</set>
<!--set換成bag也會按id排序,bag對應的是list-->
<set name="stus" table="Teacher_Student" >
<key column="Teacher_id"/>
<many-to-many class="Student" column="Student_id"/>
</set>
//擴展按添加順序排列
1.在Student類中添加記錄添加順序的屬性並且聲明
private Integer sequence;
2.在Teacher映射文件中的order-by="sequence"
單向一對多(常用)
外鍵依然標記在員工表的dept_id,只不過要將部門類中添加一個屬性:
private Set<Employee> emps;//dept_id
屬性的映射
<set name="emps">
<key column="dept_id"/>
<one-to-many class="Employee"/>
</set>
案例1
員工A所屬部門B,兩者都存入db
情況1.session.save(先部門,後員工)
- 結果:三條SQL;保存部門,保存員工,更新員工;
情況2.session.save(先員工,後部門)
- 結果:三條SQL;保存員工,保存部門,更新員工;
情況3.session.save(部門)
//單獨保存部門(部門中有爲保存的員工)會失敗
- 結果:2條SQL;保存部門,更新員工因找不到而報錯;
案例2
Department dept=(Department)session.get(Department.class,1L);
Set<Employee> emps = dept.getEmps();//返回代理對象
System.out.println("__________");
System.out.println(dept);//調用
結果:
- 查部門的sql
- 橫線
- 查 員工的sql
- 輸出部門
原因:dept.getEmps();
返回的是代理對象(延遲加載);使用前纔會實例化.該代理對象也是決不爲空,於load()一樣.要用.size()方法來判斷是否爲空.
級聯
可以通過one方來控制many方,<set cascade="XXX">
save-update:
- session.save(主對象):同時保存其集合中的從對象
- session.update(主對象):1.去持久化新增的從對象;2.修改遊離的從對象;3.將已經丟出集合的從對象,其對應的外鍵設爲null;
delete: 刪除主對象,同時刪除其集合中的從對象(在表中刪除)
- all:save-update+delete
- delete-orphan:1.將外鍵設爲null的從對象,從表中刪除.2.將從集合中剔除的從對象,從表中刪除.3刪除主對象,也雙光從對象.
- all-delete-orphan:上面的全部.訂單就要用這個.
雙向=一對多+多對一(常用)
數據庫的表格不用,一樣是員工表格有外鍵dept_id
更改映射文件
<!--員工-->
<class name="Employee">
<many-to-one name="dept" column="dept_id"/>
</class>
<!--部門-->
<class name="Department" inverse="true">
<set name="emps">
<key column="dept_id"/>
<one-to-many class="Employee"/>
</set>
</class>
inverse=”true”: 表示部門放棄對關係的管理,一切由員工的映射管理.
true:刪除部門,同時會刪對應的員工;而且會出現多餘的SQL來維護二者關係
false:刪除部門,只會將員工的外鍵爲null
一對一(不常用)
QQ號對應QQ空間,可以使用QQ號的主鍵,作爲外鍵指向QQ空間的主鍵;
即QQ空間的主鍵生成,是跟隨QQ號的主鍵
<!--QQ號-->
<one-to-one name="zone">
<!--QQ空間-->
<!--聲明主鍵生成方式依賴別的表的主鍵-->
<id name="id">
<generator class="foregn">
<param name="property">類中的QQ號屬性名</param>
</generator >
</id>
<!--constained="true",表示被控制的-->
<one-to-one name="類中的QQ號屬性名" constained="true">
組件關係
多個類,對應一個表,類與類是組件關係
類中的實例對象屬性,就是多類中的其他屬性進行一次包裝,組件對象沒有對應的表格
public class 公司{
private Long id;
private String name;
private Address 營業地址; //組件1
private Address 註冊; //組件2
}
public class Address {
private String 省;
private String 城;
private String 街;
}
公司類的映射文件
<class name="公司">
<id></id>
<property></property>
<!--組件-->
<component name="營業地址">
<property name="省" column="表中的列1">
`
`
</component>
<!--組件-->
<component name="註冊地址">
<property name="省" column="表中的列2">
`
`
</component>
</class>
繼承關係
多個類,對應一個表,類與類是繼承關係
映射文件(映射的類用父類!)
<class name="商品" discriminator-value="1">
<id> </id>
<property name="XX">
<!--聲明表中區別的列名-->
<discriminator column="type" type="int">
<!--添加子類-->
<subclass name="書" discriminator-value="2">
<property name="作者"/>
</subclass >
<subclass name="衣" discriminator-value="3">
<property name="顏色"/>
</subclass >
</class>
此時 session.get(衣服.class,1L);
sql爲 wher id=1 and type = 3;