溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Hibernate映射一對(duì)多關(guān)聯(lián)關(guān)系是什么

發(fā)布時(shí)間:2021-12-06 09:10:53 來(lái)源:億速云 閱讀:128 作者:iii 欄目:編程語(yǔ)言

這篇文章主要講解了“Hibernate映射一對(duì)多關(guān)聯(lián)關(guān)系是什么”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Hibernate映射一對(duì)多關(guān)聯(lián)關(guān)系是什么”吧!

在域模型中,類和類之間最普通的關(guān)系就是關(guān)聯(lián)關(guān)系。在UML語(yǔ)言中,關(guān)聯(lián)是有方向的。以客戶(Customer)和訂單(Order)的關(guān)系為例,一個(gè)客戶可以發(fā)出多個(gè)訂單,而一個(gè)訂單只能屬于一個(gè)客戶。

從Order到Customer的關(guān)聯(lián)是多對(duì)一關(guān)聯(lián),這意味著每個(gè)Order對(duì)象都會(huì)引用一個(gè)Customer對(duì)象,因此在Order類中應(yīng)該定義一個(gè)Customer類型的屬性,來(lái)引用所關(guān)聯(lián)的Customer對(duì)象。

從Customer到Order的關(guān)聯(lián)是一對(duì)多的關(guān)聯(lián),這意味著每個(gè)Customer對(duì)象都會(huì)引用一組Order對(duì)象,因此在Customer類中應(yīng)該定義一個(gè)集合類型的屬性,來(lái)引用所有關(guān)聯(lián)的Order對(duì)象。

Hibernate映射一對(duì)多關(guān)聯(lián)關(guān)系是什么

一、建立多對(duì)一的單向關(guān)聯(lián)關(guān)系

如上例中,我們只需在Order類中定義一個(gè)customer屬性,而在Customer類中無(wú)需定義存放Order對(duì)象的集合屬性。

Order.java

package mypack;   public class Order  implements java.io.Serializable {       private long id;       private String orderNumber;       private Customer customer;//定義一個(gè)Customer屬性       public Order() {      }       public Order(Customer customer) {          this.customer = customer;      }      public Order(String orderNumber, Customer customer) {         this.orderNumber = orderNumber;         this.customer = customer;      }          //省略了id,orderNumber的構(gòu)造方法      public Customer getCustomer() {          return this.customer;      }            public void setCustomer(Customer customer) {          this.customer = customer;      }   }

Customer類的所有屬性都是和CUSTOMERS表中的字段一一對(duì)應(yīng),因此可以直接使用如下的映射代碼:

<class name="mypack.Customer" table="CUSTOMERS" >     <id name="id" type="long" column="ID">       <generator class="increment"/>     </id>      <property name="name" type="string" >         <column name="NAME" length="15" />     </property>          </class>

Order類的orderNumber屬性和ORDERS表中ORDER_NUMBER字段對(duì)應(yīng),映射代碼和上面類似,此處省去。我們關(guān)注的主要地方是,Order類中的customer屬性,因?yàn)樗荂ustomer類型的,是與ORDERS表的外鍵CUSTOMER_ID對(duì)應(yīng)的,它的真實(shí)值是存在CUSTOMERS表中而ORDERS表存的只是對(duì)它的引用,因此customer的映射方法不能如上面一樣。

<many-to-one          name="customer"         column="CUSTOMER_ID"         class="mypack.Customer"         not-null="true"           lazy="false" />

使用方法のBussiness.java演示:

package mypack;   import org.hibernate.*;  import org.hibernate.cfg.Configuration;  import java.util.*;   public class BusinessService{    public static SessionFactory sessionFactory;    static{       try{        // 初始化         Configuration config = new Configuration();         config.configure();         sessionFactory = config.buildSessionFactory();      }catch(RuntimeException e){e.printStackTrace();throw e;}    }  /*根據(jù)參數(shù)指定customer的customer_id找出記錄*/   public List findOrdersByCustomer(Customer customer){      Session session = sessionFactory.openSession();      Transaction tx = null;      try {        tx = session.beginTransaction();         List orders=session.createQuery("from Order as o where o.customer.id="+customer.getId())                           .list();        //Hibernate執(zhí)行:select * from ORDERS where CUSTOMER_ID=customer.getId();        tx.commit();        return orders;      }catch (RuntimeException e) {        if (tx != null) {          tx.rollback();        }        throw e;      } finally {        session.close();      }    }  /*根據(jù)OID找出指定customer_id的記錄*/   public Customer findCustomer(long customer_id){      Session session = sessionFactory.openSession();      Transaction tx = null;      try {        tx = session.beginTransaction();        Customer customer=(Customer)session.get(Customer.class,new Long(customer_id));        tx.commit();        return customer;      }catch (RuntimeException e) {        if (tx != null) {          tx.rollback();        }        throw e;      } finally {        session.close();      }    }   /*        public void saveCustomerAndOrderWithCascade(){      Session session = sessionFactory.openSession();      Transaction tx = null;      try {        tx = session.beginTransaction();         Customer customer=new Customer("Jack");//創(chuàng)建一個(gè)Customer持久化對(duì)象        //不保存customer對(duì)象,這樣執(zhí)行的話會(huì)出現(xiàn)異常        Order order1=new Order("Jack_Order001",customer);        Order order2=new Order("Jack_Order002",customer);//創(chuàng)建兩個(gè)Order對(duì)象         session.save(order1);        session.save(order2);         tx.commit();       }catch (RuntimeException e) {        if (tx != null) {          tx.rollback();        }        e.printStackTrace();      } finally {        session.close();      }    }   */  public void saveCustomerAndOrder(){      Session session = sessionFactory.openSession();      Transaction tx = null;      try {        tx = session.beginTransaction();         Customer customer=new Customer("Tom");//創(chuàng)建一個(gè)Customer持久化對(duì)象        session.save(customer);         Order order1=new Order("Tom_Order001",customer);        Order order2=new Order("Tom_Order002",customer);//創(chuàng)建兩個(gè)Order對(duì)象        session.save(order1);        session.save(order2);       // 對(duì)同一個(gè)customerHibernate執(zhí)行兩次插入ORDERS表        tx.commit();       }catch (RuntimeException e) {        if (tx != null) {          tx.rollback();        }        throw e;      } finally {        session.close();      }    }        public void printOrders(List orders){        for (Iterator it = orders.iterator(); it.hasNext();) {           Order order=(Order)it.next();           System.out.println("OrderNumber of "+order.getCustomer().getName()+ " :"+order.getOrderNumber());        }    }      public void test(){        saveCustomerAndOrder();      //  saveCustomerAndOrderWithCascade();        Customer customer=findCustomer(1);        List orders=findOrdersByCustomer(customer);        printOrders(orders);    }     public static void main(String args[]){      new BusinessService().test();      sessionFactory.close();    }  }  <span style="font-size:16px;color:#cc33cc;"><strong>  </strong></span>

上述代碼中方法 saveCustomerAndOrderWithCascade()如果沒有session.save(customer)這一句,

執(zhí)行時(shí)會(huì)拋出PropertyValueException異常,主要原因是:

在調(diào)用session.save(order1)方法之前,order1和customer對(duì)象都是臨時(shí)的,臨時(shí)對(duì)象是由new創(chuàng)建的,都是沒有持久化的對(duì)象。假設(shè) session.save(order1)被成功執(zhí)行,order1會(huì)被成功持久化,變成持久化對(duì)象,但是Hibernate不會(huì)自動(dòng)持久化order1所關(guān)聯(lián)的customer對(duì)象。

在執(zhí)行session.save(order1)時(shí),插入ORDERS表記錄的CUSTOMER_ID字段為null,這違反了數(shù)據(jù)庫(kù)完整性約束,即ORDERS表中不允許CUSTOMER_ID為null。

疑問假設(shè)ORDERS表中CUSTOMER_ID字段允許為null:

<many-to-one          name="customer"         column="CUSTOMER_ID"         class="mypack.Customer"         not-null="false"           lazy="false"      />

這樣執(zhí)行的話,能夠成功的向ORDERS表中插入兩條數(shù)據(jù);但是當(dāng)Hibernate自動(dòng)清理(flush)緩存中所有持久化對(duì)象時(shí),又會(huì)拋出新的異常

org.hibernate.TransientObjectException:object references an unsaved transient instance -save the transient instance before flushing :mypack.Customer

所謂清理是指Hibernate按照持久化對(duì)象的屬性變化來(lái)同步更新數(shù)據(jù)庫(kù)。在清理的時(shí)候Hibernate會(huì)發(fā)現(xiàn)order1和order2都引用臨時(shí)對(duì)象customer,而在ORDERS表中CUSTOMER_ID字段為null,這就意味著內(nèi)存中持久化對(duì)象的屬性和數(shù)據(jù)庫(kù)中記錄不一致。之所以會(huì)報(bào)錯(cuò)是因?yàn)閛rder1中customer屬性引用了一個(gè)臨時(shí)對(duì)象Customer。

由此可見,Hibernate持久化一個(gè)對(duì)象時(shí),默認(rèn)情況下不會(huì)自動(dòng)持久化所關(guān)聯(lián)的其他對(duì)象。但是,我們我們希望當(dāng)Hibernate持久化Order對(duì)象時(shí)自動(dòng)持久化所關(guān)聯(lián)的Customer對(duì)象,我們可以修改映射文件如下:

<many-to-one          name="customer"         column="CUSTOMER_ID"         class="mypack.Customer"         cascade="save-update"         not-null="false"           lazy="false"      />

當(dāng)cascade屬性為“save-update”,表明保存或更新對(duì)象時(shí),會(huì)級(jí)聯(lián)保存或更新與它所關(guān)聯(lián)的對(duì)象。如上例中,執(zhí)行saveCustomerAndOrderWithCascade()時(shí),Hibernate會(huì)把order1與customer對(duì)象一起持久化,此時(shí)Hibernate會(huì)執(zhí)行

insert into CUSTOMERS(ID,NAME) values(2,"Jack");  insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) value (3,"Jack_Order001",2);

二、映射一對(duì)多雙向關(guān)聯(lián)關(guān)系

類類之間建立了聯(lián)系,就可以很方便地從一個(gè)對(duì)象導(dǎo)航到另一個(gè)或者另一組與它相關(guān)聯(lián)的對(duì)象。正如上例中,對(duì)于給定的Order對(duì)象,如果想獲得與之關(guān)聯(lián)的Customer對(duì)象,可以直接如下調(diào)用:

Customer customer=order.getCustomer();

那么對(duì)于給定的Customer對(duì)象,如何一次獲得所有與之關(guān)聯(lián)的Order對(duì)象呢?由于上例中Customer對(duì)象沒有和Order對(duì)象關(guān)聯(lián),我們也可以通過Hibernate API去查詢數(shù)據(jù)庫(kù):

List orders=session.createQuery("from Order as o where o.customer.id="+customer.getId()).list();

顯然這樣做的效率會(huì)很低,而且復(fù)雜的關(guān)聯(lián)關(guān)系也會(huì)給編程帶來(lái)影響。我們可以為Customer類和Order類簡(jiǎn)歷一對(duì)多的雙向關(guān)聯(lián)。

第一部分我們已經(jīng)建立了Order類和Customer類的多對(duì)一關(guān)聯(lián),現(xiàn)在我們?cè)僭黾覥ustomer到Order類的一對(duì)多關(guān)聯(lián)。

Customer.java文件:

package mypack;  import java.util.HashSet;   import java.util.Set;  //Hibernate要求在持久化類中定義集合類屬性時(shí),必須要把屬性聲明為接口類型。   public class Customer  implements java.io.Serializable {       private long id;       private String name;       private Set orders = new HashSet();//初始化為集合實(shí)現(xiàn)類,這樣做可以提高程序的健壯性,同時(shí)避免了應(yīng)用程序訪問取詞為null的orders集合的方法而拋出NullPointerException。      public Customer() {      }       public Customer(String name, Set orders) {         this.name = name;         this.orders = orders;      }     //省略了id,name的get和set訪問方法      public Set getOrders() {          return this.orders;      }            public void setOrders(Set orders) {          this.orders = orders;      }  }

接下來(lái)就是映射文件的配置Customer.hbm.xml:

<class name="mypack.Customer" table="CUSTOMERS" >    <id name="id" type="long" column="ID">      <generator class="increment"/>    </id>     <property name="name" type="string" >        <column name="NAME" length="15" />    </property>    <set         name="orders"       cascade="save-update"                         <key column="CUSTOMER_ID" />//表示ORDERS表通過外鍵CUSTOMER_ID參照CUSTOMERS表        <one-to-many class="mypack.Order" />     </set>      </class>

使用方法のBussiness.java演示分函數(shù)介紹:

(1)saveCustomerAndOrderWithCascade()方法:當(dāng)映射文件中<set>的屬性為“save-update”時(shí),Hibernate在持久化Customer對(duì)象時(shí)也會(huì)自動(dòng)持久化其所關(guān)聯(lián)的Order對(duì)象

  public void saveCustomerAndOrderWithCascade(){      Session session = sessionFactory.openSession();      Transaction tx = null;      try {        tx = session.beginTransaction();  /*創(chuàng)建一個(gè)customer對(duì)象和order對(duì)象*/       Customer customer=new Customer("Tom",new HashSet());        Order order=new Order();        order.setOrderNumber("Tom_Order001");  /*建立Customer與Order的一對(duì)多雙向關(guān)聯(lián)關(guān)系*/       order.setCustomer(customer);        customer.getOrders().add(order);  /*保存Customer對(duì)象*/       session.save(customer);  /* 當(dāng)映射文件中<set>的屬性為“save-update”時(shí),Hibernate在持久化Customer對(duì)象時(shí)也會(huì)自動(dòng)持久化其所關(guān)聯(lián)的Order對(duì)象   insert into CUSTOMERS(ID,NAME) values(1,"Tom");   insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) values(1,"Tom_Order001",1)*/       tx.commit();        idOfTom=customer.getId();        idOfTomOrder=order.getId();                            }catch (RuntimeException e) {        if (tx != null) {          tx.rollback();        }        e.printStackTrace();      } finally {        session.close();      }    }

當(dāng)映射文件中<set>的屬性為“save-update”時(shí),Hibernate在持久化Customer對(duì)象時(shí)也會(huì)自動(dòng)持久化其所關(guān)聯(lián)的Order對(duì)象

insert into CUSTOMERS(ID,NAME) values(1,"Tom");  insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) values(1,"Tom_Order001",1)

(2)printOrdersOfCustomer(Long customerId)方法:打印與指定customerId關(guān)聯(lián)的所有Order對(duì)象

public void printOrdersOfCustomer(Long customerId){    Session session = sessionFactory.openSession();    Transaction tx = null;    try {      tx = session.beginTransaction();      Customer customer=(Customer)session.get(Customer.class,customerId);      printOrders(customer.getOrders());//使用getOrders獲取一個(gè)order對(duì)象set      tx.commit();    }catch (RuntimeException e) {      if (tx != null) {         tx.rollback();      }      throw e;    } finally {       session.close();    }  }

其調(diào)用的函數(shù)printOrders(Set orders)

public void printOrders(Set orders){       for (Iterator it = orders.iterator(); it.hasNext();) {          Order order=(Order)it.next();          System.out.println("OrderNumber of "+order.getCustomer().getName()+ " :"+order.getOrderNumber());       }   }

(3)saveCustomerAndOrderWithInverse()方法:演示映射文件<set>屬性為inverse

public void saveCustomerAndOrderWithInverse(){        saveCustomerAndOrderSeparately();        associateCustomerAndOrder();     }

調(diào)用的函數(shù)saveCustomerAndOrderSeparately():即是分別存儲(chǔ),與saveCustomerAndOrderWithCascade()方法恰好相反。

Customer customer=new Customer();  customer.setName("Jack");  Order order=new Order();  order.setOrderNumber("Jack_Order001");  session.save(customer);  session.save(order);&nbsp;  tx.commit();  idOfJack=customer.getId();  idOfJackOrder=order.getId();

為了使上述代碼正常執(zhí)行,需要確保Order.hbm.xml文件的<many-to-one>元素的not null取默認(rèn)值false,否則會(huì)出現(xiàn)異常;Hibernate會(huì)執(zhí)行如下

insert into CUSTOMERS(ID,NAME) values(2,"Jack");  insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) values(2,"Jack_Order001",null);

調(diào)用的函數(shù)associateCustomerAndOrder():該方法加載由saveCustomerAndOrderSeparately()方法持久化Customer和Order對(duì)象,然后建立兩者之間的一對(duì)多的關(guān)系

public void associateCustomerAndOrder(){     Session session = sessionFactory.openSession();     Transaction tx = null;     try {       tx = session.beginTransaction();       /*加載持久化對(duì)象Customer、Order*/      Customer customer=(Customer)session.load(Customer.class,idOfJack);       Order order=(Order)session.load(Order.class,idOfJackOrder);       /*建立Customer和Order的關(guān)聯(lián)關(guān)系*/      order.setCustomer(customer);       customer.getOrders().add(order);       tx.commit();     }catch (RuntimeException e) {       if (tx != null) {         tx.rollback();       }        e.printStackTrace();     } finally {       session.close();     }   }

Hibernate映射一對(duì)多關(guān)聯(lián)關(guān)系是什么

這樣重復(fù)執(zhí)行多余的SQL語(yǔ)句會(huì)影響java應(yīng)用的性能,解決的方法是將<set>的inverse屬性設(shè)為true。因此修改Customer.hbm.xml文件:

<set           name="orders"         inverse="true"         cascade="save-update"           >                    <key column="CUSTOMER_ID" />//表示ORDERS表通過外鍵CUSTOMER_ID參照CUSTOMERS表          <one-to-many class="mypack.Order" />  </set>

(4)級(jí)聯(lián)刪除:

tx = session.beginTransaction();        Customer customer=(Customer)session.load(Customer.class,customerId);        session.delete(customer);        tx.commit();

如果要?jiǎng)h除Customer所關(guān)聯(lián)的Order對(duì)象的話,需要將cascade屬性設(shè)置為delete,如下:

<set          name="orders"        inverse="true"        cascade="delete"          >  <key column="CUSTOMER_ID" />         <one-to-many class="mypack.Order" />      </set>

執(zhí)行后,Hibernate會(huì)做以下動(dòng)作:

delete from ORDERS where CUSTOMER_ID=2;  delete from CUSTOMERS where ID=2;

如果關(guān)聯(lián)雙方是父子關(guān)系,就可以把復(fù)方的cascade設(shè)置為all-delete-orphan;這樣刪除父方對(duì)象時(shí)就會(huì)級(jí)聯(lián)刪除所有關(guān)聯(lián)的子方對(duì)象。

三、映射一對(duì)多雙向自身關(guān)聯(lián)關(guān)Hibernate映射一對(duì)多關(guān)聯(lián)關(guān)系是什么

Hibernate映射一對(duì)多關(guān)聯(lián)關(guān)系是什么

Hibernate映射一對(duì)多關(guān)聯(lián)關(guān)系是什么

Category.java:

package mypack;  import java.util.HashSet;  import java.util.Set;  public class Category  implements java.io.Serializable {       private long id;       private String name;       private Set childCategories = new HashSet(0);       private Category parentCategory;       public Category() {      }       public Category(String name, Set childCategories, Category parentCategory) {         this.name = name;         this.childCategories = childCategories;         this.parentCategory = parentCategory;      }           public long getId() {          return this.id;      }            public void setId(long id) {          this.id = id;      }      public String getName() {          return this.name;      }            public void setName(String name) {          this.name = name;      }      public Set getChildCategories() {          return this.childCategories;      }            public void setChildCategories(Set childCategories) {          this.childCategories = childCategories;      }      public Category getParentCategory() {          return this.parentCategory;      }            public void setParentCategory(Category parentCategory) {          this.parentCategory = parentCategory;      }  }

配置文件Category.hbm.xml:

<?xml version="1.0"?>  <!DOCTYPE hibernate-mapping  PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  <hibernate-mapping >     <class name="mypack.Category" table="CATEGORIES" >      <id name="id" type="long" column="ID">        <generator class="increment"/>      </id>       <property name="name" type="string" >          <column name="NAME" length="15" />      </property>       <set           name="childCategories"         cascade="save-update"         inverse="true"         >          <key column="CATEGORY_ID" />          <one-to-many class="mypack.Category" />       </set>         <many-to-one          name="parentCategory"         column="CATEGORY_ID"         class="mypack.Category"        />     </class>   </hibernate-mapping>

感謝各位的閱讀,以上就是“Hibernate映射一對(duì)多關(guān)聯(lián)關(guān)系是什么”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Hibernate映射一對(duì)多關(guān)聯(lián)關(guān)系是什么這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI