Hibernate一对一关联关系

  一对一的关联关系有一下几类:

  1. 单向一对一

    a). 基于主键的

    b). 基于外键的

  2. 双向一对一

    a). 基于外键的

好, 下面咱们主要说一下怎么来设置这种关系, 主要是说怎么用, 其中也说一下一些地方为什么这么配置.

OneByOne

  一: 基于主键的 单向一对一

    我们使用 Person类和 IdCard类来做测试, 一个Person 只能对应一个 IdCard. Just So.

    Person类:

package com.single.one2one_primary;

public class Person {

    private Integer id;
    private String name;

    private IdCard idCard;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public IdCard getIdCard() {
        return idCard;
    }

    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }

}

    IdCard类:

package com.single.one2one_primary;

public class IdCard {

    private Integer id;
    private String card;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getCard() {
        return card;
    }

    public void setCard(String card) {
        this.card = card;
    }

}

    IdCard类的映射文件:

<?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.test.one2one_primary.IdCard" table="IDCARDS">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="identity" />
        </id>
        <property name="card" type="java.lang.String">
            <column name="CARD" />
        </property>

    </class>
</hibernate-mapping>

    Person的映射文件:

<?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.test.one2one_primary.Person" table="PERSONS">

        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <!--
                使用 foregin策略:
                    指使用"对方"的主键来生成自己的主键, 自己并不能独立生成主键.

                <param name="property">idCard</param>:
                    指定使用当前持久化类中的那个属性作为"对方"
                    name="property": 固定写法

                使用该策略生成主键, 还需要增加一个 <one-to-one> 标签
                并且 <one-to-one> 标签属性还应增加 constrained="true" 属性

                constrained="true":
                    指为当前持久化类对应的数据表的主键增加一个外键约束
                    引用被关联的对象("对方")所使用的数据库主键
             -->
            <generator class="foreign" >
                <param name="property">idCard</param>
            </generator>
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>

        <one-to-one name="idCard" constrained="true"></one-to-one>

    </class>
</hibernate-mapping>

    Hibernate主配置文件:

<?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="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">oracle</property>
        <property name="hibernate.connection.url">jdbc:mysql:///hibernate</property>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

        <!-- 配置数据库方言 -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL57InnoDBDialect</property>

        <!-- 设置数据表生成策略 -->
        <property name="hibernate.hbm2ddl.auto">update</property>

        <!-- 是否格式化SQL -->
        <property name="hibernate.format_sql">true</property>

        <!-- 是否显示SQL -->
        <property name="hibernate.show_sql">true</property>

        <mapping resource="com/single/one2one_primary/IdCard.hbm.xml"/>
        <mapping resource="com/single/one2one_primary/Person.hbm.xml"/>
        <!--
        <mapping resource="com/single/one2one_foreign/IdCard.hbm.xml"/>
        <mapping resource="com/single/one2one_foreign/Person.hbm.xml"/>
        <mapping resource="com/doubles/one2one_foreign/IdCard.hbm.xml"/>
        <mapping resource="com/doubles/one2one_foreign/Person.hbm.xml"/>
        -->

    </session-factory>
</hibernate-configuration>

    建立单元测试类:

ackage com.single.one2one_foreign;

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

public class TestHibernate {

    private SessionFactory sessionFactory;
    private Session session;
    private Transaction transaction;

    @Before
    public void init(){

        sessionFactory = new Configuration().configure().buildSessionFactory();
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
    }

    @After
    public void distory(){
        transaction.commit();
        session.close();
        sessionFactory.close();
    }
}

    测试保存数据:

    @Test
    public void testInsert(){

        Person person = new Person();
        person.setName("Jerry");

        IdCard idCard = new IdCard();
        idCard.setCard("100003");

        // 设置关联关系
        person.setIdCard(idCard);

        // 不管保存顺序怎么样, 总是先保存 Order表, 然后再保存 Person表.
        session.save(person);
        session.save(idCard);

    }

    测试查询数据:

    @Test
    public void testQuery(){

        // 默认使用懒加载
        // 可以在 <one-to-one> 总加上 lazy="false" 关闭懒加载
        Person person = session.get(Person.class, 1);
        System.out.println(person.getName());

        // 这是一个代理对象
        System.out.println(person.getIdCard().getClass().getName());

    }

    测试删除数据:

    @Test
    public void testDelete(){

        IdCard idCard = session.get(IdCard.class, 1);

        // 删除不了的, 因为有外键约束!
        // 如何解决呢? 我添加了 cascade="all" 属性, 但是不管用. 不知道怎么解决了.
        session.delete(idCard);

    }

    测试更新数据:

    @Test
    public void testUpdate(){

        IdCard idCard = session.get(IdCard.class, 1);
        idCard.setCard("200002");

        // 我感觉更新数据一般不会出现什么问题, 主要是保存数据和删除数据的时候容易出问题.
        session.update(idCard);

    }

  二: 基于外键的单向一对一

    还是利用上面的Person类和IdCard类, 咱们主要是修改配置文件

    使用外键, 然后由于是单向的, 所以配置比较简单, 主要来修改 Person.hbm.xml 文件, eg:

<?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 package="com.single.one2one_foreign">
    <class name="Person" table="PERSONS">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="identity" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>

        <!--
            使用  unique="true" 来确定是唯一的, 要不然会出现一对多现象.
        -->
        <many-to-one name="idCard" unique="true"
            class="IdCard" column="IDCARD_ID"></many-to-one>

    </class>
</hibernate-mapping>

    这个外键会出现在 Person 表中, 下面咱们还要说双向一对一, <many-to-one>标签出现在哪个表中, 外键就会在哪个表中.

    测试数据的保存:

    @Test
    public void testInsert(){

        Person person = new Person();
        person.setName("Tom");

        IdCard idCard = new IdCard();
        idCard.setIdCard("10002");

        // 设置关联关系
        person.setIdCard(idCard);

        // 先保存没有外键的一端, 然后再保存有外键的一端
        // 否则会多出一条 update 语句
        session.save(idCard);
        session.save(person);

    }

    测试查询数据(同上面一样, 默认使用懒加载):

    @Test
    public void testQuery(){

        // 默认使用懒加载
        // 可以在 <one-to-one> 总加上 lazy="false" 关闭懒加载
        Person person = session.get(Person.class, 1);
        System.out.println(person.getName());

        // 这是一个代理对象
        System.out.println(person.getIdCard().getClass().getName());

    }

    测试删除数据(同上面一样, 级联删除还是不行):

    @Test
    public void testDelete(){

        IdCard idCard = session.get(IdCard.class, 1);

        // 删除不了的, 因为有外键约束!
        // 如何解决呢? 我添加了 cascade="all" 属性, 但是不管用. 不知道怎么解决了.
        session.delete(idCard);

    }

    测试更新数据(同上面一样):

    @Test
    public void testUpdate(){

        IdCard idCard = session.get(IdCard.class, 1);
        idCard.setIdCard("200002");

        // 我感觉更新数据一般不会出现什么问题, 主要是保存数据和删除数据的时候容易出问题.
        session.update(idCard);

    }

  三: 基于外检的双向一对一

    这里跟基于外键的单向一对一差不多, 无非就是在另一个持久化类中添加了一个属性,

    这个地方, 我们需要在 IdCard 类中添加 Person 类对象, 并添加上 getter/setter 方法. 然后对它们的映射文件进行配置:

    IdCard的映射文件:

<?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 package="com.doubles.one2one_foreign">
    <class name="IdCard" table="IDCARD">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="identity" />
        </id>
        <property name="idCard" type="java.lang.String">
            <column name="IDCARD" />
        </property>

        <!--
            一定要加上 property-ref="idCard"
            否则, Hibernate查询的时候, 会出现SQL连接逻辑出错 on idcard0_.ID=person1_.ID
            正常的应该是: on idcard0_.ID=person1_.IDCARD_ID
            如果改为 property-ref="name"(这个name是Person类中的name)
            那么, 左外连接语句就变成了 on idcard0_.ID=person1_.NAME
            可以发现我们配置的 property-ref 的值在对应类中的属性对应的列
            就是左外连接 on idcard0_.ID=person1_.? 处, ? 所代表的的值.
            如果不配置, property-ref 的取值就是本类的主键属性对应的数据列(ID)
            配置成 name 就是 Person类中 name 属性对应的数据列(NAME)
            配置成 idCard 就是 Person 类中 idCard 属性对应的数据列(IDCARD_ID)
        -->
        <one-to-one name="person" class="Person" property-ref="idCard"></one-to-one>

    </class>
</hibernate-mapping>

    Person类的映射文件:

<?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 package="com.doubles.one2one_foreign">
    <class name="Person" table="PERSON">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="identity" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>

        <!--
            使用唯一约束(unique="true")保证是一对一
            column="IDCARD_ID": 外键的名字
        -->
        <many-to-one name="idCard" class="IdCard"
            column="IDCARD_ID" unique="true">
        </many-to-one>

    </class>
</hibernate-mapping>

    测试保存数据:

    @Test
    public void testInsert(){

        Person person = new Person();
        person.setName("Mike");

        IdCard idCard = new IdCard();
        idCard.setIdCard("10001");

        person.setIdCard(idCard);
        idCard.setPerson(person);

        // 先保存没有外键的一端, 然后再保存有外键的一端
        // 否则会多出一条 update 语句
        session.save(idCard);
        session.save(person);

    }

    测试查询数据:

    @Test
    public void testQuery(){

        // 在双向一对一的情况下
        // 1. 查询没有外键的数据表时, 默认使用左外连接(left outer join)进行查询
        IdCard idCard = session.get(IdCard.class, 1);
        System.out.println(idCard.getIdCard());
        System.out.println(idCard.getPerson().getClass().getName());

        // 2. 查询有外键的数据表时, 默认使用懒加载
        Person person = session.get(Person.class, 1);
        System.out.println(person.getName());
        System.out.println(person.getIdCard().getClass().getName());

    }

    测试删除数据(同上):

    只要有外键关联, 没有设置级联操作, 而且外键还有数据表在引用, 就不能删除. 不论是本篇博文中的那种关系.

    测试更新数据(同上):

    最容易出错的就是查询数据中的懒加载和删除数据中的级联操作, 至于更新数据, 没什么问题.

    你很有可能问, 为什么没有基于主键的双向一对一?

    很好理解, 因为基于主键的单向一对一: 一方的主键使用另一方的主键来生成自己的主键, 它自己不能生成主键, 只能依赖于对方, 如果是双向的话, 这样, 一方依赖另一方生成主键, 另一方又依赖这一方生成主键, 这样就乱了.

  

时间: 2024-11-05 21:58:36

Hibernate一对一关联关系的相关文章

Hibernate 一对一关联关系

在Hibernate中,一对一关联关系有两种情况: 基于外键映射的一对一 基于主键映射的一对一 基于外键映射的一对一 这种映射其实是由多对一而来,这种映射情况下,外键可以放在任意的一端,在需要存放外键的一端(简称a端),添加<many-to-one>节点,为<many-to-one>节点增加unique="true"属性来约束多对一成为一对一,另一端(简称b端)使用<one-to-one>节点来映射,并增加property-ref属性指定使用在该端(

hibernate 一对一关联关系 及其懒加载,总结

一对一,则有主对象和从对象之区别 若无主对象,则从对象亦无存在之意义. 主对象为person 从对象为idcard <hibernate-mapping package="cn.itcast.hibernate.domain"> <class name="Person"> <id name="id"> <generator class="native" /> </id>

Hibernate关联关系映射之一对一关联关系

人和身份证之间就是一个典型的一对一关联关系.实现一对一关联关系映射的方式有两种一种是基于外键,一种是基于主键,下面我们先看基于外键的关联方式 首先看他们的实体类 Person类 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package entity; public class Person {     private Integer id;     private String name;     pr

Hibernate映射一对一关联关系

映射一对一关联 Hibernate提供了两种映射一对一关联关系的方式,分别是按照外键映射和按照主键映射. 下面是员工表和档案信息表(员工和档案表之间的关系是一对一的关系)  档案表(dept) 1 public class Dept { 2 private Integer deptid; 3 private String mystreet; 4 private Emp emp; 5 6 public Dept() { 7 } 8 9 public Dept(Integer deptid, Str

Hibernate(5)—— 联合主键 、一对一关联关系映射(xml和注解) 和 领域驱动设计

俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及的知识点总结如下: One to One 映射关系 一对一单向外键(XML/Annotation) 一对一双向外键关联(XML/Annotation) 联合主键 一对一单向外键联合主键(Xml/Annotation) 一对一组件关联(XML/Annotation) 理解组件 领域驱动设计——自动生成数据库脚本 一对一关系的小结 一些出错问题的总结 自动生成数据库脚本 一般在项目开发过程中,我们的习惯是先建好数据库和表,然后在进

Hibernate之关联关系映射(一对一主键映射和一对一外键映射)

1:Hibernate的关联关系映射的一对一外键映射: 1.1:第一首先引包,省略 1.2:第二创建实体类: 这里使用用户信息和身份证信息的关系,用户的主键编号既可以做身份证信息的主键又可以做身份证信息的外键,这里先做外键. 创建User.java: 用户和身份证一对一的关联关系映射       private IdCart idCart; IdCart.java: 身份证和用户,一对一的关系       private User user; 1 package com.bie.bean; 2

(十)Hibernate的一对一关联关系

一.概述 我们以Company与Address类为例,介绍映射一对一关联关系的方法. 一对一的关联方法有两种 -按照外键映射:两个表任意一个表定义一个外键,来关联另一个表. -按照主键映射:一个表的主键同时作为外键,和另一个表的主键保持一致. 二.按照外键映射 (1)创建实体类 public class Company { private Integer id; private String name; private Address address; public Integer getId(

Hibernate利用关联关系操纵对象

利用关联关系操纵对象 数据对象之间关联关系有一对一.一对多及多对多关联关系.在数据库操作中,数据对象之间的关联关系使用JDBC处理很困难.本节讲解如何在Hibernate中处理这些对象之间的关联关系.本节使用到4个类,它们分别是Student(学生).Card(学生证).Group(班级)和Course(课程),它们之间的关联关系如图1-1所示.这些实体存在级联(cascade)问题.例如,当删除一个班级的信息时,还要删除该班的所有学生的基本信息.如果直接使用JDBC执行这种级联操作,会非常烦琐

hibernate.一对一关联

实体类关系 一对一 一对多 多对一 多对多 Hibernate提供了两种映射一对一关联关系的方式:按照外键映射和按照主键映射.下面以员工账号和员工档案表为例,介绍这两种映射方式,并使用这两种映射方式分别完成以下持久化操作: (1)保存员工档案的同时分配给员工一个账号. (2)加载员工档案的同时加载账号信息. 1.按照外键映射 步骤一:创建实体类Users1和Resume1 Users1创建如下: public class Users1 { private Integer userid; priv