在关联中,一般来说都存在着单向关联和双向关联。然后,在一对一关联中,我觉得是不存在这样单向一对一关联的,因为一对一关联双方需要相互感知。在Hibernate中,一对一关联的设置存在两种方式:共享主键和唯一外键。
共享主键
关系的拥有者负责生成这样的一个主键,然后让关系的另一方知道,下面用关于居民和护照的一个例子来说明。
首先,居民拥有三个属性(id、name、passport),护照也拥有三个属性(id、serialNo、person)。
数据库设计
由于是共享主键,那么数据库的设计如下:
t_person
person_id | person_name |
passport_id | passport_serialNo |
java类设计
Person
@SuppressWarnings("serial")
public class Person implements Serializable {
private Integer id;
private String name;
private Passport passport;
}
Passport
@SuppressWarnings("serial")
public class Passport implements Serializable {
private Integer id;
private String serialNo;
private Person person;
}
关系映射文件配置
从上述意义上来说,Person是这个一对一关联关系的拥有者,那么自然是由Person来生成主键,然后再将这个主键值赋值给Passport。
Person.hbm.xml
<hibernate-mapping>
<class name="com.daniel.model.persistence.Person" table="t_person">
<id name="id" column="person_id" type="java.lang.Integer">
<generator class="native"></generator>
</id>
<property name="name" column="person_name" type="java.lang.String">
</property>
<one-to-one name="passport" cascade="all" class="com.daniel.model.persistence.Passport">
</one-to-one>
</class>
</hibernate-mapping>
级联方式自然是选择all,因为无论增删改都需要进行表之间的关联。
Passport.hbm.xml
<hibernate-mapping>
<class name="com.daniel.model.persistence.Passport" table="t_passport">
<id name="id" column="passport_id" type="java.lang.Integer">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<property name="serialNo" column="passport_serialNo" type="java.lang.String">
</property>
<one-to-one name="person" class="com.daniel.model.persistence.Person" constrained="true">
</one-to-one>
</class>
</hibernate-mapping>
注意,当使用foreign这种主键生成策略的时候,表明需要传递一个参数给当前对象作为主键,上面配置的是使用person属性的主键作为参数。
运行结果
Session session = HibernateSessionFactory.getSession();
Transaction tx = null;
try
{
tx= session.beginTransaction();
Person per = new Person();
per.setName("张三");
Passport pass = new Passport();
pass.setSerialNo("wdqwdqdqw12312312");
/*
* 必须同时设置,否则只会插入一条记录,正确来说应该是插入两条记录
*/
per.setPassport(pass);
pass.setPerson(per);
session.save(per);
tx.commit();
}
catch(Exception e)
{
tx.rollback();
}
finally
{
HibernateSessionFactory.closeSession();
}
可以看到执行了的sql语句
Hibernate: insert into t_person (person_name) values (?) Hibernate: insert into t_passport (passport_serialNo, passport_id) values (?, ?) |
如果我们执行查询
Person per = (Person) session.get(Person.class, 5);
看到这时候只执行了一条sql查询语句,是使用左连接的
Hibernate: select person0_.person_id as person1_1_1_, person0_.person_name as person2_1_1_, passport1_.passport_id as passport1_0_0_,
passport1_.passport_serialNo as passport2_0_0_ from t_person person0_
left outer join t_passport passport1_ on person0_.person_id=passport1_.passport_id where person0_.person_id=?
唯一外键关联
这种方式需要在关系拥有者所在的表中加入一个外键列,因为两者的主键不再是一一对应,所有需要在关系的拥有者处加入一个列来记录关系的另一方的主键。
数据库的设计
t_person1
person_id | person_name | passport_id |
passport_id | passport_serialNo |
跟共享主键的类设计一样。
映射文件配置
Person.hbm.xml
<hibernate-mapping>
<class name="com.daniel.model.persistence.Person" table="t_person1">
<id name="id" column="person_id" type="java.lang.Integer">
<generator class="native"></generator>
</id>
<property name="name" column="person_name" type="java.lang.String">
</property>
<many-to-one name="passport" cascade="all" class="com.daniel.model.persistence.Passport">
<column name="passport_id"></column>
</many-to-one>
</class>
</hibernate-mapping>
这时候,用<many-to-one>来配置关系的拥有者,因为唯一外键关联是一对多的特殊形式,column指定外键列名。
Passport.hbm.xml
<class name="com.daniel.model.persistence.Passport" table="t_passport1">
<id name="id" column="passport_id" type="java.lang.Integer">
<generator class="native"></generator>
</id>
<property name="serialNo" column="passport_serialNo" type="java.lang.String">
</property>
<one-to-one name="person" class="com.daniel.model.persistence.Person">
</one-to-one>
</class>
运行结果
Session session = HibernateSessionFactory.getSession();
Transaction tx = null;
try
{
tx= session.beginTransaction();
Person per = new Person();
per.setName("李四");
Passport pass = new Passport();
pass.setSerialNo("fqwefwef1332");
per.setPassport(pass);
session.save(per);
tx.commit();
}
catch(Exception e)
{
tx.rollback();
}
finally
{
HibernateSessionFactory.closeSession();
}
这时候,不需要两方同时设置,只需要设置一方就可以了。可以看到执行的sql语句,先插入一条t_passport1记录,再插入一条t_person1记录。
Hibernate: insert into t_passport1 (passport_serialNo) values (?)
Hibernate: insert into t_person1 (person_name, passport_id) values (?, ?)
执行查询
Person per = (Person) session.get(Person.class, 1);
System.out.println(per.getPassport().getId());
输出的sql语句
Hibernate: select person0_.person_id as person1_1_0_, person0_.person_name as person2_1_0_, person0_.passport_id as passport3_1_0_ from
t_person1 person0_ where person0_.person_id=?
Hibernate: select passport0_.passport_id as passport1_0_1_, passport0_.passport_serialNo as passport2_0_1_, person1_.person_id as person1_1_0_,
person1_.person_name as person2_1_0_, person1_.passport_id as passport3_1_0_ from t_passport1 passport0_ left outer join t_person1 person1_ on
passport0_.passport_id=person1_.person_id where passport0_.passport_id=?