Hibernate——关系映射之一对一

关系映射之一对一

基于主键的一对一

基于主键的一对一就是说:对于从表,它的主键就是它的外键。那么这个一对一关系就非常明确。

经典案例:人<–>身份证,Person的id同时是IDCard的id

这里写图片描述

我们建立项目HibernateOneToOne进行测试。

两个domain对象以及它们的映射文件如下:

  • Person对象以及映射文件:
package com.gavin.domain;

import java.io.Serializable;

public class Person implements Serializable{
    private int id;
    private String name;
    private IDCard idCard;

    public int getId() {
        return id;
    }

    public void setId(int 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;
    }
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD//EN"
        "http://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.gavin.domain">
    <class name="com.gavin.domain.Person" table="person" schema="hibernate">
        <id name="id" column="id" type="java.lang.Integer">
            <generator class="increment"/>
        </id>
        <property name="name" column="name" length="64" type="java.lang.String"/>
        <!--这里配置Person和IdCard属性是一对一的关系-->
        <one-to-one name="idCard"/>
    </class>
</hibernate-mapping>
  • IDCard对象以及它的映射文件:
package com.gavin.domain;

import java.io.Serializable;
import java.util.Date;

public class IDCard implements Serializable {
    private int id;
    private Date validateTime;
    private Person person;

    public int getId() {
        return id;
    }

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

    public Date getValidateTime() {
        return validateTime;
    }

    public void setValidateTime(Date validateTime) {
        this.validateTime = validateTime;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD//EN"
        "http://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.gavin.domain.IDCard" table="idcard" schema="hibernate">
        <id name="id" type="java.lang.Integer" column="id">
            <!--因为IDCard的主键是引用Person的主键,故这里采用外键策略-->
            <generator class="foreign">
                <!--这里的值是跟哪一个domain属性一对一-->
                <param name="property">person</param>
            </generator>
        </id>
        <property name="validateTime" column="validateTime" type="java.util.Date"/>
        <one-to-one name="person" constrained="true"/>
    </class>
</hibernate-mapping>

这里需要注意的是,在IDCard的映射文件中,主键ID的生成策略必须采用foreign。另外,one-to-one必须设置constrained='true',对应的数据库表idcard才能生成外键关系,否则是不生成外键的。这个结论从Hibernate生成的SQL语句也可以看出来。

  • 测试代码如下:
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();

Person person = new Person();
person.setName("小明");
IDCard idCard = new IDCard();
idCard.setValidateTime(new Date());

// 表明idCard对象是属于person这个对象的
idCard.setPerson(person);
// 这样是不行的,因为是是IDCard关联Person,所以只能idCard.setPerson(person)
// person.setIdCard(idCard);

// 最好先保存Person,再保存idCard
session.save(person);
session.save(idCard);

transaction.commit();
  • Hibernate生成的SQL语句如下:
Hibernate: drop table if exists idcard
Hibernate: drop table if exists person
Hibernate: create table idcard (id integer not null, validateTime datetime, primary key (id)) engine=MyISAM
Hibernate: create table person (id integer not null, name varchar(64), primary key (id)) engine=MyISAM
Hibernate: alter table idcard add constraint FK4opm8ny1jtp2g518o4ktymcne foreign key (id) references person (id)
Hibernate: select max(id) from person
Hibernate: insert into person (name, id) values (?, ?)
Hibernate: insert into idcard (validateTime, id) values (?, ?)
  • 数据库表结构如下:

这里写图片描述

基于外键的一对一

基于外键的一对一,Person的id和IDCard的id不一样,此时IDCard增加一列pid来外键关联Person的id,以此形成一对一的关系。

这里写图片描述

那么这里就有一个问题,如上图所示,外键怎么确保一对一关系而不重复呢?(如果不加额外的约束,一个Person可以同时被两个IDCard关联)

解决这个问题的方案就是:IDCard中的pid有外键约束,同时也有unique约束,要保证pid在IDCard表中也是唯一的。


在基于外键的一对一关系中,domain对象和基于主键的一对一是一样的,只是从表对应的对象的关系映射文件要变一变。

也就是说,这里的IDCard.hbm.xml文件要稍作修改。

其实,这里的one-to-one关系只是many-to-one的一个特例,因为不加约束的话,一个Person可以对应多个IDCard,所以这里我们给IDCard配上many-to-one关系,但是要加上unique='true'的约束,这样才能符合我们的要求。

  • IDCard.hbm.xml修改如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD//EN"
        "http://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.gavin.domain.IDCard" table="idcard" schema="hibernate">
        <id name="id" type="java.lang.Integer" column="id">
            <generator class="assigned"/>
        </id>
        <property name="validateTime" column="validateTime" type="java.util.Date"/>
        <many-to-one name="person" column="pid" unique="true"/>
    </class>
</hibernate-mapping>

可以看到,改变的地方有两个,一个是主键ID的生成策略,这里由之前的foreign改成了assigned;另外就是这里配置的是many-to-one,但是添加了unique约束。

  • 测试方法需要手动设置IDCard的id号,如下:
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();

Person person = new Person();
person.setName("小明");
IDCard idCard = new IDCard();
idCard.setId(101010);
idCard.setValidateTime(new Date());

// 表明idCard对象是属于person这个对象的
idCard.setPerson(person);
// 这样是不行的,因为是是IDCard关联Person,所以只能idCard.setPerson(person)
//        person.setIdCard(idCard);

session.save(person);
session.save(idCard);

transaction.commit();
  • Hibernate生成的SQL语句为:
Hibernate: drop table if exists idcard
Hibernate: drop table if exists person
Hibernate: create table idcard (id integer not null, validateTime datetime, pid integer, primary key (id)) engine=MyISAM
Hibernate: create table person (id integer not null, name varchar(64), primary key (id)) engine=MyISAM
Hibernate: alter table idcard add constraint UK_p21bt5txra8q5jmfpvq8kv2h unique (pid)
Hibernate: alter table idcard add constraint FKq0b0d1t0ol6vj0bfoam5f44eb foreign key (pid) references person (id)
Hibernate: select max(id) from person
Hibernate: insert into person (name, id) values (?, ?)
Hibernate: insert into idcard (validateTime, pid, id) values (?, ?, ?)

可以看到idcard表相比之前又增加了一列pid,并且添加了外键约束和unique约束。

  • 生成的表结构如下:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/gggavin/article/details/79511112