zoukankan      html  css  js  c++  java
  • Hibernate 表关系描述之OneToMany

     上篇写了一对一,那么在这篇我们讲一对多,那么在表中的一对多,是使用外键关联,通过一张表的一个键另一个表的外键来建立一多关系;
     而在类中表示为一个类中有一个集合属性包含对方类的很多对象,而在另一个类中,只包含前术类的一个对象,从而实现一对多关系的建立!
     而在Hibernate中采用的是Set类型集合,而使用<one-to-many>主<many-to-one>来实现,好了,我们来看一下:
    首先看看表结构!

    customer表:

    create table customers
    (
        ID 
    bigint not null    primary key auto_increment,
        userName 
    varchar(20)
    );
    Order表:
    create table orders
    (
        ID 
    bigint not null primary key auto_increment,
        customers_ID 
    bigint,    --应该为customers(ID)外键
        orderDescription varchar(50)--订单描述
    );

    有了上面简单的客户表与订单表,来建立一个Project!~并且添加Hibernate~并由上面二张表生成Beans,主键都是native类型~自动递增!
    我们将 自动生成的Customer.java    Bean添加一个SET属性,代码如下:

    Customers.java


    为Order.java   Bean 去掉cid(CustomerID)并添加一个Customer属性,代码如下:

    Orders.java


    修改Customer.hbm.xml映射文件:

    <?xml version="1.0" encoding="GBK"?>
    <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
    >

    <hibernate-mapping>
        
    <class name="fengyan.Hibernate.Customers" table="customers" catalog="study">
            
    <id name="id" type="java.lang.Long">
                
    <column name="ID" />
                
    <generator class="native" />
            
    </id>
            
    <property name="userName" type="java.lang.String">
                
    <column name="userName" length="20" />
            
    </property>
            
            
    <!-- name集合属性orders    
                 table对应的表名    
                 cascade 级联关系,当保存或更新时会级联保存与这个Customers对象相关联的所有Orders对象
                 inverse=true是将 控权抛出(给Orders) 
    -->
            
    <set name="orders" table="orders" cascade="save-update" inverse="true">
                
    <key column="customers_ID"></key><!-- 表字段 -->        
                
    <one-to-many class="fengyan.Hibernate.Orders"/><!-- 关联的类 -->
            
    </set>
        
    </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"
    >

    <hibernate-mapping>
        
    <class name="fengyan.Hibernate.Orders" table="orders" catalog="study">
            
    <id name="id" type="java.lang.Long">
                
    <column name="ID" />
                
    <generator class="native" />
            
    </id>
           
            
    <property name="orderDescription" type="java.lang.String">
                
    <column name="orderDescription" length="50" />
            
    </property>
            
            
    <!-- name属性名
                 class对应的类
                 column对应的表字段 
    -->
            
    <many-to-one name="customer" class="fengyan.Hibernate.Customers" column="customers_ID"></many-to-one>
        
    </class>
    </hibernate-mapping>

    然后我们建立一个CustomersDAO操作类

    package fengyan.Hibernate;

    import org.hibernate.Session;
    import org.hibernate.Transaction;

    public class CustomersDAO {
        
        
    public void save(Customers customer)
        
    {
            Session session 
    = HibernateSessionFactory.getSession();//会话
            try
            
    {
                Transaction tx 
    = session.beginTransaction();//事物
                session.save(customer);//保存
                tx.commit();//提交事物
            }

            
    catch(Exception e)
            
    {
                System.out.println(
    "DAO has Errors:"+e);
            }

            
    finally
            
    {
                session.close();
    //关闭会话
            }

        }


    }

    再来建立一个Servlet     MyServlet.java代码如下:

    package fengyan.Hibernate;

    import java.io.IOException;
    import java.io.PrintWriter;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    public class MyServlet extends HttpServlet {

        
    public void doGet(HttpServletRequest request, HttpServletResponse response)
                
    throws ServletException, IOException {

            
    //声明一个用户
            Customers customer = new Customers("fengyan");
            
            
    //声明二订单
            Orders order1 = new Orders("描述1");
            Orders order2 
    = new Orders("描述2");
            
            
    //订单关联用户
            order1.setCustomer(customer);
            order2.setCustomer(customer);
            
            
    //用户关联订单
            customer.getOrders().add(order1);
            customer.getOrders().add(order2);
            
            
    //save
            CustomersDAO cd = new CustomersDAO();
            cd.save(customer);
    //我们仅仅保存了Customers!
            
        }

    }

    最后一个JSP测试页面:

    <%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
    <html>
      
    <head>      
        
    <title>My JSP 'index.jsp' starting page</title>  
      
    </head>  
      
    <body>
       
    <br>
       
    <href="servlet/MyServlet">add</a>
      
    </body>
    </html>

    运行效果如下:

    我们可以看到,在doGet()方法中,我产仅仅save(customer),并没有save(order)但却执行了三条SQL,由显示的SQL语句我们可以知道,将用户fengyan添加到Customers表中的同时也将 order1及order2添加到Orders表中!
    为什么Hibernate会自动帮我们将订单也添加到表中呢,就是我们在Customers.hbm.xml映射配置中,set节点的cascade=save-update,就是当我们保存或更新Customers的时候也会自动保存相应的Orders对象!

    当我们现在将 doGet()方法内  订单关联用户的代码注释掉,看一下效果:

    从显示的SQL语句看,我们似乎看不出什么区别,但这时如果打开MYSQL,会发现:

    我们发现现在Hiberante帮自动插入的Customers_ID字段为空,为什么会这样的呢,我们不是在下面用

    //用户关联订单
      customer.getOrders().add(order1);
      customer.getOrders().add(order2);


    不是已经由用户关联了订单吗?这是因为我们在Customers.hbm.xml的set节点中加了属性inverse=true,这句话的意思是将主控制权交出去,具体是什么意思呢?就是在一对多关系中将 主动权交出,具体也就是交给了Orders,也就是用户与订单之间从属关系主要是由Orders对象来确定,也即订单自己来决定它属于哪个对象,所以在这里,我们将    订单关联用户的代码注释掉后,虽然后面 用户关联了订单,但因为用户已经将主动权交出,所以Hibernate在帮我们save订单的时候并不知道订单是属于哪个用户,也自然就在Customers_ID字段填空值了!
    那么如果此时我们将Customers.hbm.xml中的 inverse=true去掉是不是就行了呢?带着这样的猜想,我来尝试一下,结果如下图:


    我们首先查寻数据库发现Hibernate已经正确的将Orders对象持久化到表中了,

    同时我们看控制台监视的SQL语句,这时我们发现有五条语句,前三条分别是添加一个用户和二条订单,四五条是修改订单的,具体修改什么,我们发现是修改了customers_ID字段,原来这种情况,Hibernate是先将 订单持久化到表中,因为我们注释了订单关联用户的代码,所以Hibernate还是先插入空值,然后再根据我们的 用户关联订单 再来更新Orders表将 Customers_ID字段修改为正确的值!
    这个时候我们发现,虽然这样可以,但还是会有一些问题,因为当数据量很大的时候,这样的操作是很占资源的,会影响性能,同时如果我们的数据库customers_ID字段定义为not null,非空,那么可想而知这种方式是不可行的!
    那么可不可以将inverse=true加上,Customers将主动权交出后,我们仅仅用订单关联用户,而用户关联订单的注释掉呢?我们式式!
    运行结果我们发现仅仅是将 用户添加到表中去了,而订单却丢失,这为什么,不是用户将主动权交出,而我们也用订单关联了用户,为何没有加入订单呢,首先要知道,关联是仅仅减缓到订单属于哪个用户,也就是关联订单的customers_ID字段!但用户类里,属性 Set orders = new HashSet();初始是为空的,这样虽然订单关联了用户,但用户对象内的orders属性还是为空,订单还没产生呢,这样Hibernate自然在保存用户的时候,判断集合为空,自然不会去添加订单!这个有点像什么呢,就好比腾讯的QQ宠物里面的所谓“超值大礼包”,每一种礼包我们可以认为是一个订单,而这样的“礼包”一产生也的属于对象,当然就是属于所有拥有QQ宠物的QQ号了!这可以认为上 订单关联了对象,但至于在操作对象的时候,订单有没有,这个其实还是由QQ号码决定的,看它要不要,它如果要了,就有订单,不要呢,当然就没订单了,虽然腾讯希望订单是属于他的!我个人觉得这个比如很恰当!

    所以得出总结是:无论我们的Customer类是否将主动权交出去,我们都要将用户关系到订单!那可能会想:既然这样交不交主动权有什么区别呢?有!而且很大!就像我们一开始运行的效果。

    如果我们将 用户主动权交出,通过监视的SQL我们可以看到只执行了三条SQL语句,分别说是插入用户和二条订单!没有更新!
    而如果我们没有将Customers主动权交出的话,运行效果如下:
     
    有五条语句,性能不一样,同时可以解决comstomers_ID字段不为空的问题!所以一般我们还是会选择交出主动权!

    另外补充一下cascade的属性值:

    <!--
    none:在保存,更新或删除当前对象时,忽略其他关联的对象。它是cascade属性的默认值
    save-update:当通过Session的save()以及saveOrUpdate()方法来保存或更新当前对象时,级联保存所有关联的新建的临时对象,并且级联更新所有关联的游离对象。
    delete:当通过Session的delete()方法删除当前对象时,级联删除所有关联的对象。
    all: 包含save-update以及delete的行为。此外,对当前对象执行evict()或lock()操作时,也会对所有关联的持久化对象执行evict()或lock()操作。
    delete-orphan:删除所有和当前对象解除关联关系的对象。
    all-delete-orphan:包含all和delete-orphan的行为
    -->


     

  • 相关阅读:
    飞思卡尔IMX6处理器的GPIO配置方式
    批处理清除VisualStudio解决方案文件夹
    总结过去10年的程序员生涯,给程序员小弟弟小妹妹们的一些总结性忠告
    详解Linux2.6内核中基于platform机制的驱动模型 (经典)
    [驱动注册]platform_driver_register()与platform_device_register()
    机器人系统常用仿真软件介绍效果与评价指标
    WINCE的批处理
    项目开发中的人月及如何计算
    常用的六个富文本编辑器
    如何获取公众号里面的歌曲
  • 原文地址:https://www.cnblogs.com/eflylab/p/606607.html
Copyright © 2011-2022 走看看