美文网首页
Hibernate_5 在hibernate 领域模型中单向多对

Hibernate_5 在hibernate 领域模型中单向多对

作者: mm_cuckoo | 来源:发表于2017-12-14 00:20 被阅读8次

    单向多对一(n - 1)

    单向 n-1 关联只需从 n 的一端可以访问 1 的一端

    下面介绍 n - 1 时,以 Order (订单)和用户(Customer) 为例: 一个用户能发出多个订单, 而一个订单只能属于一个客户. 从 Order 到 Customer 的关联是多对一关联; 而从 Customer 到 Order 是一对多关联。

    步骤

    1. 在Order 中添加 Customer 的关联关系

    2. 在Order 的映射文件中配置映射多对一的关联关系。
      代码如下:下面是两种书写方式,效果是相同的

      <!-- 
          映射多对一的关联关系。 使用 many-to-one 来映射多对一的关联关系 
          name: 多这一端关联的一那一端的属性的名字
          class: 一那一端的属性对应的类名
          column: 一那一端在多的一端对应的数据表中的外键的名字
      -->
      <!--    <many-to-one name="customer" class="com.cfox.hibernate.n21.Customer" column="CUSTOMER_ID"/> -->  
      <many-to-one name="customer" class="com.cfox.hibernate.n21.Customer">
          <column name="CUSTOMER_ID"/>
      </many-to-one>
      

    <many-to-one> 属性说明

    属性 描述
    name 属性名。
    column (可选) 外间字段名。它也可以通过嵌套的 <column> 元素指定。
    class (可选 ) 默认是通过反射得到属性类型): 关联的类的名字。
    cascade(级联) (可选) 指明哪些操作会从父对象级联到关联的对象。
    fetch (可选 - 默认为 select ) 在外连接抓取(outer-join fetching)和序列选择抓取(sequential select fetching)两者中选择其一。
    update, insert (可选 - defaults to true ) 指定对应的字段是否包含在用于UPDATE 和/或 INSERT 的SQL语句中。如果二者都是false ,则这是一个纯粹的 “外源性(derived)”关联,它的值是通过映射到同一个(或多个)字段的某些其他属性得到 或者通过trigger(触发器)、或其他程序。
    property-ref (可选) 指定关联类的一个属性,这个属性将会和本外键相对应。 如果没有指定,会使用对方关联类的主键。
    access (可选 - 默认是 property ) Hibernate用来访问属性的策略。
    unique (可选) 使用DDL为外键字段生成一个唯一约束。此外, 这也可以用作property-ref 的目标属性。这使关联同时具有 一对一的效果。
    not-null (可选) 使用DDL为外键字段生成一个非空约束。
    optimistic-lock (可选 - 默认为 true ) 指定这个属性在做更新时是否需要获得乐观锁定(optimistic lock)。 换句话说,它决定这个属性发生脏数据时版本(version)的值是否增长。
    lazy (可选 - 默认为 proxy ) 默认情况下,单点关联是经过代理的。lazy="true" 指定此属性应该在实例变量第一次被访问时应该延迟抓取(fetche lazily)(需要运行时字节码的增强)。lazy="false" 指定此关联总是被预先抓取。
    not-found (可选 - 默认为 exception ) 指定外键引用的数据不存在时如何处理: ignore 会将数据不存在作为关联到一个空对象(null)处理。
    entity-name (optional) 被关联的类的实体名。

    完整示例代码

    public class Order {
        
        private Integer orderId;
        private String orderName;
        private Customer customer;
        
        //对应变量的get 和set 方法省略
    }
    
    
    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
    <!-- Generated 2017-10-19 10:20:11 by Hibernate Tools 3.4.0.CR1 -->
    <hibernate-mapping>
        <class name="com.cfox.hibernate.n21.Order" table="ORDERS">
            <id name="orderId" type="java.lang.Integer">
                <column name="ORDERID" />
                <generator class="native" />
            </id>
            <property name="orderName" type="java.lang.String">
                <column name="ORDER_NAME" />
            </property>
            
            <!-- 
                映射多对一的关联关系。 使用 many-to-one 来映射多对一的关联关系 
                name: 多这一端关联的一那一端的属性的名字
                class: 一那一端的属性对应的类名
                column: 一那一端在多的一端对应的数据表中的外键的名字
            -->
    <!--    <many-to-one name="customer" class="com.cfox.hibernate.n21.Customer" column="CUSTOMER_ID"/> -->  
            <many-to-one name="customer" class="com.cfox.hibernate.n21.Customer">
                <column name="CUSTOMER_ID"/>
            </many-to-one>
      </class>
    </hibernate-mapping>
    
    public class Customer {
        private Integer customerId;
        private String name;
        
        //对应变量的get 和set 方法省略
    }
    
    
    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
    <!-- Generated 2017-10-19 10:20:11 by Hibernate Tools 3.4.0.CR1 -->
    <hibernate-mapping>
        <class name="com.cfox.hibernate.n21.Customer" table="CUSTOMERS">
            <id name="customerId" type="java.lang.Integer">
                <column name="CUSTOMERID" />
                <generator class="native" />
            </id>
            <property name="name" type="java.lang.String">
                <column name="NAME" />
            </property>
            
        </class>
    </hibernate-mapping>
    
    

    双向一对多(1 - n)

    双向一对多喝双向多对一是完全相同的,从多的一端可以访问多的一端,从一的一端也可以访问多的一端。

    下面还是使用 Order 和Customer 来介绍双向一对多

    先看示例代码:

    实体bean

    public class Customer {
        private Integer customerId;
        private String customerName;
        private Set<Order> orders = new HashSet<Order>();
        public Customer() {
        }
        public Customer(String customerName) {
            this.customerName = customerName;
        }
        // 省略set 和 get 方法
    }
    
    
    public class Order {
        private Integer orderId;
        private String orderName;
        private Customer customer;
        public Order() {
        }
        public Order(String orderName) {
            this.orderName = orderName;
        }
     // 省略set 和 get 方法
    }
    
    

    映射文件

    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
    <hibernate-mapping>
        <class name="com.cfox.hibernate.Order" table="ORDERS">
            <id name="orderId" type="java.lang.Integer">
                <column name="ORDER_ID" />
                <generator class="native" />
            </id>
            <property name="orderName" type="java.lang.String">
                <column name="ORDER_NAME" />
            </property>
            <many-to-one name="customer" class="com.cfox.hibernate.Customer">
                <column name="CUSTOMER_ID"/>
            </many-to-one>
        </class>
    </hibernate-mapping>
    
    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
    <hibernate-mapping >
        <class name="com.cfox.hibernate.Customer" table="CUSTOMER">
            <id name="customerId" type="java.lang.Integer">
                <column name="CUSTOMER_ID" />
                <generator class="native" />
            </id>
            <property name="customerName" type="java.lang.String">
                <column name="CUSTOMER_NAME" />
            </property>
            <set name="orders" table="ORDERS" inverse="true" order-by="ORDER_NAME DESC">
                <!-- 设定与所关联的持久化类对应的表的外键
                        column: 指定关联表的外键名
                 -->
                <key column="CUSTOMER_ID"></key>
                <!-- 设定集合属性中所关联的持久化类
                        class: 指定关联的持久化类的类名
                 -->
                <one-to-many class="com.cfox.hibernate.Order"/>
            </set>
        </class>
    </hibernate-mapping>
    

    介绍一下上面的几个元素:

    • <set>: 映射持久化类的,也就是一的一端存放多的一端的映射
      • name :设定待映射的持久化类的属性的
    • <key>:设定与所关联的持久化类对应的表的外键
      • column: 指定关联表的外键名
    映射关系图
    image
    下面介绍<set> 元素中的 inverse 属性
    • 在hibernate中通过对 inverse 属性的来决定是由双向关联的哪一方来维护表和表之间的关系. inverse = false 的为主动方,inverse = true 的为被动方, 由主动方负责维护关联关系
    • 在没有设置 inverse=true 的情况下,父子两边都维护父子
      关系
    • 在 1-n 关系中,将 n 方设为主控方将有助于性能改善(如果要国家元首记住全国人民的名字,不是太可能,但要让全国人民知道国家元首,就容易的多)
    • 在 1-N 关系中,若将 1 方设为主控方
      • 会额外多出 update 语句。
      • 插入数据时无法同时插入外键列,因而无法为外键列添加非空约束
    下面介绍<set> 元素中的 order-by 属性
    • 如果设置了该属性, 当 Hibernate 通过 select 语句到数据库中检索集合对象时, 利用 order by 子句进行排序
    • order-by 属性中还可以加入 SQL 函数

    在操作中注意

    • 保存操作

          public void testMany2OneSave(){
              Customer customer = new Customer();
              customer.setCustomerName("AA");
              
              Order order1 = new Order();
              order1.setOrderName("ORDER-1");
              
              Order order2 = new Order();
              order2.setOrderName("ORDER-2");
              
              //设定关联关系
              order1.setCustomer(customer);
              order2.setCustomer(customer);
              
              customer.getOrders().add(order1);
              customer.getOrders().add(order2);
              
              //执行  save 操作: 先插入 Customer, 再插入 Order, 3 条 INSERT, 2 条 UPDATE
              //因为 1 的一端和 n 的一端都维护关联关系. 所以会多出 UPDATE
              //可以在 1 的一端的 set 节点指定 inverse=true, 来使 1 的一端放弃维护关联关系!
              //建议设定 set 的 inverse=true, 建议先插入 1 的一端, 后插入多的一端
              //好处是不会多出 UPDATE 语句
              session.save(customer);
              
      //      session.save(order1);
      //      session.save(order2);
              
              //先插入 Order, 再插入 Cusomer, 3 条 INSERT, 4 条 UPDATE
      //      session.save(order1);
      //      session.save(order2);
      //      
      //      session.save(customer);
          }
      
    • 查询操作
      通过多的一端查询 1 的一端

      public void testMany2OneGet(){
              //1. 若查询多的一端的一个对象, 则默认情况下, 只查询了多的一端的对象. 而没有查询关联的 1 的那一端的对象!
      
              Order order = (Order) session.get(Order.class, 1);
              System.out.println(order.getOrderName()); 
              
              System.out.println(order.getCustomer().getClass().getName());
              
              //session.close();
              
              //2. 在需要使用到关联的对象时, 才发送对应的 SQL 语句. 
              Customer customer = order.getCustomer();
              System.out.println(customer.getCustomerName()); 
              
              //3. 在查询 Customer 对象时, 由多的一端导航到 1 的一端时, 
              //若此时 session 已被关闭, 则默认情况下
              //会发生 LazyInitializationException 异常
              
              //4. 获取 Order 对象时, 默认情况下, 其关联的 Customer 对象是一个代理对象!
          }
      

      通过 1 的一端查询多的一端

      public void testOne2ManyGet(){
              //1. 对 n 的一端的集合使用延迟加载
              Customer customer = (Customer) session.get(Customer.class, 7);
              System.out.println(customer.getCustomerName()); 
              //2. 返回的多的一端的集合时 Hibernate 内置的集合类型. 
              //该类型具有延迟加载和存放代理对象的功能. 
              System.out.println(customer.getOrders().getClass()); 
              
              //session.close();
              //3. 可能会抛出 LazyInitializationException 异常 
              
              System.out.println(customer.getOrders().size()); 
              
              //4. 再需要使用集合中元素的时候进行初始化. 
          }
      
    • 删除操作

          public void testDelete(){
              //在不设定级联关系的情况下, 且 1 这一端的对象有 n 的对象在引用, 不能直接删除 1 这一端的对象
              Customer customer = (Customer) session.get(Customer.class, 1);
              session.delete(customer); 
          }
      
    • 修改操作

          public void testCascade(){
              Customer customer = (Customer) session.get(Customer.class, 3);
              customer.getOrders().clear();
          }
      

    相关文章

      网友评论

          本文标题:Hibernate_5 在hibernate 领域模型中单向多对

          本文链接:https://www.haomeiwen.com/subject/puutwxtx.html