hibernate的關聯、一對多(4)
-
什麼是關聯(association) 1.1 關聯指的是類之間的引用關係。如果類A與類B關聯,那麼被引用的類B將被定義為類A的屬性。例如:
public class A{ private B b = new B(); public A(){} }
1.2 關聯的分類:關聯可以分為一對一、一對多/多對一、多對多關聯 關聯是有方向的 一個訂單表對應多個訂單項,多個訂單項對應一個訂單表 訂單表、訂單項表
select * from t_order t,t_orderItem m where t.oid = m.oid and ..... class Order{ private STIRNG OID; ... private Set<OrderItem> OrderItems; } session.get(Order.class,1)
#關鍵點都在資料庫中的外來鍵上面,請好好理解下面這二句SQL和一對多及多對一的關係 #select * from Orders where cid=? //這條SQL返回客戶對應的0-n個訂單 #select * from Customers where customerId=? //這條SQL返回訂單對應的1個客戶 #通過這二條SQL不難看出,外來鍵在這裡有二個用途:查詢客戶的訂單,查詢訂單對應的客戶 2. 案例:如何建立客戶和訂單一對多雙向關聯 2.1 先不建立客戶和訂單的關聯關係,定義實體及對映檔案,單獨執行儲存操作 2.2 建立客戶到訂單的一對多關聯關係
2.3 建立訂單到客戶的多對一關聯關係
<many-to-one name="customer" class="entity.Customer" column="cid">
<!--1.註釋 2.只讀-->
<property name="cid" type="java.lang.Integer" column="cid" insert="false" update="false">
</property>
2.4 注意:在Hibernate當中定義實體物件的集合屬性時,只能使用介面而不能使用類
#insert屬性設定中主控方概念理解: 3. 以客戶和訂單的一對多雙向關聯為例,講解Set元素中的cascade|inverse|outter-join|lazy屬性作用 3.1 lazy:預設值為true,true延遲載入,false立即載入(一般設定為true,不使用立即載入,因為影響查詢效能) 3.2 outter-join:預設值為false,true使用左外聯接查詢關聯的(但一般不用,因為當我們把該屬性設定為true時,所有的查詢語句都會預設左外聯,那樣效能不高) 3.3 inverse:預設值為false,true表示將對方設定為主控方(一對多雙向關聯中一般將多方設定為主控方,這樣可以減少SQL語句的數量,減少多餘的操作) 3.4 cascade:用來控制如何操作關聯的持久化物件的 3.4.1 none:儲存,更新或刪除當前物件時,忽略其它關聯的物件 3.4.2 save-update:儲存、更新時級聯儲存所有的臨時物件,並且級聯更新關聯的遊離物件 3.4.3 delete:通過session的delete方法刪除當前物件,級聯刪除關聯的物件 3.4.4 all:等於save-update操作+delete操作
小結: 多方的CRUD與沒有建立關聯關係之前的操作一致 一方的CRUD與沒有建立關聯關係之前的操作有變化 D:刪除一方之前先刪除多方 C:級聯操作 R:程式碼控制多方
-
案例:選單物件的一對多雙向自關聯
-
如何將多方對映成一個有序的集合
核心 訂單表、訂單項
級聯新增 外來鍵處理的三種方式 1、刪除從表對應的實體類中的外來鍵屬性 2、在配置的xml中外來鍵屬性上新增 insert=false,update=false的設定。 3、在配置的xml中的manyToOne標籤中新增insert=false,update=false的設定。 級聯新增 casecade=save-update 介紹
級聯查詢 配置檔案介紹以及後臺sql的形成過程 級聯查詢時的問題 Lazy=true介紹 查單個時存在問題 Lazy=false介紹 查所有時存在問題 解決方案:通過欄位控制,強制載入。Hibernate.initialize()
普通刪除 Order Add 講外來鍵的處理 Get/list order.getOrderItems.size講懶載入的處理,sql形成過程 Del 將關係的處理中的刪除
程式碼: 2個實體類: 訂單表:
package com.four.entity;
import java.util.HashSet;
import java.util.Set;
public class Order {
private Integer orderId;
private String orderNo;
//定義一對多的關係時 一定需要採用介面方式,不許使用實現類①
private Set<OrderItem> orderItems=new HashSet<OrderItem>();//一對多的關係
private Integer initOrderItems =0; //0代表懶載入 1代表立即載入
public Integer getInitOrderItems() {
return initOrderItems;
}
public void setInitOrderItems(Integer initOrderItems) {
this.initOrderItems = initOrderItems;
}
public Set<OrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(Set<OrderItem> orderItems) {
this.orderItems = orderItems;
}
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
}
訂單項表:
package com.four.entity;
public class OrderItem {
private Integer orderItemId;
private Integer productId;
private Integer quantity;
private Integer oid;
private Order order;
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public Integer getOrderItemId() {
return orderItemId;
}
public void setOrderItemId(Integer orderItemId) {
this.orderItemId = orderItemId;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
public Integer getOid() {
return oid;
}
public void setOid(Integer oid) {
this.oid = oid;
}
}
配置: Order.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class table="t_hibernate_order" name="com.four.entity.Order">
<!--
session.getOrder
->select *from t_hibernate_order where order_id =?(以7為例)
7 P7
Order order = Class.forName("com.four.entity.Order").newInstanse(): 反射例項化
order.setOrderId(7);
order.setOrderId(P7);
通過外來鍵:one-to-many 找到orderItem.hbm.xml檔案
形成了一個sql語句 (建模)select *from t_hibernate_order_item where oid(外來鍵)=?(當前主鍵 7)
查出來一個集合
28 11 11 7
29 11 11 7 (配置檔案介紹以及後臺sql的形成過程)
30 11 11 7
...
通過反射得到含有資料庫查出來的值得物件集合 EntityBaseDao的處理(建模的一個類)
orderItems=List<OrderItem>
order.setOrderItems(orderItems);
-->
<id name="orderId" type="java.lang.Integer" column="order_id">
<generator class="increment"></generator>
</id>
<property name="orderNo" type="java.lang.String" column="order_no"></property>
<!--
cascade:用來配置維護實體類之間的關係所用
inverse:關係交由反方控制 =true 就是交給反方,false就是自己控制
反方是誰?現在我們配置這個表是Order訂單,那麼反方就是訂單項
跟它有關係的另外一方,就是反方 關係交給訂單項來維護
-->
<!--lazy 預設是懶載入 true false就是立即載入 懶載入多少條資料,查詢多少條,列50條,查詢51次,太佔記憶體和效能。所以定義一個initOrderItems--> <!-- lazy="false" -->
<!-- 級聯儲存和修改 改為級聯刪除,但是有問題。等多對多的時候在講 -->
<set name="orderItems" cascade="save-update" inverse="true">
<!-- 這裡一定填外來鍵 -->
<key column="oid"></key>
<!-- 一對多 對應多個訂單項 -->
<one-to-many class="com.four.entity.OrderItem"></one-to-many>
</set>
</class>
</hibernate-mapping>
OrderItem.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class table="t_hibernate_order_item" name="com.four.entity.OrderItem">
<id name="orderItemId" type="java.lang.Integer" column="order_Item_id">
<generator class="increment"></generator>
</id>
<property name="productId" type="java.lang.Integer" column="product_id"></property>
<property name="quantity" type="java.lang.Integer" column="quantity"></property>
<property name="oid" type="java.lang.Integer" column="oid" insert="false" update="false"></property>
<!-- 這張表,對應後臺的那個欄位 -->
<!-- 會報錯 --><!-- 可以交給上面來維護,也可以交給下面維護 把上面的insert。。。。放到下面,上面的oid就可以操作資料庫 -->
<many-to-one name="order" class="com.four.entity.Order" column="oid">
</many-to-one>
</class>
hibernate.cfg.xml(就是hibernate需要管理的資料庫表對應的實體類對映檔案,看我前面寫的部落格):
<!-- 一對多 -->
<mapping resource="com/four/entity/Order.hbm.xml"/>
<mapping resource="com/four/entity/OrderItem.hbm.xml"/>
Dao方法:
package com.four.dao;
import java.io.Serializable;
import java.util.List;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.four.entity.Order;
import com.four.entity.OrderItem;
import com.tow.util.SessionFactoryUtil;
public class OrderDao {
//訂單項增加
public Integer addOrderItem(OrderItem orderItem){
Session session = SessionFactoryUtil.getSession();
Transaction transaction = session.beginTransaction();
Integer otid = (Integer) session.save(orderItem);
transaction.commit();
session.close();
return otid;
}
//訂單增加
public Integer addOrder(Order order){
Session session = SessionFactoryUtil.getSession();
Transaction transaction = session.beginTransaction();
Integer otid = (Integer) session.save(order);
transaction.commit();
session.close();
return otid;
}
//查詢
public Order getOrder(Order order){
Session session = SessionFactoryUtil.getSession();
Transaction transaction = session.beginTransaction();
Order o = session.get(Order.class, order.getOrderId());
//是否強制載入
if(o!=null && new Integer(1).equals(order.getInitOrderItems())){
Hibernate.initialize(o.getOrderItems());
}
transaction.commit();
session.close();
return o;
}
//查詢
public List<Order> getOrderList(){
Session session = SessionFactoryUtil.getSession();
Transaction transaction = session.beginTransaction();
List list = session.createQuery("from Order").list();
transaction.commit();
session.close();
return list;
}
//普通刪除
public void delOrder(Order order){
Session session = SessionFactoryUtil.getSession();
Transaction transaction = session.beginTransaction();
Order o = session.get(Order.class, order.getOrderId());
for (OrderItem ot : o.getOrderItems()) {
session.delete(ot);
}
session.delete(o);
transaction.commit();
session.close();
}
}
然後就是測試Junit:
package com.four.dao;
import static org.junit.Assert.*;
import java.util.List;
import org.hibernate.Session;
import org.junit.Test;
import com.four.entity.Order;
import com.four.entity.OrderItem;
public class OrderDaoTest {
private OrderDao orderDao=new OrderDao();
/**
*
* Caused by: org.hibernate.MappingException:
* Repeated column in mapping for entity:
* com.zking.four.entity.OrderItem column: oid
* (should be mapped with insert="false" update="false")
* 同一欄位被映射了兩次
*/
@Test
public void testAddOrderItem() {
OrderItem orderItem=new OrderItem();
// orderItem.setOid(4);
orderItem.setProductId(89);
orderItem.setQuantity(78);
Order order=new Order();
order.setOrderId(4);
orderItem.setOrder(order);
this.orderDao.addOrderItem(orderItem);
}
/**
* 提交一個具有6個訂單項的訂單
* addOrder 1
* addOrderItem 6
*/
@Test
public void testAddOrder() {
Order order=new Order();
order.setOrderNo("P5");
OrderItem orderItem;
for (int i = 1; i < 7; i++) {
orderItem=new OrderItem();
orderItem.setProductId(i);
orderItem.setQuantity(i);
//雙向關聯
order.getOrderItems().add(orderItem);
orderItem.setOrder(order);
}
this.orderDao.addOrder(order);
}
/**
* org.hibernate.LazyInitializationException:
* failed to lazily initialize a collection of role:
* com.zking.four.entity.Order.orderItems,
* could not initialize proxy - no Session
* 懶載入
*/
@Test
public void testGetOrder() {
Order order=new Order();
order.setOrderId(5);
order.setInitOrderItems(1);
Order o = this.orderDao.getOrder(order);
System.out.println(o.getOrderNo());
System.out.println(o.getOrderItems().size());
//org.hibernate.collection.internal.PersistentSet PersistentSet看原始碼②
// System.out.println(o.getOrderItems().getClass().getName());
for (OrderItem ot : o.getOrderItems()) {
System.out.println(ot.getProductId());
}
}
@Test
public void testGetOrderList() {
List<Order> orderList = this.orderDao.getOrderList();
for (Order o : orderList) {
System.out.println(o.getOrderNo());
System.out.println(o.getOrderItems().size());
for (OrderItem ot : o.getOrderItems()) {
System.out.println(ot.getProductId());
}
}
}
/**
* 刪不掉,2種方案:
* 1、把有關聯的一起刪除.
* 2、先查詢出來,刪除從表在刪主表(太麻煩)
*/
@Test
public void testDelOrder() {
Order order=new Order();
order.setOrderId(4);
this.orderDao.delOrder(order);
}
}