一对一映射本来以为挺简单的,但是一接触发现还是有些琢磨头的,但是理解之后,才会发现原来是挺简单的。
一对一映射关系,也有一个主表和从表的概念,例如人和身份证就是一对一的关系,如果将IdCard的主键设为Person的主键,那么Person为主表,而IdCard为从表,这样的映射关系决定了从表不能单独存在,必须依赖于一个主表。但是主表却可以单独存在,因为它不受从表的限制。并且得先有主表中的记录,才会对应的有从表中的记录。
下面举例说明:
建立Person和IdCard两个类:
package com.suo.domain;
public class Person {
private int id;
private String name;
private IdCard idCard;
……//set/get方法
}
package com.suo.domain;
public class IdCard {
private int id;
private String No;
private Person person;
……//set/get方法
}
然后就是两个类的映射文件:
Person.hbm.xml:
<hibernate-mapping package="com.suo.domain">
<class name="Person">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<one-to-one name="idCard"/>
<!-- 这一句作用不大,只是和idCard建立起关联,在通过主表查询从表的时候起作用-->
</class>
</hibernate-mapping>
IdCard.hbm.xml:
<hibernate-mapping package="com.suo.domain">
<class name="IdCard" table="id_card">
<id name="id">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<property name="No"/>
<one-to-one name="person" constrained="true"/>
<!-- 这一句很关键,因为这里的主键和person的主键是相同的,所以要和person建立起关联,
这也就是为什么这个表是从表的原因。constrained是添加约束,即将主键同时设置为外键-->
</class>
</hibernate-mapping>
注意这两个映射文件的最后一个属性的配置,IdCard.hbm.xml中的person属性是一定要配的,因为还要通过这个属性找到主表中对应记录的主键,将它作为自己的主键,它建立起从表和主表的关联。而Person.hbm.xml中的idCard属性却不是一定要配的,不配的话,不会影响到IdCard记录的保存,也不会影响到它本身的保存,那么它什么时候起作用呢?就在于通过主表来查询从表的信息时起作用,若是不配这个属性的话,那么就没有建立起主表到从表的关联,是不能通过主表记录查询到从表相应的记录的(即使有getIdCard()和setIdCard()方法,也不会起作用,因为hibernate是根据映射文件来起作用的,没有配置的属性,就等于没有这个属性一样)。所以可以认为我们这里配置的是一个双向的关联,主表关联到从表,从表关联到主表。
下面再来说一下添加记录时的情况:
若是单单添加Person,那么很简单,因为它不受IdCard的限制,可以单独存在,这个就不多说了。关键是添加IdCard的记录时,就有些稍微复杂了。先看一个例子(添加的方法就不贴出来了):
public static void main(String args[]){
Person person=new Person();
person.setName("suo");
IdCard idCard=new IdCard();
idCard.setPerson(person);//相当于是设定主键,因为idCard的主键是person的外键。
idCard.setNo("130427");
add(idCard);
}
注意,这里定义了一个Person对象,一个IdCard对象,然后只执行了一个add(idCard)方法,即没有去添加person,而是直接去添加身份证记录,会报错吗???答案是不会报错的。运行之后,先看一下执行的sql语句:
Hibernate: insert into Person (name) values (?)
Hibernate: insert into id_card (No, id) values (?, ?)
可以看到执行了两个插入语句,而且第一个是插入person的,第二个才插入idCard,我们并没有调用add(person)啊,怎么会输出插入person呢?这个我也不知道其中的内部机理,看源代码完全,也不知道哪里是哪里。可能是在setPerson的时候,发现这个person对象是处于瞬时状态,并且在设置主键的时候,要用到这个person对象的主键,那么只有先添加这个person对象的记录了。
所以,我们可以得到如下的结论:
若要添加一个身份证记录,如果person表中已经存在了和这个身份证对应的记录,那么设置好属性,直接插入即可;如果person表中还没有和这个身份证对应的记录,那么一定要先添加一个和这个身份证对应的person,然后再添加身份证记录,这个是必须的,即使你没有显示的去调用add(person),add(idCard)也会分两步执行,先添加person,后添加对应的身份证记录,但是前提必须是有一个person对象,不管是脱管的还是瞬时的,一定要有,否则身份证记录得不到主键,就会报错了。