学习hibernate(五) -- hibernate映射关系

组合关系

    在开发时,有一种情况,有一张数据表的数据来自于多个对象。比如,一个computer(电脑)表,其中有电脑的基本信息、CPU信息、显卡信息、主板信息和内存信息等等,对应的实体对象则是电脑对象、CPU对象、显卡对象和内存对象。这种情况下可以使用组合关系映射。

    以电脑与CPU为例,Computer类中包含了一个Cpu类,在*.hbm.xml文件中,使用component来进行组合关系映射。

    看一下这两个实体类的代码和映射文件:

package cn.net.bysoft.model;

public class Computer {
//getter and setter

    private int id;
    private String name;
    private Cpu cpu;
}
package cn.net.bysoft.model;

public class Cpu {

    //getter and setter

    private String name;
}
<?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 package="cn.net.bysoft.model">
    <class name="Computer" table="computer">
        <id name="id" type="integer" column="ID">
            <!-- 指定主键的生成方式,native是使用数据库本地的方式 -->
            <generator class="native"></generator>
        </id>
        <property name="name" type="string" column="NAME"></property>
        <!-- 使用组合关系映射,CPU的Name信息存放在Cpu_Name字段中。 -->
        <component name="cpu" class="Cpu">
            <property name="name" type="string" column="CPU_NAME"></property>
        </component>
    </class>
</hibernate-mapping>

    生成的数据表并测试save方法:

@Test
    public void testComponent() throws IOException {
        //    save一个组合关系对象。
        Computer computer = new Computer();
        computer.setName("pc");
        Cpu cpu = new Cpu();
        cpu.setName("Inter");
        computer.setCpu(cpu);
        session.save(computer);
    }

181926_IKI2_2450666.png

一对多与多对一

    一对多与多对一关联,一般用于一个对象包含另一个对象的集合,被包含的对象中存在包含对象的实体,比如一个customer可以有多比订单(order)。在数据表中,order表有customer表的外键。

    在*.hbm中,一的一端使用set标签设置多的一端的集合,在set中加入key与one-to-many。

    多的一端使用<one-to-may>属性配置一对多。

    看一下实体类代码和配置文件:

package cn.net.bysoft.model1;

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

public class Customer {
    getter/setter属性
    
    private int Id;
    private String name;
    //    一对用户可以有多个订单。
    private Set<Order> orders = new HashSet<Order>();
}
package cn.net.bysoft.model1;

public class Order {
    getter/setter属性
    
    private int id;
    private String name;
    //    每个订单都属于一个用户。
    private Customer customer;
}
<?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="cn.net.bysoft.model1.Customer" table="CUSTOMERS">
        <id name="id" type="integer" column="ID">
            <!-- 指定主键的生成方式,native是使用数据库本地的方式 -->
            <generator class="native"></generator>
        </id>
        <property name="name" type="string" column="NAME"></property>
        <!-- 一对多 -->
        <set name="orders" table="ORDERS">
            <key column="CUSTOMER_ID"></key>
            <one-to-many class="cn.net.bysoft.model1.Order"/>
        </set>
    </class>
</hibernate-mapping>
<?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="cn.net.bysoft.model1.Order" table="ORDERS">
        <id name="id" type="integer" column="ID">
            <!-- 指定主键的生成方式,native是使用数据库本地的方式 -->
            <generator class="native"></generator>
        </id>
        <property name="name" type="string" column="NAME"></property>
        <!-- 多对一 -->
        <many-to-one name="customer" class="cn.net.bysoft.model1.Customer" column="CUSTOMER_ID">
        </many-to-one>
    </class>
</hibernate-mapping>

    新增和删除的时候有一些需要注意的地方,看一段save代码:

    @Test
    public void testOneToManySave() {
        Customer customer = new Customer();
        customer.setName("Kobe");

        Order order1 = new Order();
        order1.setName("buy book");
        Order order2 = new Order();
        order2.setName("buy ball");

        customer.getOrders().add(order1);
        customer.getOrders().add(order2);

        order1.setCustomer(customer);
        order2.setCustomer(customer);

        session.save(customer);
        session.save(order1);
        session.save(order2);

        /**
         * output: Hibernate: 
         * insert into CUSTOMERS (NAME) values (?) 
         * insert into ORDERS (NAME, CUSTOMER_ID) values (?, ?) 
         * insert into ORDERS (NAME, CUSTOMER_ID) values (?, ?)
         * */
    }

    先save一的一端,在save多的一端,会打印三条sql语句,是正常的。如果先保存多的一端在保存一的一端,会输出七条sql语句,其中有4条是update语句,因为先保存多的一端,此时并没有一的一端的主键,等到保存好一的一端后,在回头由两端都进行update。

        session.save(order1);
        session.save(order2);
        session.save(customer);

        /**
         * output: 
         * Hibernate: insert into ORDERS (NAME, CUSTOMER_ID) values (?,?) 
         * Hibernate: insert into ORDERS (NAME, CUSTOMER_ID) values (?, ?)
         * Hibernate: insert into CUSTOMERS (NAME) values (?) 
         * Hibernate: update ORDERS set NAME=?, CUSTOMER_ID=? where ID=? 
         * Hibernate: update ORDERS set NAME=?, CUSTOMER_ID=? where ID=? 
         * Hibernate: update ORDERS set CUSTOMER_ID=? where ID=? 
         * Hibernate: update ORDERS set CUSTOMER_ID=? where ID=?
         * */

    正常来说,不需要两端都进行update维护,要解决该问题需要在一端加入inverse属性,建议由多的一端去控制,所以在Customer.hbm.xml中的set节点中加入:

<set name="orders" table="ORDERS" inverse="true">

    在进行保存时,update语句减少了,只由多的一端维护关系:

175954_WoP5_2450666.png

    再来说说delete。删除时在不设置级联的情况下,不能删除一的一端,因为这一端的主键被关键关联着:

    @Test
    public void testOneToManyDelete() {
        Customer customer = (Customer) session.get(Customer.class, 1);
        session.delete(customer);
        /**
         * output: 
         * INFO: HHH000010: On release of batch it still contained JDBC statements
         * */
    }

    使用级联关系的属性是cascade,该属性有三个值,分别是:

  • delete:删除一的一端,会连带着删除多的一端;

  • delete-orphan:一的一端使用多的一端的集合的clear属性可以删除多的一端;

  • save-update:值保存一的一端,多的一端会自动保存;

    在设置好级联关系后,可直接删除一的一端:

<set name="orders" table="ORDERS" inverse="true" cascade="delete">

    在项目中建议使用手动控制关联关系。

一对一

    有两种情况均为一对一关联关系,一个部门有一个部门经理。可以使用某一个字段做外键,也可以使用主键做外键。先来看看主外键情况做一对一。

    在Manager对象的配置文件中加入<one-to-one>节点,而Dept对象中加入<many-to-one>节点,对该节点加入unique属性进行唯一约束,下面是实体类与配置文件内容:

package cn.net.bysoft.model1;

public class Dept {
    //getter/setter

    private int id;
    private String name;
    private Manager manager;
}
package cn.net.bysoft.model1;

public class Manager {
    //getter/setter
    
    private int id;
    private String name;
    private Dept dept;
}
<?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="cn.net.bysoft.model1.Dept" table="DEPT">
        <id name="id" type="integer" column="ID">
            <!-- 指定主键的生成方式,native是使用数据库本地的方式 -->
            <generator class="native"></generator>
        </id>
        <property name="name" type="string" column="NAME"></property>
        <!-- 多对一 -->
        <many-to-one name="manager" class="cn.net.bysoft.model1.Manager"
            column="MANAGER_ID" unique="true"></many-to-one>
    </class>
</hibernate-mapping>
<?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="cn.net.bysoft.model1.Manager" table="MANAGER">
        <id name="id" type="integer" column="ID">
            <!-- 指定主键的生成方式,native是使用数据库本地的方式 -->
            <generator class="native"></generator>
        </id>
        <property name="name" type="string" column="NAME"></property>
        <!-- 一对一 -->
        <one-to-one name="dept" class="cn.net.bysoft.model1.Dept" property-ref="manager"></one-to-one>
    </class>
</hibernate-mapping>

    接下来是主键与主键做一对一关系,实体类无需改变,只需要修改dept的配置文件,将id节点的class修改成foreign模式,将many-to-one修改成one-to-one,添加constrained属性等于true,具体如下:

<?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="cn.net.bysoft.model1.Dept" table="DEPT">
        <id name="id" type="integer" column="ID">
            <!-- 指定主键的生成方式,native是使用数据库本地的方式 -->
            <generator class="foreign">
                <param name="property">manager</param>
            </generator>
        </id>
        <property name="name" type="string" column="NAME"></property>
        <one-to-one name="manager" class="cn.net.bysoft.model1.Manager" constrained="true">
        </one-to-one>
    </class>
</hibernate-mapping>

    增删改查方法的调用与一对多多对一一样。

多对多

    用数据表描述多对多,需要有三张表,A表,B表,A-R-B表。

    用对象描述多对多,A对象中有B对象的集合,B对象中也有A对象的集合。

    举个例子,现在有订单类与商品类,一个订单中可以有多个商品,而一个商品也可以属于多个订单。看一下实体类的代码:

package cn.net.bysoft.model1;

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

public class Orders {

    //getter and setter

    private int id;
    private String name;
    private Set<Products> products = new HashSet<Products>();
}
package cn.net.bysoft.model1;

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

public class Products {
    //getter and setter

    private int id;
    private String name;
    private Set<Orders> orders = new HashSet<Orders>();
}

    两个对象的配置文件也很像,每个配置文件中都有set节点,但是在多对多中,必须有一个set的inverse=true,具体如下:

235947_cUT3_2450666.png

235948_3L4k_2450666.png

    使用方式与一对多多对一相同。

转载于:https://my.oschina.net/u/2450666/blog/653932

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值