Web开发 | Hibernate - 11.Hibernate的关联关系映射之一对多映射

一、JavaWeb中一对多的设计及其建表原则

关联关系图解
关联关系图解


二、SQL的建表

hibernate_day03
新建数据库

cst_customer

CREATE TABLE `cst_customer` (
  `cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
  `cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
  `cust_user_id` bigint(32) DEFAULT NULL COMMENT '负责人id',
  `cust_create_id` bigint(32) DEFAULT NULL COMMENT '创建人id',
  `cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
  `cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
  `cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
  `cust_linkman` varchar(64) DEFAULT NULL COMMENT '联系人',
  `cust_phone` varchar(64) DEFAULT NULL COMMENT '固定电话',
  `cust_mobile` varchar(16) DEFAULT NULL COMMENT '移动电话',
  PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

cst_linkman

CREATE TABLE `cst_linkman` (
  `lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
  `lkm_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',
  `lkm_cust_id` bigint(32) DEFAULT NULL COMMENT '客户id',
  `lkm_gender` char(1) DEFAULT NULL COMMENT '联系人性别',
  `lkm_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
  `lkm_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',
  `lkm_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
  `lkm_qq` varchar(16) DEFAULT NULL COMMENT '联系人qq',
  `lkm_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',
  `lkm_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',
  PRIMARY KEY (`lkm_id`),
  KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
  CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

四、编写常规的客户和联系人的JavaBean程序与映射配置文件

JavaBean

  /Hibernate5_d03_c01/src/hibernate/domain/Customer.java
程序代码如下:

package hibernate.domain;

public class Customer {
    private Long cust_id; 
    private String cust_name; 
    private Long cust_user_id;
    private Long cust_create_id; 
    private String cust_source; 
    private String cust_industry; 
    private String cust_level; 
    private String cust_linkman; 
    private String cust_phone; 
    private String cust_mobile;

    //省略 getter 和 setter 方法

    @Override
    public String toString() {
        return "Custom [cust_id=" + cust_id + ", cust_name=" + cust_name + ", cust_user_id=" + cust_user_id
                + ", cust_create_id=" + cust_create_id + ", cust_source=" + cust_source + ", cust_industry="
                + cust_industry + ", cust_level=" + cust_level + ", cust_linkman=" + cust_linkman + ", cust_phone="
                + cust_phone + ", cust_mobile=" + cust_mobile + "]";
    }
}

  /Hibernate5_d03_c01/src/hibernate/domain/LinkMan.java
程序代码如下:

package hibernate.domain;

public class LinkMan {
    private Integer lkm_id;
    private String lkm_name;
    //  lkm_cust_id 外键,不用写
    private String lkm_gender;
    private String lkm_phone;
    private String lkm_mobile;
    private String lkm_email;
    private String lkm_qq;
    private String lkm_position;
    private String lkm_memo;

    //省略 getter 和 setter 方法
}
映射配置文件

  /Hibernate5_d03_c01/src/hibernate/domain/Customer.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>
    <!-- javabean与表之间的对应关系 -->
    <class name="hibernate.domain.Customer" table="cst_customer">
        <!-- 主键对应 -->
        <id name="cust_id" column="cust_id">
            <!-- 主键策略(与自增长相关) -->
            <generator class="native"></generator>
        </id>
        <!-- 其他字段 -->
        <property name="cust_name" column="cust_name"></property>
        <property name="cust_user_id" column="cust_user_id"></property>
        <property name="cust_create_id" column="cust_create_id"></property>
        <property name="cust_source" column="cust_source"></property>
        <property name="cust_industry" column="cust_industry"></property>
        <property name="cust_level" column="cust_level"></property>
        <property name="cust_linkman" column="cust_linkman"></property>
        <property name="cust_phone" column="cust_phone"></property>
        <property name="cust_mobile" column="cust_mobile"></property>
    </class>
</hibernate-mapping>

  /Hibernate5_d03_c01/src/hibernate/domain/LinkMan.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>
    <!-- javabean与表之间的对应关系 -->
    <class name="hibernate.domain.LinkMan" table="cst_linkman">
        <!-- 主键对应 -->
        <id name="lkm_id" column="lkm_id">
            <!-- 主键策略 -->
            <generator class="native"></generator>
        </id>
        <!-- 其他字段 -->
        <property name="lkm_name" column="lkm_name"></property>
        <property name="lkm_gender" column="lkm_gender"></property>
        <property name="lkm_phone" column="lkm_phone"></property>
        <property name="lkm_mobile" column="lkm_mobile"></property>
        <property name="lkm_email" column="lkm_email"></property>
        <property name="lkm_qq" column="lkm_qq"></property>
        <property name="lkm_position" column="lkm_position"></property>
        <property name="lkm_memo" column="lkm_memo"></property>
    </class>
</hibernate-mapping>

  /Hibernate5_d03_c01/src/hibernate.cfg.xml
程序代码如下:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <!-- 必选配置 -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
                <!-- jdbc:mysql:///hibernate_day01 -->
        <property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/hibernate_day03</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>
                <!--数据库方言 使用的数据库类型  -->
        <property name="hibernate.dialect org.hibernate.dialect.MySQLDialect"></property>

        <!-- 可选配置 -->
                <!-- 在控制台输出sql语句 -->
        <property name="show_sql">true</property>
                <!-- 在控制台输出的sql语句格式化 -->
        <property name="hibernate.format_sql">true</property>
                <!-- 配置c3p0连接池 -->
                    <!-- C3P0 的供应商 -->
        <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
                    <!-- 最小连接 -->
        <property name="hibernate.c3p0.min_size">5</property>
                    <!-- 最大连接数 -->
        <property name="hibernate.c3p0.max_size">10</property>
                    <!-- 每 120 秒检查空闲连接 -->
        <property name="hibernate.c3p0.timeout">120</property>
                <!-- 自动建表 -->
        <property name="hibernate.hbm2ddl.auto">update</property>
                <!-- 把session绑定到当前线程中,使用getCurrentSession()获取当前session -->
        <property name="hibernate.current_session_context_class">thread</property>

        <!-- 映射文件 -->
        <mapping resource="hibernate/domain/Customer.hbm.xml"/>
        <mapping resource="hibernate/domain/LinkMan.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

五、编写Hibernate的客户和联系人的JavaBean程序与映射配置文件

JavaBean

  Hibernate框架通过JavaBean类来操作数据库,所以JavaBean类需要表现出一对多的关联关系,所以需要关联两个类。

一个客户对应多个联系人,在Java中可以用集合来表示。
客户跟联系人之间没有顺序,在Java的集合类型中可以选用set集合表示。

  /Hibernate5_d03_c01/src/hibernate/domain/Customer.java
程序代码如下:

package hibernate.domain;

import java.util.HashSet;
import java.util.Set;

public class Customer {
    ...
    //set集合表示多个联系人
    //设置与多方关联的属性
    private Set<LinkMan> linkMans = new HashSet<>();

    public Set<LinkMan> getLinkMans() {
        return linkMans;
    }
    public void setLinkMans(Set<LinkMan> linkMans) {
        this.linkMans = linkMans;
    }
    //省略 getter 和 setter 方法
    ...
}
一个联系人对应一个客户,在Java中可以直接用一个客户类表示。

  /Hibernate5_d03_c01/src/hibernate/domain/LinkMan.java
程序代码如下:

package hibernate.domain;

public class LinkMan {
    ...
    //客户类
    //与一方关联的属性
    private Customer customer;

    public Customer getCustomer() {
        return customer;
    }
    public void setCustomer(Customer customer) {
        this.customer = customer;
    }
    ...
}
映射配置文件

在映射配置文件中表示客户与联系人的关系

  /Hibernate5_d03_c01/src/hibernate/domain/Customer.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>
    ...
        <!-- 配置一对多的关系 -->
        <!--
            name:set集合的名称
            key:column:外键名称
            one-to-many class:set集合中类的全路径
        -->
        <set name="linkMans">
            <!-- 外键 -->
            <key column="lkm_cust_id"></key>
            <!-- 一对多关系 -->
            <one-to-many class="hibernate.domain.LinkMan"/>
        </set>
    </class>
</hibernate-mapping>

  /Hibernate5_d03_c01/src/hibernate/domain/LinkMan.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>
    ...
        <!-- 配置多对一的关系   -->
        <!-- name:客户类属性名称 -->
        <!-- class:属性的类的全路径 -->
        <!-- column:外键 -->
        <many-to-one name="customer" class="hibernate.domain.Customer" column="lkm_cust_id"></many-to-one>
    </class>
</hibernate-mapping>

六、一对多配置文件和具体的说明

一方与多方各自的关系
一方与多方各自的关系


七、一对多的级联保存——双向关联

必须双方都建立关系,而且都要执行保存操作

  /Hibernate5_d03_c01/src/hibernate/test/TestOne2Many.java
程序代码如下:

package hibernate.test;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import hibernate.domain.Customer;
import hibernate.domain.LinkMan;
import hibernate.util.HibernateUtils;

public class TestOne2Many {
    //双向关联
    @Test
    public void testSave(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction tr = session.beginTransaction();

        //新建1个客户和2个联系人
        Customer c = new Customer();
        c.setCust_name("马蓉");

        LinkMan m1 = new LinkMan();
        m1.setLkm_name("强哥");

        LinkMan m2 = new LinkMan();
        m2.setLkm_name("小宋");

        //双向关联
        //客户关联联系人
        c.getLinkMans().add(m1);
        c.getLinkMans().add(m2);
        //联系人关联客户
        m1.setCustomer(c);
        m2.setCustomer(c);

        //开始保存
        session.save(c);
        session.save(m1);
        session.save(m2);

        tr.commit();
        //不需要手动关闭session,当提交操作完成后,hibernate框架会自动关闭session
        //session.close();
    }
}

运行效果

运行效果
运行两次后数据库数据如下
运行效果
运行效果


八、一对多的级联保存——单向关联

保存客户及其关联的所有联系人

1、测试:如果现在代码只插入其中的一方的数据。
  1)如果只保存其中的一方的数据,那么程序会抛出异常。
  2)如果想完成只保存一方的数据,并且把相关联的数据都保存到数据库中,那么需要配置级联!
  3)级联保存是方向性。

  /Hibernate5_d03_c01/src/hibernate/test/TestOne2Many.java
程序代码如下:

...
public class TestOne2Many {
    ...
    //保存客户及其关联的所有联系人
    @Test
    public void testSave1(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction tr = session.beginTransaction();

        //新建1个客户和2个联系人
        Customer c = new Customer();
        c.setCust_name("马蓉1");

        LinkMan m1 = new LinkMan();
        m1.setLkm_name("强哥1");

        LinkMan m2 = new LinkMan();
        m2.setLkm_name("小宋1");

        //单向关联
        //客户关联联系人
        c.getLinkMans().add(m1);
        c.getLinkMans().add(m2);

        //单向关联
        session.save(c);

        tr.commit();
        //不需要手动关闭session,当提交操作完成后,hibernate框架会自动关闭session
        //session.close();
    }
}

运行报错
运行报错
运行报错
运行报错

  原因结论: hibernate中,在session.flush前,不允许持久态对象关联瞬时态对象持久态对象只能关联持久态对象

2、级联保存效果
  1)级联保存:保存一方同时可以把关联的对象也保存到数据库中!
  2)使用 cascade=”save-update”

  /Hibernate5_d03_c01/src/hibernate/domain/Customer.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>
    <!-- javabean与表之间的对应关系 -->
    <class name="hibernate.domain.Customer" table="cst_customer">
        ...
        <!-- 配置级联保存:让瞬时态对象变成持久态。cascade="save-update" -->
        <set name="linkMans" cascade="save-update">
            <!-- 外键 -->
            <key column="lkm_cust_id"></key>
            <!-- 一对多关系 -->
            <one-to-many class="hibernate.domain.LinkMan"/>
        </set>
    </class>
</hibernate-mapping>

再次运行
运行效果
运行效果
运行效果


保存联系人及其对应的客户

  /Hibernate5_d03_c01/src/hibernate/test/TestOne2Many.java
程序代码如下:

...
public class TestOne2Many {
    ...
    //保存联系人及其对应的客户
    @Test
    public void testSave2(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction tr = session.beginTransaction();

        //新建1个客户和2个联系人
        Customer c = new Customer();
        c.setCust_name("马蓉1");

        LinkMan m1 = new LinkMan();
        m1.setLkm_name("强哥1");

        LinkMan m2 = new LinkMan();
        m2.setLkm_name("小宋1");

        //单向关联
        //联系人关联客户
        m1.setCustomer(c);
        m2.setCustomer(c);

        //单向关联
        session.save(m1);
        session.save(m2);

        tr.commit();
        //不需要手动关闭session,当提交操作完成后,hibernate框架会自动关闭session
        //session.close();
    }
}

  /Hibernate5_d03_c01/src/hibernate/domain/LinkMan.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>
    <!-- javabean与表之间的对应关系 -->
    <class name="hibernate.domain.LinkMan" table="cst_linkman">
        ...
        <!-- 配置级联保存:把瞬时态的客户变成持久态。cascade="save-update" -->
        <many-to-one name="customer" 
                     class="hibernate.domain.Customer" 
                     column="lkm_cust_id"
                     cascade="save-update">
         </many-to-one>
    </class>
</hibernate-mapping>

运行效果
运行效果
运行效果

  由此可见,在联系人关联客户中,只执行了3条SQL语句,故较客户关联联系人,此配置性能较好。


九、一对多的级联删除

1、 先来给大家在数据库中演示含有外键的删除客户功能,那么 SQL 语句是会报出错误的。delete from cst_customer where cust_id = 1;

2、如果使用 Hibernate 框架直接删除客户的时候,测试发现是可以删除的

3、上述的删除是普通的删除,那么也可以使用级联删除,

4、级联删除也是有方向性的。<many-to-one cascade="delete" />

删除客户及其关联的所有联系人

  /Hibernate5_d03_c01/src/hibernate/test/TestOne2Many.java
程序代码如下:

...
public class TestOne2Many {
    ...
    //删除客户及其关联的所有联系人
    @Test
    public void testDelete(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction tr = session.beginTransaction();

        //获取一个客户
        //cust_id是Long类型,所以要写4L
        Customer c = session.get(Customer.class, 4L);

        //删除客户
        session.delete(c);
        tr.commit();
    }
}

  /Hibernate5_d03_c01/src/hibernate/domain/Customer.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>
    <!-- javabean与表之间的对应关系 -->
    <class name="hibernate.domain.Customer" table="cst_customer">
        ...
        <!-- 配置级联删除。 cascade="delete" -->
        <set name="linkMans" cascade="delete">
            <!-- 外键 -->
            <key column="lkm_cust_id"></key>
            <!-- 一对多关系 -->
            <one-to-many class="hibernate.domain.LinkMan"/>
        </set>
    </class>
</hibernate-mapping>

找出 id 对应的客户
找出 id 对应的客户
找出 id 对应的联系人
找出 id 对应的联系人
把找出的联系人对应的客户 id 设置为 null
把找出的联系人对应的客户 id 设置为 null
删除联系人
删除联系人
删除客户
删除客户


删除联系人及其对应的客户

删掉客户配置的级联删除信息
  /Hibernate5_d03_c01/src/hibernate/domain/Customer.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>
    <!-- javabean与表之间的对应关系 -->
    <class name="hibernate.domain.Customer" table="cst_customer">
        ...
        <set name="linkMans">
            <!-- 外键 -->
            <key column="lkm_cust_id"></key>
            <!-- 一对多关系 -->
            <one-to-many class="hibernate.domain.LinkMan"/>
        </set>
    </class>
</hibernate-mapping>

  /Hibernate5_d03_c01/src/hibernate/domain/LinkMan.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>
    <!-- javabean与表之间的对应关系 -->
    <class name="hibernate.domain.LinkMan" table="cst_linkman">
        ...
        <!-- 配置级联保存:把瞬时态的客户变成持久态。cascade="save-update" -->
        <!-- 配置级联删除:会产生无用的记录。cascade="delete" -->
        <many-to-one name="customer" 
                     class="hibernate.domain.Customer" 
                     column="lkm_cust_id"
                     cascade="delete">
         </many-to-one>
    </class>
</hibernate-mapping>

  /Hibernate5_d03_c01/src/hibernate/test/TestOne2Many.java
程序代码如下:

...
public class TestOne2Many {
    ...
    //删除联系人及其对应的客户
    @Test
    public void testDelete1(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction tr = session.beginTransaction();
        //获取一个联系人
        LinkMan linkMan = session.get(LinkMan.class, 1);
        //删除联系人
        session.delete(linkMan);
        tr.commit();
    }
}

运行效果
运行效果
运行效果
运行效果
运行效果

  由此可见,剩下一条联系人数据没有关联到客户,这条数据已无用,说明这种方式不合理。所以不要把级联删除配置到多方,这样会产生无用的记录。


十、”孤儿删除”和”cascade取值”

孤儿删除(孤子删除)

只有在一对多的环境下才有孤儿删除

  在一对多的关系中,可以将一的一方认为是父方.将多的一方认为是子方.孤儿删除:在解除了父子关系的时候.将子方记录就直接删除。
  <set name="linkmans" cascade="delete-orphan">

孤儿删除

删掉联系人配置的级联删除信息
  /Hibernate5_d03_c01/src/hibernate/domain/LinkMan.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>
    <!-- javabean与表之间的对应关系 -->
    <class name="hibernate.domain.LinkMan" table="cst_linkman">
        ...
        <many-to-one name="customer" 
                     class="hibernate.domain.Customer" 
                     column="lkm_cust_id">
         </many-to-one>
    </class>
</hibernate-mapping>

  /Hibernate5_d03_c01/src/hibernate/domain/Customer.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>
    <!-- javabean与表之间的对应关系 -->
    <class name="hibernate.domain.Customer" table="cst_customer">
        ...
        <!-- 配置孤儿删除。cascade="delete-orphan" -->
        <set name="linkMans" cascade="delete-orphan">
            <!-- 外键 -->
            <key column="lkm_cust_id"></key>
            <!-- 一对多关系 -->
            <one-to-many class="hibernate.domain.LinkMan"/>
        </set>
    </class>
</hibernate-mapping>

  /Hibernate5_d03_c01/src/hibernate/test/TestOne2Many.java
程序代码如下:

...
public class TestOne2Many {
    ...
    //孤儿删除
    @Test
    public void testDelete2(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction tr = session.beginTransaction();

        //获取id为10的客户
        //id为10客户下有2个联系人:id为910 
        Customer c = session.get(Customer.class, 10L);
        //要删除id为10的联系人
        LinkMan m2 = session.get(LinkMan.class, 10);

        //联系人与客户解除关系
        c.getLinkMans().remove(m2);
        //由快照功能实现
        tr.commit();
    }
}

运行效果
运行效果
运行效果
运行效果
运行效果
运行效果

  总结:一般在业务开发中,不要两端都配置级联。(多方尽量不要配置级联,尽量在一方配置级联)。


cascade的取值
Hibernate级联开发配置, cascade的部分取值
none 不使用级联
save-update 级联保存或更新(对关联瞬时对象执行 save 操作,对关联托管对象执行 update)
delete 级联删除(对关联对象进行删除)
delete-orphan 孤儿删除.(注意:只能应用在一对多关系)
all 除了 delete-orphan 的所有情况.(包含save-update、delete)
all-delete-orphan 包含了 delete-orphan 的所有情况.(包含 save-update、delete、delete-orphan)

十一、放弃外键维护

维护外键
将没有关系的一个联系人和客户建立关系。(双方)

先测试双方都维护外键的时候,会产生多余的 SQL 语句。
  1、想修改客户和联系人的关系,进行双向关联,双方都会维护外键,会产生多余的 SQL 语句。
  2、产生的原因:session 的一级缓存中的快照机制,会让双方都更新数据库,产生了多余的 SQL 语句。

删掉客户配置的级联删除信息
  /Hibernate5_d03_c01/src/hibernate/domain/Customer.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>
    <!-- javabean与表之间的对应关系 -->
    <class name="hibernate.domain.Customer" table="cst_customer">
        ...
        <set name="linkMans">
            <!-- 外键 -->
            <key column="lkm_cust_id"></key>
            <!-- 一对多关系 -->
            <one-to-many class="hibernate.domain.LinkMan"/>
        </set>
    </class>
</hibernate-mapping>

手动在数据表添加信息

添加信息
添加信息

  /Hibernate5_d03_c01/src/hibernate/test/TestOne2Many.java
程序代码如下:

public class TestOne2Many {
    ...
    //inverse 放弃外键维护
    @Test
    public void testsave3(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction tr = session.beginTransaction();
        //获取一个客户
        Customer c = session.get(Customer.class, 13L);
        //获取一个联系人
        LinkMan m = session.get(LinkMan.class, 15);
        //双向关联
        //联系人与客户做双向关联
        c.getLinkMans().add(m);
        m.setCustomer(c);

        session.save(c);
        session.save(m);

        tr.commit();
    }
}

运行效果
运行效果
运行效果
运行效果
运行效果

  由此可知,在控制台打印了两条SQL语句,而这两条SQL语句的功能都是一样的,所以有一条是多余的,我们需要改进。

放弃外键维护
将没有关系的一个联系人和客户建立关系。(双方)

如果不想产生多余的 SQL 语句,那么需要一方来放弃外键的维护。
  在标签上配置一个<inverse="true">
  true:放弃.;alse:不放弃。默认值是 false。

手动在数据表修改信息

修改信息

  /Hibernate5_d03_c01/src/hibernate/domain/Customer.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>
    <!-- javabean与表之间的对应关系 -->
    <class name="hibernate.domain.Customer" table="cst_customer">
        ...
        <!-- 配置放弃外键维护。inverse="true" -->
        <set name="linkMans" inverse="true">
            <!-- 外键 -->
            <key column="lkm_cust_id"></key>
            <!-- 一对多关系 -->
            <one-to-many class="hibernate.domain.LinkMan"/>
        </set>
    </class>
</hibernate-mapping>

运行效果
运行效果
运行效果
运行效果
运行效果

  由此可知,在控制台只打印了一条SQL语句

十二、cascade和inverse的区别

1、cascade用来级联操作(保存、修改和删除) 在一方设置。
2、inverse用来维护外键的,在一方设置。一般,我们都让一方放弃外键维护。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的影城管理系统,源码+数据库+论文答辩+毕业论文+视频演示 随着现在网络的快速发展,网上管理系统也逐渐快速发展起来,网上管理模式很快融入到了许多生活之中,随之就产生了“小徐影城管理系统”,这样就让小徐影城管理系统更加方便简单。 对于本小徐影城管理系统的设计来说,系统开发主要是采用java语言技术,在整个系统的设计中应用MySQL数据库来完成数据存储,具体根据小徐影城管理系统的现状来进行开发的,具体根据现实的需求来实现小徐影城管理系统网络化的管理,各类信息有序地进行存储,进入小徐影城管理系统页面之后,方可开始操作主控界面,主要功能包括管理员:首页、个人中心、用户管理、电影类型管理、放映厅管理、电影信息管理、购票统计管理、系统管理、订单管理,用户前台;首页、电影信息、电影资讯、个人中心、后台管理、在线客服等功能。 本论文主要讲述了小徐影城管理系统开发背景,该系统它主要是对需求分析和功能需求做了介绍,并且对系统做了详细的测试和总结。具体从业务流程、数据库设计和系统结构等多方面的问题。望能利用先进的计算机技术和网络技术来改变目前的小徐影城管理系统状况,提高管理效率。 关键词:小徐影城管理系统;Spring Boot框架,MySQL数据库
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值