zoukankan      html  css  js  c++  java
  • Hibernate中表与表之间的关联一对多,级联保存和级联删除

    1:Hibernate的一对多操作(重点)

      一对多映射配置

      第一步:创建两个实体类:客户和联系人(例)以客户为一,联系人为多:  

     1 package com.yinfu.entity;
     2 
     3 public class LinkMan {
     4 
     5     private Integer lkm_id;
     6     private String lkm_name;
     7     private String lkm_gender;
     8     private String lkm_phone;
     9     public Integer getLkm_id() {
    10         return lkm_id;
    11     }
    12     public void setLkm_id(Integer lkm_id) {
    13         this.lkm_id = lkm_id;
    14     }
    15     public String getLkm_name() {
    16         return lkm_name;
    17     }
    18     public void setLkm_name(String lkm_name) {
    19         this.lkm_name = lkm_name;
    20     }
    21     public String getLkm_gender() {
    22         return lkm_gender;
    23     }
    24     public void setLkm_gender(String lkm_gender) {
    25         this.lkm_gender = lkm_gender;
    26     }
    27     public String getLkm_phone() {
    28         return lkm_phone;
    29     }
    30     public void setLkm_phone(String lkm_phone) {
    31         this.lkm_phone = lkm_phone;
    32     }
    33 }
    LinkMan
     1 package com.yinfu.entity;
     2 
     3 public class Customer {
     4 
     5     private Integer cid;
     6     private String custName;
     7     private String custLevel;
     8     private String custSource;
     9     private String custPhone;
    10     private String custMobile;
    11     public Integer getCid() {
    12         return cid;
    13     }
    14     public void setCid(Integer cid) {
    15         this.cid = cid;
    16     }
    17     public String getCustName() {
    18         return custName;
    19     }
    20     public void setCustName(String custName) {
    21         this.custName = custName;
    22     }
    23     public String getCustLevel() {
    24         return custLevel;
    25     }
    26     public void setCustLevel(String custLevel) {
    27         this.custLevel = custLevel;
    28     }
    29     public String getCustSource() {
    30         return custSource;
    31     }
    32     public void setCustSource(String custSource) {
    33         this.custSource = custSource;
    34     }
    35     public String getCustPhone() {
    36         return custPhone;
    37     }
    38     public void setCustPhone(String custPhone) {
    39         this.custPhone = custPhone;
    40     }
    41     public String getCustMobile() {
    42         return custMobile;
    43     }
    44     public void setCustMobile(String custMobile) {
    45         this.custMobile = custMobile;
    46     }
    47 }
    Customer

      第二步:让两个实体类之间互相表示

        在客户实体类里面表示多个联系人(一个客户里面多个联系人):

     1 package com.yinfu.entity;
     2 
     3 import java.util.HashSet;
     4 import java.util.Set;
     5 
     6 public class Customer {
     7 
     8     private Integer cid;
     9     private String custName;
    10     private String custLevel;
    11     private String custSource;
    12     private String custPhone;
    13     private String custMobile;
    14     
    15     //客户中表示多个联系人,一个客户有多个联系人
    16     //Hibernate要求使用集合表示多的数据,有set表示(set无序,主要是set可有重复元素)
    17     private Set<LinkMan> setLinkMan = new HashSet<LinkMan>();
    18     
    19     public Set<LinkMan> getSetLinkMan() {
    20         return setLinkMan;
    21     }
    22     public void setSetLinkMan(Set<LinkMan> setLinkMan) {
    23         this.setLinkMan = setLinkMan;
    24     }
    25     public Integer getCid() {
    26         return cid;
    27     }
    28     public void setCid(Integer cid) {
    29         this.cid = cid;
    30     }
    31     public String getCustName() {
    32         return custName;
    33     }
    34     public void setCustName(String custName) {
    35         this.custName = custName;
    36     }
    37     public String getCustLevel() {
    38         return custLevel;
    39     }
    40     public void setCustLevel(String custLevel) {
    41         this.custLevel = custLevel;
    42     }
    43     public String getCustSource() {
    44         return custSource;
    45     }
    46     public void setCustSource(String custSource) {
    47         this.custSource = custSource;
    48     }
    49     public String getCustPhone() {
    50         return custPhone;
    51     }
    52     public void setCustPhone(String custPhone) {
    53         this.custPhone = custPhone;
    54     }
    55     public String getCustMobile() {
    56         return custMobile;
    57     }
    58     public void setCustMobile(String custMobile) {
    59         this.custMobile = custMobile;
    60     }
    61 }
    Customer

        联系人实体类里面表示所属客户(一个联系人只能属于一个客户):

     1 package com.yinfu.entity;
     2 
     3 public class LinkMan {
     4 
     5     private Integer lkm_id;
     6     private String lkm_name;
     7     private String lkm_gender;
     8     private String lkm_phone;
     9     
    10     //在联系人中表示所属客户,一个联系人对应一个客户
    11     private Customer customer;
    12     
    13     public Customer getCustomer() {
    14         return customer;
    15     }
    16     public void setCustomer(Customer customer) {
    17         this.customer = customer;
    18     }
    19     public Integer getLkm_id() {
    20         return lkm_id;
    21     }
    22     public void setLkm_id(Integer lkm_id) {
    23         this.lkm_id = lkm_id;
    24     }
    25     public String getLkm_name() {
    26         return lkm_name;
    27     }
    28     public void setLkm_name(String lkm_name) {
    29         this.lkm_name = lkm_name;
    30     }
    31     public String getLkm_gender() {
    32         return lkm_gender;
    33     }
    34     public void setLkm_gender(String lkm_gender) {
    35         this.lkm_gender = lkm_gender;
    36     }
    37     public String getLkm_phone() {
    38         return lkm_phone;
    39     }
    40     public void setLkm_phone(String lkm_phone) {
    41         this.lkm_phone = lkm_phone;
    42     }
    43 }
    LinkMan

      第三步:配置映射关系(映射文件)

        在映射文件中配置一对多关系

        Customer的配置文件:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <!DOCTYPE hibernate-mapping PUBLIC 
     3     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
     4     "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
     5 <hibernate-mapping>
     6 
     7     <class name="com.yinfu.entity.Customer" table="t_customer">
     8         <id name="cid" column="cid">
     9             <generator class="native"></generator>
    10         </id>
    11         <property name="custName" column="custName"></property>
    12         <property name="custLevel" column="custLevel"></property>
    13         <property name="custSource" column="custSource"></property>
    14         <property name="custPhone" column="custPhone"></property>
    15         <property name="custMobile" column="custMobile"></property>
    16         <!-- 客户配置文件中表示所有的联系人
    17             set标签表示所有联系人
    18             set标签中的name属性:写在客户实体类里面表示所有联系人的set集合名称
    19          -->
    20          <set name="setLinkMan" cascade="save-update,delete" inverse="true">
    21              <!-- 一对多建表有外键
    22                  Hibernate机制:双向维护外键,在一和多那方都配置外键
    23                  column属性:外键值
    24               -->
    25              <key column="clid"></key>
    26              <!-- 客户表表示的所有联系人,class表示联系人实体类的全类名 -->
    27              <one-to-many class="com.yinfu.entity.LinkMan"/>
    28          </set>
    29     </class>
    30     
    31 </hibernate-mapping>
    Customer.hbm.xml

        LinkMan的配置文件:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <!DOCTYPE hibernate-mapping PUBLIC 
     3     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
     4     "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
     5 <hibernate-mapping>
     6 
     7     <class name="com.yinfu.entity.LinkMan" table="t_linkman">
     8         <id name="lkm_id" column="lkm_id">
     9             <generator class="native"></generator>
    10         </id>
    11         <property name="lkm_name" column="lkm_name"></property>
    12         <property name="lkm_gender" column="lkm_gender"></property>
    13         <property name="lkm_phone" column="lkm_phone"></property>
    14         <!-- 表示联系人所属的客户
    15             name属性:在联系人实体类中用来表示客户的字段名
    16             class属性:客户实体类的全类名
    17             column属性:外键名,要与客户映射文件中的外键名相同
    18          -->
    19         <many-to-one name="customer" class="com.yinfu.entity.Customer" column="clid"></many-to-one>
    20     </class>
    21     
    22 </hibernate-mapping>
    LinkMan.hbm.xml

      第四步:创建核心配置文件

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <!DOCTYPE hibernate-configuration PUBLIC
     3     "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
     4     "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
     5 <hibernate-configuration>
     6 <!-- 此配置文件的文件名和位置是固定的的
     7     文件名:hibernate.cfg.xml
     8     位置:要写在src文件中
     9  -->
    10     <session-factory>
    11         <!-- 第一部分:配置数据库信息 -->
    12         <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    13         <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/test</property>
    14         <property name="hibernate.connection.username">root</property>
    15         <property name="hibernate.connection.password">song12345</property>
    16         
    17         <!-- 第二部分:配置hibernate信息(可有可无) -->
    18         <!-- 输出底层的SQL语句 -->
    19         <property name="hibernate.show_sql">true</property>
    20         <!-- 对底曾语句进行格式化 -->
    21         <property name="hibernate.format_sql">true</property>
    22         <!-- hibernate帮创建表,需要配置
    23             update:如果有表就更新,没有表就创建
    24          -->
    25         <property name="hibernate.hbm2ddl.auto">update</property>
    26         <!-- 配置数据库的方言 
    27             识别不同数据库中的特有的语句和关键字
    28             比如:分页查询
    29                     MySQL关键字是limit
    30                     oracle中的使用的是top-n分析中的rownum
    31         -->
    32         <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    33         <!-- 指定线程管理方式,与本地线程进行绑定,实现单线程操作 -->
    34         <property name="hibernate.current_session_context_class">thread</property>
    35     
    36         <!-- 第三部分:把映射文件放到核心配置文件中 -->
    37         <mapping resource="com/yinfu/entity/Customer.hbm.xml"/>
    38         <mapping resource="com/yinfu/entity/LinkMan.hbm.xml"/>
    39     </session-factory>
    40     
    41 </hibernate-configuration>
    hibernate.cfg.xml

      创建工具类生成SessionFactory和session对象

     1 package com.yinfu.utils;
     2 
     3 import org.hibernate.Session;
     4 import org.hibernate.SessionFactory;
     5 import org.hibernate.cfg.Configuration;
     6 
     7 //工具类
     8 public class HibernateUtils {
     9 
    10     private final static Configuration cfg;
    11     private final static SessionFactory sessionFactory;
    12     
    13     //用静态代码块来实现对象只在类加载的时候创建一次(静态代码块只执行一次)
    14     static{
    15         //创建configuration对象,
    16         cfg = new Configuration();
    17         cfg.configure();
    18         //根据Configuration对象创建sessionFactory对象
    19         sessionFactory = cfg.buildSessionFactory();
    20     }
    21     
    22     //返回与本地线程绑定的session
    23     public static Session getSession(){
    24         return sessionFactory.getCurrentSession();
    25     }
    26     
    27     //创建一个方法用于返回sessionFactory对象
    28     public static SessionFactory getSessionFactory(){
    29         return sessionFactory;
    30     }
    31     
    32     
    33 }
    HibernateUtils

    在工具类中直接写一个main方法,内部不写代码,直接执行,生成两个表,而外键是在一对多的多中的表上创建的

     (一:级联保存):

    复杂的测试类:

     1 package com.yinfu.test;
     2 
     3 import java.util.List;
     4 
     5 import org.hibernate.SQLQuery;
     6 import org.hibernate.Session;
     7 import org.hibernate.SessionFactory;
     8 import org.hibernate.Transaction;
     9 import org.junit.Test;
    10 
    11 import com.yinfu.entity.Customer;
    12 import com.yinfu.entity.LinkMan;
    13 import com.yinfu.entity.User;
    14 import com.yinfu.utils.HibernateUtils;
    15 
    16 public class HibernateOneToMany {
    17 
    18     //一对多的级联保存
    19     @Test
    20     public void testAddDemo(){
    21         SessionFactory sessionFactory = null;
    22         Session session = null;
    23         Transaction tx = null;
    24         try {
    25             sessionFactory = HibernateUtils.getSessionFactory();
    26             session = sessionFactory.openSession();
    27             tx = session.beginTransaction();
    28             
    29             //添加客户,为这个客户添加一个联系人
    30             //1:创建客户和联系人对象
    31             Customer customer = new Customer();
    32             customer.setCustName("传智播客");
    33             customer.setCustLevel("VIP");
    34             customer.setCustSource("网络");
    35             customer.setCustMobile("110");
    36             customer.setCustPhone("911");
    37             
    38             LinkMan linkMan = new LinkMan();
    39             linkMan.setLkm_name("张三");
    40             linkMan.setLkm_gender("男");
    41             linkMan.setLkm_phone("123");;
    42             
    43             //2:客户里面表示所有联系人,联系人里面表示客户
    44             //建立客户对象和联系人对象关系
    45             //2.1:把联系人对象放到客户对象中的set集合里面
    46             customer.getSetLinkMan().add(linkMan);
    47             //2.2把客户对象放到联系人中的Customer属性中
    48             linkMan.setCustomer(customer);
    49             
    50             //3:保存到数据库
    51             session.save(customer);
    52             session.save(linkMan);
    53             
    54             tx.commit();
    55         } catch (Exception e) {
    56             e.printStackTrace();
    57             tx.rollback();
    58         }finally{
    59             session.close();
    60             sessionFactory.close();
    61         }
    62     }
    63 }
    HibernateOneToMany

    简化做法:

    首先在Customer的配置文件Customer.hbm.xml文件中的set标签上添加一个cascade属性值为save-update:

    然后在测试类中这样修改一下就行:

    (二:级联删除)

    删除客户的同时将客户对应的联系人全部删除

    首先在Customer的配置文件Customer.hbm.xml文件中的set标签上添加一个cascade属性值为delete,如果cascade属性有多个值,用英文逗号隔开;

    测试代码:根据ID查出customer在调用delete方法就行:

     1 Hibernate: 
     2     select
     3         customer0_.cid as cid1_0_0_,
     4         customer0_.custName as custName2_0_0_,
     5         customer0_.custLevel as custLeve3_0_0_,
     6         customer0_.custSource as custSour4_0_0_,
     7         customer0_.custPhone as custPhon5_0_0_,
     8         customer0_.custMobile as custMobi6_0_0_ 
     9     from
    10         t_customer customer0_ 
    11     where
    12         customer0_.cid=?
    13 Hibernate: 
    14     select
    15         setlinkman0_.clid as clid5_1_0_,
    16         setlinkman0_.lkm_id as lkm_id1_1_0_,
    17         setlinkman0_.lkm_id as lkm_id1_1_1_,
    18         setlinkman0_.lkm_name as lkm_name2_1_1_,
    19         setlinkman0_.lkm_gender as lkm_gend3_1_1_,
    20         setlinkman0_.lkm_phone as lkm_phon4_1_1_,
    21         setlinkman0_.clid as clid5_1_1_ 
    22     from
    23         t_linkman setlinkman0_ 
    24     where
    25         setlinkman0_.clid=?
    26 Hibernate: 
    27     update
    28         t_linkman 
    29     set
    30         clid=null 
    31     where
    32         clid=?
    33 Hibernate: 
    34     delete 
    35     from
    36         t_linkman 
    37     where
    38         lkm_id=?
    39 Hibernate: 
    40     delete 
    41     from
    42         t_customer 
    43     where
    44         cid=?
    内部SQL执行过程

     (一对多修改操作)

     执行结果底层的SQL语句:(这样存在双向维护外键,存在性能不足)

     1 Hibernate: 
     2     select
     3         customer0_.cid as cid1_0_0_,
     4         customer0_.custName as custName2_0_0_,
     5         customer0_.custLevel as custLeve3_0_0_,
     6         customer0_.custSource as custSour4_0_0_,
     7         customer0_.custPhone as custPhon5_0_0_,
     8         customer0_.custMobile as custMobi6_0_0_ 
     9     from
    10         t_customer customer0_ 
    11     where
    12         customer0_.cid=?
    13 Hibernate: 
    14     select
    15         linkman0_.lkm_id as lkm_id1_1_0_,
    16         linkman0_.lkm_name as lkm_name2_1_0_,
    17         linkman0_.lkm_gender as lkm_gend3_1_0_,
    18         linkman0_.lkm_phone as lkm_phon4_1_0_,
    19         linkman0_.clid as clid5_1_0_ 
    20     from
    21         t_linkman linkman0_ 
    22     where
    23         linkman0_.lkm_id=?
    24 Hibernate: 
    25     select
    26         setlinkman0_.clid as clid5_1_0_,
    27         setlinkman0_.lkm_id as lkm_id1_1_0_,
    28         setlinkman0_.lkm_id as lkm_id1_1_1_,
    29         setlinkman0_.lkm_name as lkm_name2_1_1_,
    30         setlinkman0_.lkm_gender as lkm_gend3_1_1_,
    31         setlinkman0_.lkm_phone as lkm_phon4_1_1_,
    32         setlinkman0_.clid as clid5_1_1_ 
    33     from
    34         t_linkman setlinkman0_ 
    35     where
    36         setlinkman0_.clid=?
    37 Hibernate: 
    38   下面进行了两次修改,由于Hibernate是双向维护外键,所以,客户和联系人中都要进行外键维护,存在性能不足
    39   update
    40         t_linkman 
    41     set
    42         lkm_name=?,
    43         lkm_gender=?,
    44         lkm_phone=?,
    45         clid=? 
    46     where
    47         lkm_id=?
    48 Hibernate: 
    49     update
    50         t_linkman 
    51     set
    52         clid=? 
    53     where
    54         lkm_id=?
    底层SQL语句

    性能优化,解决方法,在一对多中让一放弃外键维护,即让customer放弃外键维护

    具体实现:

    在需要放弃外键的对象的映射文件中的set标签中进行配置,添加inverse属性:默认值是false,修改为true,就是放弃外键维护

    底层SQL的变化:

  • 相关阅读:
    动态Webapi参考资料
    解决异步事务好文章
    .net core 插件开发
    端口被占用代码
    性能测试
    .NET/.NET Core 单元测试:Specflow
    Autofac 替换默认控制器骚操作
    Swagger非常好的文章
    sqlserver入门到精通(2016安装教程)
    springboot 学习之路 27(实现ip白名单功能)
  • 原文地址:https://www.cnblogs.com/lubolin/p/7767399.html
Copyright © 2011-2022 走看看