Hibernate關聯就是将關聯映射到資料庫,所謂關聯關系就是對象模型在記憶體中的一個或多個引用。
先看兩個關系實體類: 都提供set/get方法
public class Order {
private Integer orderId;
private String orderNo;
private List<OrderItem> orderItems = new ArrayList<>();
private Integer initChildren =0; //0代表懶加載 預設為0
}
public class OrderItem {
private Integer orderItemId;
private Integer productId;
private Integer quantity;
private Integer oid;
private Order order;
}
下面介紹幾種單向的關聯關系。
文章目錄
- 一、單向N-1
- 二、單向的1-1
- 三、單向的1-N
- 四、單向N-N
- 懶加載
- 删除對應資料
- hibernate架構一對多的執行原理
一、單向N-1
所謂的單向1-N,也就是一個a實體類中有可以有多個b實體類的關系。
xml配置:
<many-to-one name="orderItem" column="productId"/>
二、單向的1-1
這種情況也是使用many-to-one,
不過在标簽中多加一個unique=true這個屬性,
這個屬性代表的是限制了多的一端的多重性唯一。
通過這種手段可以映射一對一唯一外鍵關聯。
xml配置:
<many-to-one name="address" column="oid" unique="true"/>
三、單向的1-N
一對多關聯映射,在多的一端添加一個外鍵指向一的一端,它維護的關系是一指向多。
xml配置:
<!--
bag标簽:
lazy:是否懶加載,預設是懶加載
name:類的關聯屬性名
cascade:級聯關系 級聯新增與修改
inverse:關聯關系交給對方控制,預設是true 意思就是目前類不維護關聯關系
子标簽key:
column:主表的主鍵,從表的外鍵。一般寫從表的外鍵
子标簽one-to-many:
class:外鍵對應的實體類
-->
<bag lazy="true" name="orderItems" cascade="save-update" inverse="true">
<key column="oid"></key>
<one-to-many class="com.zlk.three.entity.OrderItem"/>
</bag>
在這裡有兩個選擇,可以選擇bag标簽也可以使用set标簽,
bag是一個無序的集合,它可以包含重複的元素,相當于java的java.util.List。
set類似于bag,但它隻能存儲唯一對象,若元素相同,則新的會替換掉舊的元素。類似Java中的java.util.Set。
四、單向N-N
兩個實體類:
public class User {
private int id;
private String name;
private Set roles;
}
public class Role {
private int id;
private String name;
}
現在需要映射這樣的N-N關系,一個User可以有多個Role,而一個Role有可以被多個User所擁有。
這樣我們就可以将一個N-N關系拆分為兩個N-1的關系。
xml配置:
<bag name="roles" table="t_user_role">
<key column="userid"/>
<many-to-many class="xxx.Role" column="roleid"/>
</bag>
參考在這,想了解更多關系,請參考如何了解hibernate關聯關系。
懶加載
四種模式已經介紹完了,在這裡我先着重講解一下1-N。
在1-N的xml配置中:
<bag lazy="true" name="orderItems" cascade="save-update" inverse="true">
<key column="oid"></key>
<one-to-many class="com.zlk.three.entity.OrderItem"/>
</bag>
上面介紹過lazy是懶模式。他預設是true,那什麼是懶模式呢?
當lazy= false 的時候,會讓hibernate執行完兩次操作,session才會關閉,
當lazy= true 的時候,會讓hibernate執行完一次操作,session就會關閉
為什麼要預設true呢?
出于性能的考慮,是以hibarnate3.0出現lazy這個屬性,并讓他預設等于true,也就是不加載關聯屬性。
可以使用JUnit測試一下,
@Test
public void testGetOrder() {
Order order = new Order();
order.setOrderId(1);
Order o = this.demoDao.getOrder(order );
List<OrderItem> orderItems = o.getOrderItems();
for (OrderItem orderItem : orderItems) {
System.out.println(orderItem);
}
System.out.println(o);
}
當lazy=false時候,他會正常運作,并列印出資料。
當lazy=true時候,他會報no-Session錯,這是因為允許了懶加載,隻執行了一次操作,是以 List orderItems = o.getOrderItems();這裡面是沒有值的,
但如果我隻是單純的寫要查出所有的訂單那怎麼辦呢?
這裡就運用到了上面實體類中的private Integer initChildren =0; //0代表懶加載 預設為0。
通過這個屬性來控制是否強制加載關聯對象。
public Order getOrder(Order order) {
Session session = SessionFactoryUtils.openSession();
Transaction transaction = session.beginTransaction();
Order o = session.get(Order.class, order.getOrderId());
if(o != null && new Integer(1).equals(order.getInitChildren())) {
//強制加載關聯對象
Hibernate.initialize(o.getOrderItems());
}
transaction.commit();
session.close();
return o;
}
這樣就可以使用懶加載檢視所有訂單了。
删除對應資料
若你想删除訂單的時候,這時候不能直接删除訂單,因為你的從表OrderItem裡面還是有資料的,是以你必須先删除從表裡面的值,才可以删除order。
方法如下:
public void delOrder(Order order) {
Session session = SessionFactoryUtils.openSession();
Transaction transaction = session.beginTransaction();
Order order2 = session.get(Order.class, order.getOrderId());
for (OrderItem oi : order2.getOrderItems()) {
session.delete(oi);
}
session.delete(order2);
transaction.commit();
session.close();
}
hibernate架構一對多的執行原理
hibernate架構一對多的執行原理:
1,對hibernate.cfg.xml進行模組化,等到sessionfactory對象
2,并且拿到mapping resourse裡的内容
3,拿到order.hbm.xml配置檔案
4,可以再次模組化,拿到實體類屬性和對應的資料庫表列段
5,生成動态的sql:select * 表名
執行sql最終得到meterData原資料模型
6,通過實體類進行反射 Class.forName(“com.zlk.three.entity.Order”).newInstance(0);
獲得所有資料庫内的資料。
最終得到List,且裡面都有值(到這裡隻是處理了表裡面的非外鍵列段)
7,處理關聯關系 :通過标簽獲得orderItems(所有訂單) oid 和訂單實體類com.zlk.three.entity.OrderItem
然後通過one-to-many 裡的 class(也就是com.zlk.three.entity.OrderItem),就可獲得這個實體類的xml,
再通過這個xml獲得對應的資料庫表。
8,與第五步相同,最終獲得了一個list
9,給order的關聯關系屬性指派
就是在第六步中獲得List時,一個一個的加進去:
for(Order o:List){
o.setOrderItems(List );
}