1. 程式人生 > >hibernate的關聯、一對多(4)

hibernate的關聯、一對多(4)

  1. 什麼是關聯(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. 如何將多方對映成一個有序的集合

    核心 訂單表、訂單項

級聯新增 外來鍵處理的三種方式 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);
	}
	}