天天看點

hibernate關聯映射之一對多關系

以訂單和訂單項為例來講解Hibernate關聯映射中的一對多關聯關系。兩個實體類如下:

訂單類:

package com.zking.four.entity;

import java.util.HashSet;
import java.util.Set;

/**
 * 訂單實體類(一對多中的一)
 * @author LJ
 * @Date 2018年10月23日
 * @Time 下午10:08:48
 */
public class Order {

	private Integer orderId;//訂單ID(主鍵)
	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.zking.four.entity;

/**
 * 訂單項實體類(一對多中的多)
 * @author LJ
 * @Date 2018年10月23日
 * @Time 下午10:08:25
 */
public class OrderItem {

	private Integer orderItemId;//訂單項ID(主鍵)
	private Integer productId;//商品ID
	private Integer quantity;//數量
	private Integer oid;//訂單ID(外鍵)
	
	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.zking.four.entity.Order">
		<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:反方,是否将關聯關系交給對方控制
		 -->
		<set name="orderItems" cascade="save-update" inverse="true">
			<!-- 配置外鍵字段 -->
			<key column="oid"></key>
			<!-- 配置外鍵關聯的類 -->
			<one-to-many class="com.zking.four.entity.OrderItem"/>
		</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.zking.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>
		<many-to-one name="order" class="com.zking.four.entity.Order" column="oid"></many-to-one>
	</class>
</hibernate-mapping>
           

1、級聯新增

/**
	 * 新增訂單項表
	 * @author LJ
	 * @Date 2018年10月23日
	 * @Time 下午8:03:18
	 * @param orderItem
	 * @return
	 */
	public Integer saveOrderItem(OrderItem orderItem) {
		Session session = SessionFactoryUtil.getSession();//擷取session
		Transaction transaction = session.beginTransaction();//開啟事務
		Integer oid = (Integer) session.save(orderItem);//新增訂單項
		transaction.commit();//送出事務
		SessionFactoryUtil.closeSession();//關閉session
		return oid;
	}
	
	@Test
	public void testSaveOrderItem() {
		OrderItem orderItem=new OrderItem();
		Order order=new Order();
		order.setOrderId(1);
		orderItem.setOrder(order);
		orderItem.setProductId(3);
		orderItem.setQuantity(45);
		this.saveOrderItem(orderItem);
	}
           
若不對外鍵進行處理的話,會出現異常java.lang.ExceptionInInitializerError,這一異常導緻的原因是Repeated column in mapping for entity: com.zking.four.entity.OrderItem column: oid (should be mapped with insert="false" update="false"),也就是因為OrderItem.hbm.xml配置檔案裡對外鍵重複配置了,是以才出現的異常
hibernate關聯映射之一對多關系

解決方式:a、删除從表對應的實體類中的外鍵屬性

    b、在配置的xml中外鍵屬性上添加 insert=false,update=false的設定。

    c、在配置的xml中的manyToOne标簽中添加insert=false,update=false的設定。我這裡選的是第二種方式

/**
	 * 新增訂單表
	 * @author LJ
	 * @Date 2018年10月23日
	 * @Time 下午8:03:31
	 * @param order
	 * @return
	 */
	public Integer saveOrder(Order order) {
		Session session = SessionFactoryUtil.getSession();
		Transaction transaction = session.beginTransaction();
		Integer oid = (Integer) session.save(order);
		transaction.commit();
		SessionFactoryUtil.closeSession();
		return oid;
	}
	
	/**
	 * 送出訂單中含有5個訂單項的方法
	 * @author LJ
	 * @Date 2018年10月23日
	 * @Time 下午5:40:05
	 */
	@Test
	public void testSaveOrder() {
		Order order=new Order();
		order.setOrderNo("2");
		OrderItem orderItem;
		for (int i = 1; i < 6; i++) {
			orderItem=new OrderItem();
			orderItem.setProductId(i);
			orderItem.setQuantity(i);
			orderItem.setOrder(order);
			order.getOrderItems().add(orderItem);
		}
		this.saveOrder(order);
	}
           

2、級聯查詢

/**
	 * 根據訂單編号查詢訂單
	 * @author LJ
	 * @Date 2018年10月23日
	 * @Time 下午8:03:39
	 * @param order
	 * @return
	 */
	public Order getOrder(Order order) {
		Session session = SessionFactoryUtil.getSession();
		Transaction transaction = session.beginTransaction();
		Order o = session.get(Order.class, order.getOrderId());
		transaction.commit();
		SessionFactoryUtil.closeSession();
		return o;
	}
	
	@Test
	public void testGetOrder() {
		Order order=new Order();
		order.setOrderId(2);//查詢訂單編号為2的訂單
		Order o = this.getOrder(order);
		System.out.println(o.getOrderNo());
		System.out.println(o.getOrderItems().size());
	}
           

運作以上代碼會發現隻輸出了o.getOrderNo(),而o.getOrderItems().size()沒有輸出,而且還報了異常org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.zking.four.entity.Order.orderItems, could not initialize proxy - no Session,這是懶加載的異常,因為級聯查詢預設是懶加載,是以要等到調用了o.getOrderItems().size()才會執行查詢語句,但這時session已經關閉了,是以出現這一異常。

hibernate關聯映射之一對多關系

也許你會想到将lazy屬性設為false就能解決這一問題,并不是這樣,因為将lazy屬性設為false後,雖然查詢單個時不會出現異常,但查詢所有時會存在問題,就是查詢所有訂單時,它會預設把訂單下的所有訂單項也給查詢出來,但我們有時并不需要用到,是以這就浪費了資源,消耗了不該消耗的性能。綜上所述,最好的解決方式就是通過字段控制是否需要懶加載。

hibernate關聯映射之一對多關系
hibernate關聯映射之一對多關系

3、普通删除

大家都知道,删除主鍵表裡的某條資料時,要先删除外鍵表裡的資料,是以删除訂單時的代碼如下:

/**
	 * 删除訂單
	 * @author LJ
	 * @Date 2018年10月23日
	 * @Time 下午8:04:18
	 * @param order
	 */
	public void delOrder(Order order) {
		Session session = SessionFactoryUtil.getSession();//擷取session
		Transaction transaction = session.beginTransaction();//開啟事務
		Order o = session.get(Order.class, order.getOrderId());//根據訂單ID擷取到該訂單
		for (OrderItem oi : o.getOrderItems()) {//周遊該訂單所有的訂單項
			session.delete(oi);//先删除訂單項
		}
		session.delete(o);//在删除該訂單
		transaction.commit();//送出事務
		session.close();//關閉session
	}
	
	@Test
	public void testDelOrder() {
		Order order=new Order();
		order.setOrderId(1);//删除訂單編号為1的訂單
		this.delOrder(order);
	}
           

繼續閱讀