• 3.4 Hibernate多表操作


    1. 一对多、多对一

    Hibernate框架实现了ORM的思想,将关系数据库中表的数据映射成对象,使开发人员把对数据库的操作转化为对对象的操作,Hibernate的关联关系映射主要包括多表的映射配置、数据的增加、删除等。

    数据库中多表之间存在着三种关系,也就是系统设计中的三种实体关系。如图所示。

    从图可以看出,系统设计的三种实体关系分别为:多对多、一对多和一对一关系。在数据库中,实体表之间的关系映射是采用外键来描述的,具体如下:

      1. 1对多  建表原则:在多的一方创建外键指向一的一方的主键:

     1 package com.eagle.domain;
     2 
     3 import java.util.HashSet;
     4 import java.util.Set;
     5 
     6 public class Customer {
     7 
     8     private Long cust_id;
     9     private String cust_name;
    10     private String cust_source;
    11     private String cust_industry;
    12     private String cust_level;
    13     private String cust_phone;
    14     private String cust_mobile;
    15     private Set<LinkMan> linkmans = new HashSet<LinkMan>();        // 一个客户有多个联系人,客户中应该放有联系人的集合
    16 
    17     public Long getCust_id() {
    18         return cust_id;
    19     }
    20 
    21     public void setCust_id(Long cust_id) {
    22         this.cust_id = cust_id;
    23     }
    24 
    25     public String getCust_name() {
    26         return cust_name;
    27     }
    28 
    29     public void setCust_name(String cust_name) {
    30         this.cust_name = cust_name;
    31     }
    32 
    33     public String getCust_source() {
    34         return cust_source;
    35     }
    36 
    37     public void setCust_source(String cust_source) {
    38         this.cust_source = cust_source;
    39     }
    40 
    41     public String getCust_industry() {
    42         return cust_industry;
    43     }
    44 
    45     public void setCust_industry(String cust_industry) {
    46         this.cust_industry = cust_industry;
    47     }
    48 
    49     public String getCust_level() {
    50         return cust_level;
    51     }
    52 
    53     public void setCust_level(String cust_level) {
    54         this.cust_level = cust_level;
    55     }
    56 
    57     public String getCust_phone() {
    58         return cust_phone;
    59     }
    60 
    61     public void setCust_phone(String cust_phone) {
    62         this.cust_phone = cust_phone;
    63     }
    64 
    65     public String getCust_mobile() {
    66         return cust_mobile;
    67     }
    68 
    69     public void setCust_mobile(String cust_mobile) {
    70         this.cust_mobile = cust_mobile;
    71     }
    72     
    73     public Set<LinkMan> getLinkmans() {
    74         return linkmans;
    75     }
    76 
    77     public void setLinkmans(Set<LinkMan> linkmans) {
    78         this.linkmans = linkmans;
    79     }
    80 
    81     @Override
    82     public String toString() {
    83         return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + ", cust_source=" + cust_source
    84                 + ", cust_industry=" + cust_industry + ", cust_level=" + cust_level + ", cust_phone=" + cust_phone
    85                 + ", cust_mobile=" + cust_mobile + "]";
    86     }
    87 
    88 }
    Customer
     1 package com.eagle.domain;
     2 
     3 public class LinkMan {
     4 
     5     private Long lkm_id;
     6     private String lkm_name;
     7     private String lkm_gender;
     8     private String lkm_phone;
     9     private String lkm_mobile;
    10     private String lkm_email;
    11     private String lkm_qq;
    12     private String lkm_position;
    13     private String lkm_memo;
    14     private Customer customer;     // Hibernate是一个ORM的框架:在关系型的数据库中描述表与表之间的关系,使用的是外键,开发语言使用是Java,面向对象的。
    15     public Long getLkm_id() {
    16         return lkm_id;
    17     }
    18     public void setLkm_id(Long lkm_id) {
    19         this.lkm_id = lkm_id;
    20     }
    21     public String getLkm_name() {
    22         return lkm_name;
    23     }
    24     public void setLkm_name(String lkm_name) {
    25         this.lkm_name = lkm_name;
    26     }
    27     public String getLkm_gender() {
    28         return lkm_gender;
    29     }
    30     public void setLkm_gender(String lkm_gender) {
    31         this.lkm_gender = lkm_gender;
    32     }
    33     public String getLkm_phone() {
    34         return lkm_phone;
    35     }
    36     public void setLkm_phone(String lkm_phone) {
    37         this.lkm_phone = lkm_phone;
    38     }
    39     public String getLkm_mobile() {
    40         return lkm_mobile;
    41     }
    42     public void setLkm_mobile(String lkm_mobile) {
    43         this.lkm_mobile = lkm_mobile;
    44     }
    45     public String getLkm_email() {
    46         return lkm_email;
    47     }
    48     public void setLkm_email(String lkm_email) {
    49         this.lkm_email = lkm_email;
    50     }
    51     public String getLkm_qq() {
    52         return lkm_qq;
    53     }
    54     public void setLkm_qq(String lkm_qq) {
    55         this.lkm_qq = lkm_qq;
    56     }
    57     public String getLkm_position() {
    58         return lkm_position;
    59     }
    60     public void setLkm_position(String lkm_position) {
    61         this.lkm_position = lkm_position;
    62     }
    63     public String getLkm_memo() {
    64         return lkm_memo;
    65     }
    66     public void setLkm_memo(String lkm_memo) {
    67         this.lkm_memo = lkm_memo;
    68     }
    69     public Customer getCustomer() {
    70         return customer;
    71     }
    72     public void setCustomer(Customer customer) {
    73         this.customer = customer;
    74     }
    75     @Override
    76     public String toString() {
    77         return "LinkMan [lkm_id=" + lkm_id + ", lkm_name=" + lkm_name + ", lkm_gender=" + lkm_gender + ", lkm_phone="
    78                 + lkm_phone + ", lkm_mobile=" + lkm_mobile + ", lkm_email=" + lkm_email + ", lkm_qq=" + lkm_qq
    79                 + ", lkm_position=" + lkm_position + ", lkm_memo=" + lkm_memo + ", customer=" + customer + "]";
    80     }
    81     
    82 }
    LinkMan
     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
     3 <hibernate-mapping>
     4     <class name="com.eagle.domain.Customer" table="cst_customer">
     5         <id name="cust_id" column="cust_id" >
     6             <generator class="native"></generator>
     7         </id>    
     8         <property name="cust_name" column="cust_name"></property>
     9         <property name="cust_source" column="cust_source"></property>
    10         <property name="cust_industry" column="cust_industry"></property>
    11         <property name="cust_level" column="cust_level"></property>
    12         <property name="cust_phone" column="cust_phone"></property>
    13         <property name="cust_mobile" column="cust_mobile"></property>
    14         
    15         <!-- 集合,一对多关系,在配置文件中配置 -->
    16         <!--
    17             name属性:    集合属性名
    18             column属性:    外键列名
    19             class属性:    与我关联的对象完整类名
    20         -->
    21         <set name="linkmans">
    22             <key column="lkm_cust_id"></key>
    23             <one-to-many class="com.eagle.domain.LinkMan" />
    24         </set>
    25     </class>
    26 </hibernate-mapping>
    Customer.hbm.xml
     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
     3 <hibernate-mapping>
     4     <class name="com.eagle.domain.LinkMan" table="cst_linkman">
     5         <id name="lkm_id" column="lkm_id">
     6             <generator class="native"></generator>
     7         </id>
     8         <property name="lkm_name"></property>
     9         <property name="lkm_gender"></property>
    10         <property name="lkm_phone"></property>
    11         <property name="lkm_mobile"></property>
    12         <property name="lkm_email"></property>
    13         <property name="lkm_qq"></property>
    14         <property name="lkm_position"></property>
    15         <property name="lkm_memo"></property>
    16         
    17         <!-- 
    18             name属性:    引用属性名
    19             column:        外键列名
    20             class属性:    与我关联的对象完整类名
    21          -->
    22         <many-to-one name="customer" column="lkm_cust_id" class="com.eagle.domain.Customer">
    23         </many-to-one>
    24     </class>
    25 </hibernate-mapping>
    LinkMan.hbm.xml
     1 package com.eagle.test;
     2 
     3 
     4 import org.hibernate.Session;
     5 import org.hibernate.Transaction;
     6 import org.junit.Test;
     7 
     8 import com.eagle.domain.Customer;
     9 import com.eagle.domain.LinkMan;
    10 import com.eagle.utils.HibernateUtils;
    11 
    12 public class HibernateTest {
    13 
    14     private Session session = HibernateUtils.getCurrentSession();
    15     
    16     @Test
    17     public void test() {
    18         Transaction tx = session.beginTransaction();
    19         // 创建一个客户
    20         Customer customer = new Customer();
    21         customer.setCust_name("百度");
    22         // 创建3个联系人
    23         LinkMan linkMan1 = new LinkMan();
    24         linkMan1.setLkm_name("姜总");
    25         LinkMan linkMan2 = new LinkMan();
    26         linkMan2.setLkm_name("李秘书");
    27         LinkMan linkMan3 = new LinkMan();
    28         linkMan3.setLkm_name("王助理");
    29         customer.getLinkmans().add(linkMan1);
    30         customer.getLinkmans().add(linkMan2);
    31         customer.getLinkmans().add(linkMan3);
    32         
    33         session.save(customer);
    34         session.save(linkMan1);
    35         session.save(linkMan2);
    36         session.save(linkMan3);
    37         tx.commit();
    38     }
    39 
    40 }
    test

      1. 使用set集合来描述Customer.java类中的属性linkMans。在Hibernate的映射文件中,使用<set>标签用来描述被映射类中的Set集合,<key>标签的column属性值对应文件多的一方的外键名称,在Customer.java客户类中,客户与联系人是一对多的关系,Hibernate的映射文件中,使用<one-to-many>标签来描述持久化类的一对多关联,其中class属性用来描述映射的关联类。

      2. <many-to-one>标签定义两个持久化类的关联,这种关联是数据表间的多对一关联,联系人与客户就是多对一的关系,所以用<many-to-one>标签来描述。<many-to-one>标签的name属性用来描述customer在LinkMan.java类中的属性的名称,class属性用来指定映射的类,column属性值对应表中的外键列名。

       3. 这就是双向关联,那么既然已经进行了双向的关联关系设置,那么我们还保存了双方,那如果我们只保存一方是否可以呢?也就是说我们建立了双向的维护关系,只保存客户或者只保存联系人是否可以。测试一下:

     1 @Test
     2     public void test1() {
     3         Transaction tx = session.beginTransaction();
     4         // 创建一个客户
     5         Customer customer = new Customer();
     6         customer.setCust_name("百度");
     7         // 创建3个联系人
     8         LinkMan linkMan1 = new LinkMan();
     9         linkMan1.setLkm_name("姜总");
    10         LinkMan linkMan2 = new LinkMan();
    11         linkMan2.setLkm_name("李秘书");
    12         LinkMan linkMan3 = new LinkMan();
    13         linkMan3.setLkm_name("王助理");
    14         customer.getLinkmans().add(linkMan1);
    15         customer.getLinkmans().add(linkMan2);
    16         customer.getLinkmans().add(linkMan3);
    17         
    18         session.save(customer);
    19 //        session.save(linkMan1);
    20 //        session.save(linkMan2);
    21 //        session.save(linkMan3);
    22         tx.commit();
    23     }
    test1

    这样操作显然不行,导致出现异常:瞬时对象异常,一个持久太对象关联了一个瞬时态对象,那就说明我们不能只保存一方。那如果我们就想只保存一个方向应该如何进行操作呢?

    我们可以使用Hibernate的级联操作。

    2. Hibernate的级联操作

    它的目的就是为了简化操作,少写两行代码.

    一定要用,save-update,不建议使用delete.


    2.1 级联保存或更新,save-update

    级联操作是指当主控方执行保存、更新或者删除操作时,其关联对象(被控方)也执行相同的操作。

    在映射文件中通过对cascade属性的设置来控制是否对关联对象采用级联操作,级联操作对各种关联关系都是有效的。

    级联是由方向性的,所谓的方向性指的是,在保存一的一方级联多的一方和在保存多的一方级联一的一方。

    首先要确定我们要保存的主控方是哪一方,我们要保存客户,所以客户是主控方,那么需要在客户的映射文件中进行如下配置:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
     3 <hibernate-mapping>
     4     <class name="com.eagle.domain.LinkMan" table="cst_linkman">
     5         <id name="lkm_id" column="lkm_id">
     6             <generator class="native"></generator>
     7         </id>
     8         <property name="lkm_name"></property>
     9         <property name="lkm_gender"></property>
    10         <property name="lkm_phone"></property>
    11         <property name="lkm_mobile"></property>
    12         <property name="lkm_email"></property>
    13         <property name="lkm_qq"></property>
    14         <property name="lkm_position"></property>
    15         <property name="lkm_memo"></property>
    16         
    17         <!-- 
    18             name属性:    引用属性名
    19             column:        外键列名
    20             class属性:    与我关联的对象完整类名
    21          -->
    22         <many-to-one name="customer" column="lkm_cust_id" class="com.eagle.domain.Customer" cascade="save-update">
    23         </many-to-one>
    24     </class>
    25 </hibernate-mapping>
    LinkMan.hbm.xml

    值得注意的是,无论是哪一方维护数据,都必须要与另一方建立关系,才能自动保存和更新:

      1. 假如customer维护数据,就必须customer与linkMan建立关系,然后只需要保存customer就可以了:

      

      customer.getLinkmans().add(linkMan1);

    customer.getLinkmans().add(linkMan2);

    customer.getLinkmans().add(linkMan3);

      session.save(customer);

      2. 反之如果linkMan维护数据,就必须与customer建立关系,然后只需保存linkMan就可以了:

      linkMan1.setCustomer(customer);

      session.save(linkMan1);

     1 @Test
     2     public void test3() {
     3         Transaction tx = session.beginTransaction();
     4         // 创建一个客户
     5         Customer customer = new Customer();
     6         customer.setCust_name("百度");
     7         // 创建3个联系人
     8         LinkMan linkMan1 = new LinkMan();
     9         linkMan1.setLkm_name("姜总");
    10         LinkMan linkMan2 = new LinkMan();
    11         linkMan2.setLkm_name("李秘书");
    12         LinkMan linkMan3 = new LinkMan();
    13         linkMan3.setLkm_name("王助理");        
    14         // 建立关系
    15         linkMan1.setCustomer(customer);
    16         customer.getLinkmans().add(linkMan2);
    17         customer.getLinkmans().add(linkMan3);        
    18 //        session.save(linkMan1);
    19 //        session.save(customer);
    20         session.save(linkMan2);
    21         tx.commit();
    22     }

      如果单独执行18行,会发现有4条insert语句:因为linkMan1关联了客户,客户又关联了linkMan2和linkMan3,所以当保存linkMan1的时候,linkMan1是可以进入到数据库的,它关联的customer也是会进入到数据库的,linkMan2和linkMan3也同样会进入到数据库,所以有4条insert语句;

     

      如果单独执行19行,这时我们保存的是customer对象,customer进入数据库,与之关联的linkMan2和linkMan3也同样进入到数据库,所以是三条insert语句;

      如果单独执行20行,只会执行1条insert语句,即linkMan2进入到数据库,但是linkMan2没有customer与之建立关系,所以客户不会保存到数据库。

    2.2 Hibernate级联删除----delete

    级联删除也是有方向性的,删除customer的同时级联删除linkMan,也可以删除联系人同时级联删除客户(这种需求很少)。

    原来JDBC中删除客户和联系人的时候,如果有外键关系是不可以删除的,但是现在我们使用了Hibernate,其实Hibernate可以实现这样的功能,但是不会删除customer同时删除联系人,默认情况下Hibernate会怎么做?

    1     @Test
    2     public void test4() {
    3         Transaction tx = session.beginTransaction();
    4         Customer customer = session.get(Customer.class, 1l);
    5         session.delete(customer);
    6         tx.commit();
    7     }
    test4

    默认情况下如果客户下面还有联系人,Hibernate会将联系人的外键置为null,然后去删除客户。那么其实有的时候我们需要删除客户的时候,同时将客户关联的联系人一并删除。这个时候我们就需要使用Hibernate的级联表保存操作了。

    <set name="linkmans" cascade="delete">

    <key column="lkm_cust_id"></key>

    <one-to-many class="com.eagle.domain.LinkMan" />

    </set>

     

     

    测试代码:

    @Test

    public void test4() {

    Transaction tx = session.beginTransaction();

    Customer customer = session.get(Customer.class, 1l);

    session.delete(customer);

    tx.commit();

    }

     

    同样,我们删除的是联系人,那么联系人是主控方,需要在联系人端配置:

    <many-to-one name="customer" column="lkm_cust_id" class="com.eagle.domain.Customer" cascade="delete">

    </many-to-one>

     测试代码:

    @Test

    public void test5() {

    Transaction tx = session.beginTransaction();

    LinkMan linkMan1 = session.get(LinkMan.class, 3l);

    session.delete(linkMan1);

    tx.commit();

    }

     

     

    2.3 级联all操作

    cascade="delete" 相当于 save-update+delete

    2.4 级联双向关联产生多余的SQL语句

    @Test

    public void test6() {

    Transaction tx = session.beginTransaction();

    LinkMan linkMan1 = session.get(LinkMan.class, 1l);

    Customer customer = session.get(Customer.class, 2l);

    linkMan1.setCustomer(customer);

    customer.getLinkmans().add(linkMan1);

    tx.commit();

    }

     因为双向维护关系,而且持久态对象可以自动更新数据库,更新customer的时候会修改一次外键,更新联系人的时候同样也会修改一次外键。这样会产生多余的SQL:

    解决办法很简单,只需要一方放弃外键维护权即可。

    <set name="linkmans" cascade="all" inverse="true">

    <key column="lkm_cust_id"></key>

    <one-to-many class="com.eagle.domain.LinkMan" />

    </set>

    3. 多对多

    关系表达:

      1. 在表中

      2. 对象中

      3. ORM原数据中

    多表操作:

      1. 操作关联属性

      

      2. inverse属性

     

      3. 级联属性

  • 相关阅读:
    Apache Log4j 学习笔记
    关于BindingResult
    Java源码分析:深入探讨Iterator模式
    hibernate的ID生成策略(annotation方式@GeneratedValue)
    HTML5本地存储不完全指南
    pageX,clientX,offsetX,layerX的那些事
    getHibernateTemplate().execute(new HibernateCallback())方法
    jQuery.extend 与 jQuery.fn.extend 的区别
    16个优秀的JavaScript教程和工具推荐
    Spring 2.5:Spring MVC中的新特性
  • 原文地址:https://www.cnblogs.com/eaglesour/p/9502906.html
Copyright © 2020-2023  润新知