1. 程式人生 > >簡單解析hibernate中的一對多關係

簡單解析hibernate中的一對多關係

  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 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 建立訂單到客戶的多對一關聯關係

  <!--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 將關係的處理中的刪除

如程式碼演示:

package com.zking.util;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

/**
 * hibernate工具類
 * 	未整合框架前使用
 * @author Administrator
 *
 */
public class HibernateUtli {

	private static SessionFactory sessionFactory;

	/**
	 * 存放當前會話
	 */
	private static ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
	/**
	 * 通過單例管理sessionFactory,整個系統執行過程中只會構建一個sessionFactory物件
	 */
	static {
		//讀取配置檔案 獲取會話  開啟事務
		sessionFactory = new Configuration().configure().buildSessionFactory();
	}
	/**
	 * 建模 獲取sessionfactory
	 * @return
	 * 2018年10月19日上午10:23:16
	 */
	public static SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	/**
	 * 開啟一個session
	 * @return
	 * 2018年10月19日上午10:20:58
	 */
	public static Session openSession() {
		Session session = threadLocal.get();
		if (null == session) {
			session = sessionFactory.openSession();
			threadLocal.set(session);
		}
		return session;
	}
	/**
	 * 開啟與執行緒繫結的session
	 * @return
	 * 2018年10月19日上午10:24:32
	 */
	public static Session getCurrentSession() {
		Session session = threadLocal.get();
		if (null == session) {
			session = sessionFactory.openSession();// 如果session為空就建立一個session
			threadLocal.set(session);// 放到當前的執行緒中
			return session;
		}
		return session;

	}
	/**
	 * 關閉session
	 * 
	 * 2018年10月19日上午10:21:17
	 */
	public static void closeSession() {
		Session session = threadLocal.get();
		if (null != session) {
			if (session.isOpen()) {
				Transaction transaction = session.beginTransaction();
				transaction.commit();
				session.close();
			}
			threadLocal.set(null);
		}
	}
	/**
	 * 測試
	 * @param args
	 * 2018年10月19日上午10:25:41
	 */
	public static void main(String[] args) {
		Session session = openSession();
		System.out.println(session.isConnected());
		closeSession();
	}
}

package com.zking.dao.daoImpl;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.zking.entity.Order;
import com.zking.entity.OrderItem;
import com.zking.util.HibernateUtli;

public class OrderDaoImpl implements IOrderDaoImpl {
	@Override
	public Integer insertOrder(Order order) {
		Session session = HibernateUtli.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		Integer save = 0;
		try {
			save = (Integer) session.save(order);
			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
			transaction.rollback();
			throw new RuntimeException("新增出現異常");
		}
		HibernateUtli.closeSession();
		return save;
	}

	@Override
	public void deleteOrder(Order order) {
		Session session = HibernateUtli.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		try {
			Order o = session.get(Order.class, order.getOrder_id());
			System.out.println(o);
			for (OrderItem ot : o.getOrderItem()) {
				session.delete(ot);
			}
			session.delete(order);
			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
			transaction.rollback();
			throw new RuntimeException("刪除出現異常");
		}
		HibernateUtli.closeSession();
	}

	@Override
	public void editOrder(Order order) {
		Session session = HibernateUtli.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		try {
			session.update(order);
			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
			transaction.rollback();
			throw new RuntimeException("修改出現異常");
		}
		HibernateUtli.closeSession();
	}

	@Override
	public Order getOrder(Order order) {
		Session session = HibernateUtli.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		Order t = null;
		try {
			t = session.get(Order.class, order.getOrder_id());
			if (t != null && new Integer(1).equals(order.getInitorderItem())) {
				//強制立即載入
				Hibernate.initialize(t.getOrderItem());
			}
			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
			transaction.rollback();
			throw new RuntimeException("查詢出現異常");
		}
		HibernateUtli.closeSession();
		return t;
	}

	@Override
	public List<Order> getOrderList(String str) {
		Session session = HibernateUtli.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		List<Order> list = null;
		try {
			Criteria criteria = session.createCriteria(Order.class);
			list = criteria.list();
			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
			transaction.rollback();
			throw new RuntimeException("查詢出現異常");
		}
		HibernateUtli.closeSession();
		return list;
	}
}
package com.zking.dao.daoImpl;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.zking.entity.OrderItem;
import com.zking.util.HibernateUtli;

public class OrderItemDaoImpl implements IOrderItemDaoImpl {
	@Override
	public Integer insertOrderItem(OrderItem orderItem) {
		Session session = HibernateUtli.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		Integer save = 0;
		try {
			save = (Integer) session.save(orderItem);
			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
			transaction.rollback();
			throw new RuntimeException("新增出現異常");
		}
		HibernateUtli.closeSession();
		return save;
	}

	@Override
	public void deleteOrderItem(OrderItem orderItem) {
		Session session = HibernateUtli.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		try {
			session.delete(orderItem);
			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
			transaction.rollback();
			throw new RuntimeException("刪除出現異常");
		}
		HibernateUtli.closeSession();
	}

	@Override
	public void editOrderItem(OrderItem orderItem) {
		Session session = HibernateUtli.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		try {
			session.update(orderItem);
			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
			transaction.rollback();
			throw new RuntimeException("修改出現異常");
		}
		HibernateUtli.closeSession();
	}

	@Override
	public OrderItem getOrderItem(OrderItem orderItem) {
		Session session = HibernateUtli.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		OrderItem t = null;
		try {
			t = session.get(OrderItem.class, orderItem.getOrder_item_id());
			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
			transaction.rollback();
			throw new RuntimeException("查詢出現異常");
		}
		HibernateUtli.closeSession();
		return t;
	}

	@Override
	public List<OrderItem> getOrderItemList(String str) {
		Session session = HibernateUtli.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		List<OrderItem> list = null;
		try {
			Criteria criteria = session.createCriteria(OrderItem.class);
			list = criteria.list();
			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
			transaction.rollback();
			throw new RuntimeException("查詢出現異常");
		}
		HibernateUtli.closeSession();
		return list;
	}
}
package com.zking.entity;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

public class Order implements Serializable {
	/**
	 * 2018年10月23日下午4:21:50
	 */
	private static final long serialVersionUID = 1575914611357638677L;
	private Integer order_id;
	private String order_no;
	/**
	 * 在描述關係的時候,一定用含集合介面進行  一對多關係
	 */
	private Set<OrderItem> orderItem = new HashSet<>();
	
	private Integer initorderItem = 0; //0 延遲載入  1 立即載入
	
	
	public Integer getInitorderItem() {
		return initorderItem;
	}

	public void setInitorderItem(Integer initorderItem) {
		this.initorderItem = initorderItem;
	}

	public Set<OrderItem> getOrderItem() {
		return orderItem;
	}

	public void setOrderItem(Set<OrderItem> orderItem) {
		this.orderItem = orderItem;
	}

	@Override
	public String toString() {
		return "Order [order_id="+ order_id +",order_no="+ order_no +"]";
	}

	public Integer getOrder_id() {
		return order_id;
	}

	public void setOrder_id(Integer order_id) {
		this.order_id = order_id;

	}

	public String getOrder_no() {
		return order_no;
	}

	public void setOrder_no(String order_no) {
		this.order_no = order_no;

	}

	public Order() {
		super();
	}

	public Order(Integer order_id,String order_no) {
		super();
		this.order_id = order_id;
		this.order_no = order_no;
	}
}
package com.zking.entity;

import java.io.Serializable;

public class OrderItem implements Serializable {
	/**
	 * 2018年10月23日下午4:33:58
	 */
	private static final long serialVersionUID = 2468758266675428160L;
	private Integer order_item_id;
	private Integer product_id;
	private Integer quantity;
	private Integer oid;
	
	private Order order;
	
	

	public Order getOrder() {
		return order;
	}

	public void setOrder(Order order) {
		this.order = order;
	}

	@Override
	public String toString() {
		return "OrderItem [order_item_id="+ order_item_id +",product_id="+ product_id +",quantity="+ quantity +",oid="+ oid +"]";
	}

	public Integer getOrder_item_id() {
		return order_item_id;
	}

	public void setOrder_item_id(Integer order_item_id) {
		this.order_item_id = order_item_id;

	}

	public Integer getProduct_id() {
		return product_id;
	}

	public void setProduct_id(Integer product_id) {
		this.product_id = product_id;

	}

	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;

	}

	public OrderItem() {
		super();
	}

	public OrderItem(Integer order_item_id,Integer product_id,Integer quantity,Integer oid) {
		super();
		this.order_item_id = order_item_id;
		this.product_id = product_id;
		this.quantity = quantity;
		this.oid = oid;
	}
}
<?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>
<!-- 
	級聯查詢原理:
	session.get(Order.class, 2);
	select * from t_hibernate_order where order_id = ?(2)
	2   P2 模擬查出來的資料
	Order o = lass.forName("com.zking.entity.Order").newInstance(); 反射例項化
	o.setorder_id(2).... 反射賦值
	通過one-to-many中的class屬性找到對應的對映檔案
	select * from t_hibernate_order_item where oid = ?(2)
	查出來的資料
	ResultSet rs
	com.zking.entity.Order
	原理是mvc EntityBaseDao,最後獲取到有值得list集合 -> orderItem
	o.setOrderItem(orderItem)
 -->
	<class name="com.zking.entity.Order" table="t_hibernate_order">
		<id name="order_id" type="java.lang.Integer" column="order_id">
			<generator class="increment" />
		</id>
		<property name="order_no" type="java.lang.String" column="order_no"></property>
		<!-- 
			cascade:配置級聯關係
			inverse:反方,是否將關聯關係交給對方控制
			lazy="false":立即載入,預設延遲載入
		 -->
		<set name="orderItem" cascade="save-update" inverse="false" >
			<!-- 配置外來鍵欄位 -->
			<key column="oid"></key>
			<!-- 配置外來鍵關聯的類(表) -->
			<one-to-many class="com.zking.entity.OrderItem"/>
		</set>
	</class>
</hibernate-mapping>
<?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 name="com.zking.entity.OrderItem" table="t_hibernate_order_item">
		<id name="order_item_id" type="java.lang.Integer" column="order_item_id">
			<generator class="increment" />
		</id>
		<property name="product_id" 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.entity.Order" column="oid"></many-to-one>
	</class>
</hibernate-mapping>
package com.zking.test;

import java.util.List;

import org.junit.Before;
import org.junit.Test;

import com.zking.dao.daoImpl.OrderDaoImpl;
import com.zking.entity.Order;
import com.zking.entity.OrderItem;

public class OrderDAOTest {
	private OrderDaoImpl orderDao = null;
	private Order order = null;
	
	@Before
	public void setUp() throws Exception {
		orderDao = new OrderDaoImpl();
		order = new Order();
	}
	
	/**
	 * 提交含有六個訂單項的一個訂單
	 * 
	 * 2018年10月23日下午7:32:48
	 */
	@Test
	public void testOrderAdd() {
		OrderItem orderItem = null;
		order.setOrder_no("P2");
		for (int i = 1; i < 7; i++) {
			orderItem = new OrderItem();
			orderItem.setProduct_id(i);
			orderItem.setQuantity(i);
			order.getOrderItem().add(orderItem);
			orderItem.setOrder(order);
		}
		this.orderDao.insertOrder(order);
	}

	@Test
	public void testDel() {
		order.setOrder_id(2);
		/**
		 * 聯表刪除解決方案:
		 * 1.cascade="all" 設定為all,不推薦直接填寫delete
		 * 2.在dao層的刪除方法中先迴圈刪除所有外來鍵,再刪主鍵
		 */
		this.orderDao.deleteOrder(order);
	}

	@Test
	public void testget() {
		order.setOrder_id(2);
		order.setInitorderItem(1);
		order = this.orderDao.getOrder(order);
		System.out.println(order);
		/**
		 * 	Lazy=true介紹	查單個時存在問題 :當要真正用到查詢物件時候,session已經關閉
		 * 不存在初始化代理
		 * could not initialize proxy(代理) - no Session
		 * 懶載入(延遲載入)問題:
		 * 將查詢的oid(object id)儲存到session的代理proxy中當要真正用到查詢物件時候,再去執行SQL
		 * 解決問題:
		 * 強制立即載入:lazy="false":立即載入,預設延遲載入
		 */
		System.out.println(order.getOrderItem().size());
	}

	@Test
	public void testgetList() {
		List<Order> orderList = this.orderDao.getOrderList("");
		/**
		 *Lazy=false介紹	查所有時存在問題 : 效能低
		 */
		for (Order order : orderList) {
			System.out.println(order);
			System.out.println(order.getOrderItem().size());
		}
	}

}

package com.zking.test;

import org.junit.Before;
import org.junit.Test;

import com.zking.dao.daoImpl.OrderItemDaoImpl;
import com.zking.entity.Order;
import com.zking.entity.OrderItem;

public class OrderItemDAOTest {
	private OrderItemDaoImpl orderItemDao = null;
	private Order order = null;
	
	@Before
	public void setUp() throws Exception {
		orderItemDao = new OrderItemDaoImpl();
		order = new Order();
	}

	@Test
	public void testOrderItemAdd() {
		OrderItem orderItem = new OrderItem();
		order.setOrder_id(1);
		orderItem.setOrder(order);
		orderItem.setProduct_id(12);
		orderItem.setQuantity(55);
//		Repeated column in mapping for entity: com.zking.entity.OrderItem column: oid (should be mapped with insert="false" update="false")
//		實體對映中的重複列:com.zking.entity。OrderItem列:oid(應該用insert="false" update="false"進行對映)
		this.orderItemDao.insertOrderItem(orderItem);
	}

}