美文网首页
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