天天看點

hibernate關聯關系

關聯關系在orm架構中是一個比較重要的一部分,在學習Hibernate架構的朋友可以重點的關注一下。關聯關系通俗一點來說就是資料庫表與表之間的關系(主外鍵關系),hibernare架構弱化了資料庫表的一些操作,強化了實體類的操作,在hibernate中操作實體類就等同于操作資料庫表。

它将資料庫中的表映射成對應的對象,以對象的形式展現,這樣我們就可以通過映射的對象來對資料庫中的資料進行間接的操作。

關聯映射是将資料庫中的表映射成與之相對應的對象,當你對這個對象進行操作的時候,Hibernate會對資料庫中對應的表執行相應的操作,你對該實體的操作實際上就是在間接的操作資料庫中與之相對應的表。

關聯的分類:關聯可以分為一對一、一對多/多對一、多對多關聯

一對多 : ,一對多是指一個對象對應多個對象 同樣也分為單向關聯和雙向關聯 如:一個教室可以有多個學生

1實作工具類

package com.lx.three.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
 * 這個類是在學習hibernate的過程中使用 (整合ssh架構之前用)
 * 作用:
 * 可以用來檢測所有的映射檔案是否正确   90%
 * @author lx
 *
 */
public class SessionFactoryUtils {
 private static SessionFactory sessionFactory;
 static {
//  存放目前會話
  Configuration cfg = new Configuration().configure("hibernate.cfg.xml");
  sessionFactory = cfg.buildSessionFactory();
 }
 public static Session openSession() {
  Session session = sessionFactory.getCurrentSession();
  if(session == null) {
   session = sessionFactory.openSession();
  }
  return session;
 }
 public static void closeSession() {
  Session session = sessionFactory.getCurrentSession();
  if(session != null && session.isOpen()) {
   session.close();
  }
 }
 public static void main(String[] args) {
  Session session = SessionFactoryUtils.openSession();
  session.beginTransaction();
  System.out.println(session.isConnected());
  SessionFactoryUtils.closeSession();
  System.out.println(session.isConnected());
 }
}

           

為了友善我們把所有的方法都寫在一起DemoDao

package com.lx.three.dao;
import java.util.List;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.lx.three.entity.Order;
import com.lx.three.entity.OrderItem;
import com.lx.three.util.SessionFactoryUtils;
public class DemoDao {
 /**
  * 為了測試關系型映射檔案配置準确
  *  講解insert=false,update=false的用途
  * @param order
  * @return
  */
 public Integer addOrder(Order order) {
  Session session = SessionFactoryUtils.openSession();
  Transaction transaction = session.beginTransaction();
  Integer oid = (Integer)session.save(order);
  transaction.commit();
  session.close();
  return oid;
 }
 public Integer addOrderItem(OrderItem orderItem) {
  Session session = SessionFactoryUtils.openSession();
  Transaction transaction = session.beginTransaction();
  Integer otid = (Integer)session.save(orderItem);
  transaction.commit();
  session.close();
  return otid;
 }
 /**
  * 為了講解懶加載的問題(hibernate3.0後所有查詢方式預設采用的是懶加載方式)
  *  1、查單個時存在問題,代理對象已經關閉
  *  2、查多個存在問題,有性能的問題
  * @param order
  * @return
  */
 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());
//   System.out.println(o.getOrderItems());
  }
  transaction.commit();
  session.close();
  return o;
 }
 public List<Order> getOrderList() {
  Session session = SessionFactoryUtils.openSession();
  Transaction transaction = session.beginTransaction();
  List<Order> list = session.createQuery("from Order").list();
  transaction.commit();
  session.close();
  return list;
 }
 /**
  * z主表的資料不能随便删除,得先删除從表中對應資訊,才能删除主表的資訊。
  * @param 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);
//  session.delete(order);
  transaction.commit();
  session.close();
 }
}


           

2.建立實體類

根據資料庫建立一端實體類

package com.lx.three.entity;
import java.util.ArrayList;
import java.util.List;
public class Order {
 private Integer orderId;
 private String orderNo;
// 建立了關聯關系, 一個訂單對應多個訂單項
 private List<OrderItem> orderItems = new ArrayList<>();
 private Integer initChildren = 0;//0是懶加載  1:false
 public Integer getInitChildren() {
  return initChildren;
 }
 public void setInitChildren(Integer initChildren) {
  this.initChildren = initChildren;
 }
 public List<OrderItem> getOrderItems() {
  return orderItems;
 }
 public void setOrderItems(List<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;
 }
 @Override
 public String toString() {
  return "Order [orderId=" + orderId + ", orderNo=" + orderNo + "]";
 }
}

           

多段實體類

package com.lx.three.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;
 }
 @Override
 public String toString() {
  return "OrderItem [orderItemId=" + orderItemId + ", productId=" + productId + ", quantity=" + quantity
    + ", oid=" + oid + "]";
 }
}

           

3在配置 .hbm.xml檔案

一端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 name="com.lx.three.entity.Order" table="t_hibernate_order">
  <id name="orderId" type="java.lang.Integer" column="order_id">
   <generator class="increment" />
  </id>
  <property name="orderNo" type="java.lang.String" column="order_no">
  </property>
  <!-- 如果所對應的關系為list  -->
  <!-- 
   bag: 标簽
    lazy : 是否需要用到懶加載
    name : 相對應關系的屬性
    cascade : 級聯 級聯的增加和修改
    inverse : 關聯關系交給對方管理,預設為true
    
   key : 主表的主鍵 從表的外建
   one-to-many : 相對應關系的實體類
   -->
  <bag lazy="true" name="orderItems" cascade="save-update" inverse="true" >
  <!-- 從表的外鍵 -->
   <key column="oid"></key>
   <one-to-many class="com.lx.three.entity.OrderItem"/>
  </bag>
 </class>
</hibernate-mapping>

           

配置這個檔案時要注意這個oid是外鍵的oid

多端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 name="com.lx.three.entity.OrderItem" table="t_hibernate_order_item">
  <id name="orderItemId" type="java.lang.Integer" column="order_item_id">
   <generator class="increment" />
  </id>
  <property name="productId" type="java.lang.Integer" column="product_id">
  </property>
  <property name="quantity" type="java.lang.Integer" column="quantity">
  </property>
  <!-- Repeated column in mapping for entity -->
  <!--特别注意:這裡必須配置屬性 insert="false" update="false"-->
  <property name="oid" type="java.lang.Integer" column="oid" insert="false" update="false">
  </property>
  <many-to-one name="order" class="com.lx.three.entity.Order" column="oid"></many-to-one>
 </class>
</hibernate-mapping>

           

注意:

<!--特别注意:這裡必須配置屬性 insert="false" update="false"-->
  <property name="oid" type="java.lang.Integer" column="oid" insert="false" update="false">

           

接下來進行測試

為了友善我們使用junit進行測試DemoDaoTest

package com.lx.three.dao;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.lx.three.entity.Order;
import com.lx.three.entity.OrderItem;
/**
 * @Before每測試一個@Test标記的測試方法,都會調用之前執行一次
 * @After每測試一個@Test标記的測試方法,都會調用之後執行一次
 * @author lx
 *
 */
public class DemoDaoTest {
 private DemoDao demoDao = new DemoDao();
// @Before
// public void setUp() throws Exception {
// }
//
// @After
// public void tearDown() throws Exception {
// }
 /**
 *添加訂單
 */
 @Test
 public void testAddOrder() {//同時給表添加資料
  Order order = new Order();
  order.setOrderNo("p20");
  OrderItem orderItem = null;
  for (int i = 0; i < 6; i++) {
   orderItem = new OrderItem();
   orderItem.setProductId(17+i);
   orderItem.setQuantity(20+i);
//   維護關聯關系
   orderItem.setOrder(order);//維護order
   order.getOrderItems().add(orderItem);
  }
  demoDao.addOrder(order);
 }
 /**
  * 這是添加訂單項的示範
  */
 @Test
 public void testAddOrderItem() {//給原有的訂單表添加訂單項
  OrderItem orderItem = new OrderItem();
  orderItem.setProductId(12);
  orderItem.setQuantity(33);
  Order order = new Order();
  order.setOrderId(3);
  order.getOrderItems().add(orderItem);
  orderItem.setOrder(order );
  demoDao.addOrderItem(orderItem);
//  購物車的做法
//  OrderItem orderItem = null;
//  for (int i = 0; i < 6; i++) {
//   orderItem = new OrderItem();
//   orderItem.setProductId(17+i);
//   orderItem.setQuantity(20+i);
   維護關聯關系
//   Order order = new Order();
//   order.setOrderId(3);
//   order.getOrderItems().add(orderItem);
//   orderItem.setOrder(order);
//   demoDao.addOrderItem(orderItem);
//  }
 }
 /**
 *擷取訂單資料 并且将訂單項資料加載出來
 */
 @Test
 public void testGetOrder() {
  Order order = new Order();
  order.setOrderId(3);
  order.setInitChildren(1);//設定自動控制是否懶加載
  Order o = this.demoDao.getOrder(order );
//  failed to lazily initialize a collection of role: 
//  com.lx.three.entity.Order.orderItems, 
//  could not initialize proxy - no Session
//  操作了兩次資料庫
  
  List<OrderItem> orderItems = o.getOrderItems();
  for (OrderItem orderItem : orderItems) {
   System.out.println(orderItem);
  }
  System.out.println(o);
 }
 @Test
 public void testGetOrderList() {
  List<Order> orderList = this.demoDao.getOrderList();
  for (Order order : orderList) {
   for (OrderItem orderItem : order.getOrderItems()) {
    System.out.println(orderItem);
   }
   System.out.println(order);
  }
 }
 /**
 *删除訂單
 */
 @Test
 public void testDelOrder() {
  Order order = new Order();
  order.setOrderId(2);
  this.demoDao.delOrder(order );
 }
}


           

懶加載問題

因為在這裡Order和OrderItem有關聯關系,如果我們從資料庫擷取一個Order對象,那麼它對應的OrderItem對象不會立馬讀出來,隻有當你使用的時候才會讀出來給你。懶加載也會涉及到一個問題,就是當你Session關閉了之後,對關聯對象的查詢就會出異常了。也就是說當你要使用關聯對象時,Session必須不能關閉,此時Hibernate才能從資料庫中查出來。

為了解決懶加載問題我們就可以在Order實體類裡面加一個屬性

private Integer initChildren = 0;//0是懶加載  1:false


           

這裡的lazy就是要不要懶加載

<bag lazy="true" name="orderItems" cascade="save-update" inverse="true" >

           

然後我們在寫方法的時候判斷一下是否要進行懶加載

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;
 }

           

在測試的時候加入下面的代碼初始化代理對象,來操作懶加載,當你設定為0時就是要進行懶加載,為1就是不懶加載。或者到Order.hbm.xml把lazy的值改為false也就是讓Hibernate禁用懶加載。

order.setInitChildren(1);//設定自動控制是否懶加載