Hibernate 映射一对多关联关系

单向关联

仅仅建立从Order到Customer的多对一关联,即仅仅在Order类中定义customer属性。或者仅仅建立从Customer到Order的一对多关联,即仅仅Customer类中定义orders集合。
这里写图片描述
单向 n-1 关联只需从 n 的一端可以访问 1 的一端
域模型: 从 Order 到 Customer 的多对一单向关联需要在Order 类中定义一个 Customer 属性, 而在 Customer 类中无需定义存放 Order 对象的集合属性

这里写图片描述

关系数据模型:ORDERS 表中的 CUSTOMER_ID 参照 CUSTOMER 表的主键
这里写图片描述

Hibernate 使用 <many-to-one> 元素来映射多对一关联关系
这里写图片描述

创建持久化类

package cn.itcast.hibernate0909.onetomany.test;

public class Customer {

    private Integer cid;
    private String name;

    public Integer getCid() {
        return cid;
    }
    public void setCid(Integer cid) {
        this.cid = cid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }



}
package cn.itcast.hibernate0909.onetomany.test;

public class Order {

    private Integer oid;
    private String orderNumber;
    private Double price;
    private Customer customer;


    public Integer getOid() {
        return oid;
    }
    public void setOid(Integer oid) {
        this.oid = oid;
    }
    public String getOrderNumber() {
        return orderNumber;
    }
    public void setOrderNumber(String orderNumber) {
        this.orderNumber = orderNumber;
    }
    public Double getPrice() {
        return price;
    }
    public void setPrice(Double price) {
        this.price = price;
    }
    public Customer getCustomer() {
        return customer;
    }
    public void setCustomer(Customer customer) {
        this.customer = customer;
    }


}

配置映射文件

Customer.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 
    Mapping file autogenerated by MyEclipse Persistence Tools
-->

<hibernate-mapping>  
<class name="cn.itcast.hibernate0909.onetomany.test.Customer" table="customers">  
    <id name="cid" type="integer">  
        <column name="cid"></column>  
        <generator class="increment"></generator>  
    </id>  
    <property name="name" type="string" >  
    </property>  
</class>  
</hibernate-mapping>  

Order.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 
    Mapping file autogenerated by MyEclipse Persistence Tools
-->

<hibernate-mapping>  
    <class name="cn.itcast.hibernate0909.onetomany.test.Order" table="orders">  
        <id name="oid" type="integer">  
            <column name="id"></column>  
            <generator class="increment"></generator>  
        </id>  

        <property name="orderNumber" type="string" >  </property>  
        <property name="price" type="double"> </property>  

        <many-to-one name="customer" class="cn.itcast.hibernate0909.onetomany.test.Customer" >  
            <column name="customer_id" ></column>     
        </many-to-one>  
    </class>  
</hibernate-mapping>  

配置hibernate.cfg.xml文件

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <!-- 是用来描述数据库的连接 -->
<session-factory>
    <!-- 
        驱动
    -->
    <property name="connection.driver_class">
        com.mysql.jdbc.Driver
    </property>
    <!-- 
        url
    -->
    <property name="connection.url">
        jdbc:mysql://localhost:3306/hibernate0909
    </property>
    <!-- 
        username
    -->
    <property name="connection.username">root</property>
    <!-- 
        password
    -->
    <property name="connection.password">123</property>
    <!-- 
        hibernate针对建表的操作
        update  如果有表,检查表的结构,如果没有则创建
        create-drop 启动hibernate创建表,结束hibernate删除表
        create  每次启动都重新创建表
        validate 每次启动都检查表的结构
    -->
    <property name="hbm2ddl.auto">update</property>
    <property name="show_sql">true</property>
    <mapping
        resource="cn/itcast/hibernate0909/onetomany/test/Order.hbm.xml" />
    <mapping
        resource="cn/itcast/hibernate0909/onetomany/test/Customer.hbm.xml" />
</session-factory>
</hibernate-configuration>

这里写图片描述

保存操作

1.先保存订单,再保存客户

这里写图片描述

junit测试结果:
这里写图片描述
这里写图片描述
这里写图片描述
从这里可以看出执行了两条insert语句,一条update语句

2.先保存客户,再保存订单

这里写图片描述
junit测试结果:
这里写图片描述
这里写图片描述
这里写图片描述
可以看出这种情况程序是执行了两条insert语句,而没有执行update语句。

保存操作的总结

保存订单时会发出两条insert语句。

而保存客户时会发出两条insert语句和一条update语句

因为订单是多的一方,而客户是一的一方,所以在多对一的关系中,应该使用一的一方进行操作,这样效率更高。


先保存客户,再保存订单,在下面的代码中注释掉session.save(c),会有什么后果
这里写图片描述
junit测试结果:
这里写图片描述

查询订单

这里写图片描述

junit测试结果:
这里写图片描述

解释说明
当hibernate持久化一个临时对象时,在默认情况下,他不会自动持久化所关联的其他临时对象,会抛出TransientObjectException.如果设定many-to-one元素的cascade属性为save-update的话,可实现自动持久化所关联的对象。

修改映射文件,重新测试:
这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

单向关联全部源码

package cn.itcast.hibernate0909.onetomany.test;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;


public class OneToManyTest {

    private static SessionFactory sessionFactory;

    static{
        Configuration  configuration = new Configuration();
        configuration.configure("cn/itcast/hibernate0909/onetomany/test/hibernate.cfg.xml");
        sessionFactory = configuration.buildSessionFactory();
    }


    /**
     * 实验1:先保存订单,再保存客户
     */
    @Test
    public void insert(){
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        Customer c = new Customer();
        c.setName("菜肉");
        Order orders = new Order();
        orders.setOrderNumber("11a");

        //将订单和客户关联起来
        orders.setCustomer(c);

        //保存订单
        session.save(orders);
        //保存客户
        session.save(c);

        tx.commit();
        session.close();
    }


    /**
     * 实验2:先保存客户,再保存订单
     */
    @Test
    public void insert2(){
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        Customer c = new Customer();
        c.setName("菜肉2");
        Order orders = new Order();
        orders.setOrderNumber("22a");

        //将订单和客户关联起来
        orders.setCustomer(c);

        //保存客户
        session.save(c);
        //保存订单
        session.save(orders);


        tx.commit();
        session.close();
    }





    //查询订单
    @Test
    public void loadOrder(){

        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        //通过id查询订单
        Order orders = (Order) session.load(Order.class, 1);
        System.out.println(orders.getOid() + " " +orders.getOrderNumber());

        //打印订单对应的客户
        System.out.println(orders.getCustomer().getCid() + " " + orders.getCustomer().getName());
        tx.commit();
        session.close();

    }


    /**
     * 实验3:先保存客户,再保存订单,注释掉session.save(c)
     */
    @Test
    public void insert3(){
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        Customer c = new Customer();
        c.setName("菜肉2");
        Order orders = new Order();
        orders.setOrderNumber("22a");

        //将订单和客户关联起来
        orders.setCustomer(c);

        //保存客户
    //  session.save(c);
        //保存订单
        session.save(orders);


        tx.commit();
        session.close();
    }


}

双向关联

双向 1-n 与 双向 n-1 是完全相同的两种情形
双向 1-n 需要在 1 的一端可以访问 n 的一端, 反之依然.

域模型

从 Order 到 Customer 的多对一单向关联需要在Order 类中定义一个 Customer 属性, 而在 Customer 类中需定义存放 Order 对象的集合属性
这里写图片描述

关系数据模型

ORDERS 表中的 CUSTOMER_ID 参照 CUSTOMER 表的主键
这里写图片描述

建立一对多的双向关联关系

配置新的映射文件和hibernate.cfg.xml文件
这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

保存客户和订单(客户和订单建立双向关联)

这里写图片描述

这里写图片描述
这里写图片描述

这里写图片描述

保存客户和不保存订单

在下面的代码中注释掉session.save(order1),会有什么后果
这里写图片描述
junit测试后发现报错:
这里写图片描述

级联保存和更新
当hibernate持久化一个临时对象时,在默认情况下,他不会自动持久化所关联的其他临时对象,会抛出TransientObjectException.如果设定set元素的cascade属性为save-update的话,可实现自动持久化所关联的对象。

修改映射文件,重新测试:
这里写图片描述

这里写图片描述

同理,注释掉session.save(c),同样会报TransientObjectException错误.需要对Order.hbm.xml文件中添加cascade属性为save-update

这里写图片描述

查询客户和订单

这里写图片描述
这里写图片描述

查询客户和订单(图)

这里写图片描述
这里写图片描述

订单变更客户

这里写图片描述
这里写图片描述

这里写图片描述

这里写图片描述

上述例子产生了两条update语句:
原因为:
Hibernate会自动清理缓存中的所有持久化对象,按照持久化对象的改变来同步更新数据库,因此执行了上述的两条更新语句所以会产生两条update语句
c.getOrders().add(o);执行了一条update语句
o.setCustomer(c);执行了一条update语句

级联删除

inverse属性

  • Inverse来源

在hibernate中通过对 inverse 属性的值决定是由双向关联的哪一方来维护表和表之间的关系. inverse=false 的为主动方,inverse=true 的为被动方, 由主动方负责维护关联关系

  • Inverse设值

在没有设置 inverse=true 的情况下,父子两边都维护父子关系

  • Inverse设值原则

在 1-n 关系中,将 n 方设为主控方将有助于性能改善(如果要国家元首记住全国人民的名字,不是太可能,但要让全国人民知道国家元首,就容易的多)
在 1-N 关系中,若将 1 方设为主控方 会额外多出 update 语句

这里写图片描述
在一的一方设值inverse为TRUE表明一的一方不维护其关系,这样就会发出一条update语句,这样效率也就提高了。

  • Inverse结论

1.在映射一对多的双向关联关系时,应该在one方把inverse属性设为true,
这可以提高性能。
2.在建立两个对象的关联时,应该同时修改关联两端的相应属性:
customer.getOrders().add(order);
order.setCustomer(customer);
这样才会使程序更加健壮,提高业务逻辑层的独立性,使业务逻辑层的程序代码
不受Hibernate实现类的影响。同理,当删除双向关联的关系时,也应该修改
关联两端的对象的相应属性:
Customer.getOrders().remove(order);
Order.setCustomer(null);

解除关联关系

解除某个订单与某个客户的关系

这里写图片描述

junit测试结果:
这里写图片描述

这里写图片描述

级联删除

这里写图片描述
junit测试结果:
这里写图片描述

错误的根本原因:就是主外键的约束
外键(jdbc)—>inverse(hibernate):inverse的属性值为true,没有关联约束,不维护关系
这里写图片描述
修改映射文件
这里写图片描述

测试结果:
这里写图片描述

这里写图片描述

这里写图片描述

测试结果通过

在数据库中对集合进行排序

<set> 元素有一个 order-by 属性, 如果设置了该属性, 当 Hibernate 通过 select 语句到数据库中检索集合对象时, 利用 order by 子句进行排序
这里写图片描述

双向关联全部源码

package cn.itcast.hibernate0909.onetomany.test2;

import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;


public class OneToManyTest {

    private static SessionFactory sessionFactory;

    static{
        Configuration  configuration = new Configuration();
        configuration.configure("cn/itcast/hibernate0909/onetomany/test2/hibernate.cfg.xml");
        sessionFactory = configuration.buildSessionFactory();
    }


    /**
     * 实验1:保存客户和订单(客户和订单建立双向关联)
     */
    @Test
    public void insert(){
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        Order orders = new Order();
        orders.setOrderNumber("11a");


        Customer c = new Customer();
        c.setName("菜肉");

        //客户和订单关联的双向关联
        c.getOrders().add(orders);
        orders.setCustomer(c);


        //保存客户
        session.save(c);

        //保存订单
        session.save(orders);

        tx.commit();
        session.close();
    }


    /**
     * 实验2:保存客户和不保存订单
     * 在下面的代码中注释掉session.save(order1),会有什么后果
     */
    @Test
    public void insert2(){
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        Order orders = new Order();
        orders.setOrderNumber("22a");

        Customer c = new Customer();
        c.setName("菜肉2");


        //客户和订单关联的双向关联
        c.getOrders().add(orders);
        orders.setCustomer(c);

        //保存客户
        session.save(c);
        //保存订单
        //session.save(orders);


        tx.commit();
        session.close();
    }





    //查询客户和订单
    @Test
    public void loadOrder(){

        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        //通过客户的id查询订单
        Customer c =  (Customer) session.load(Customer.class, 1);
        System.out.println(c.getName());

        //关联到该客户对应的订单
        Set<Order> set = c.getOrders();
        for (Order order : set) {
            System.out.println(order.getOid() +"  "+order.getOrderNumber());
        }
        tx.commit();
        session.close();

    }


    /**
     * 实验3:解除某个订单与某个客户的关系
     */
    @Test
    public void deleteRelastion(){
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        //查询客户
        Customer c =  (Customer) session.load(Customer.class, 1);
        //查询订单
        Order o = (Order) session.load(Order.class, 3);
        //设置订单关联的客户为null
        o.setCustomer(null);

        //从客户集合删除订单
        c.getOrders().remove(o);

        tx.commit();
        session.close();
    }





    /**
     * 实验3:解除某个订单与某个客户的关系
     */
    @Test
    public void deleteCascade(){
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        //查询客户
        Customer c =  (Customer) session.load(Customer.class, 1);

        session.delete(c);
        tx.commit();
        session.close();
    }







}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值