8.2.2 一对一(one-to-one)
基于外键的单向一对一(one-to-one)关联与单向多对一关联几乎是一样的。唯一的不同是单向一对一关联中外键字段具有 unique 约束。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
-----------------------------------
自己具体说明如下,仍然使用上一个例子,本例中一个人只能对应一个地址。
Address类、Person类、Address.hbm.xml文件与上一例子中全部相同,只需要如下修改Person.hbm.xml文件:
<hibernate-mapping package="com.hibernate.domain">
<class name="Person" table="person">
<id name="id" column="personid" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="personName" type="java.lang.String">
<column name="personName" length="64" not-null="true"/>
</property>
<!-- 配置many-to-one属性 -->
<!-- 其中name为Person类中“private Address address”中的address属性 -->
<!-- 这里需要增加unique约束 (这里是重点)-->
<many-to-one name="address" column="addressId" unique="true" not-null="true"/>
</class>
</hibernate-mapping>
测试结果如下:
//从数据中取出数据
Person person = (Person) session.load(Person.class, 2);
//向数据库中添加数据
Address add = new Address();
add.setAddressName("beijing");
Person p1 = new Person();
p1.setPersonName("Hibernate01");
p1.setAddress(add);
Person p2 = new Person();
p2.setPersonName("hibernate02");
p2.setAddress(add);
session.save(add);
session.save(p1);
session.save(p2);
如果还像上例这样添加数据的话,就会报错:Duplicate entry '1' for key 'UK_ggbkwpl67fgfv0qtwb6cfque8' ,因为现在一个Person只能对应唯一一个Address。
此时两个表已经建立好了,只不过两张表都为空,没有数据,数据已经回滚。
正确的添加数据的情况如下:
//向数据库中添加数据
Address add1 = new Address();
add1.setAddressName("beijing");
Address add2 = new Address();
add2.setAddressName("天津");
Person p1 = new Person();
p1.setPersonName("Hibernate01");
p1.setAddress(add1);
Person p2 = new Person();
p2.setPersonName("hibernate02");
p2.setAddress(add2);
session.save(add1);
session.save(add2);
session.save(p1);
session.save(p2);
运行成功后,通过查询数据,结果如下:
Person和Address均正确,且一一对应。查询的话此处不再赘述。
----------------------------------------------------------
基于主键的单向一对一关联通常使用一个特定的id generator。然而,在这个例子中,我们翻转了关联的方向。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<one-to-one name="person" constrained="true"/>
</class>
create table Person ( personId bigint not null primary key )
create table Address ( personId bigint not null primary key )
--------------------------------------------
自己的实践案例如下:
Person类:
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String personName;
public Person() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPersonName() {
return personName;
}
public void setPersonName(String personName) {
this.personName = personName;
}
}
Address类:
public class Address implements Serializable{
private static final long serialVersionUID = 1L;
private Integer id;
private String addressName;
private Person person; //由于Address的主键要通过Person来映射,因此Address类必须加上该属性(这点重要)
public Address() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAddressName() {
return addressName;
}
public void setAddressName(String addressName) {
this.addressName = addressName;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
Person.hbm.xml文件(该文件正常配置即可):
<hibernate-mapping package="com.hibernate.domain">
<class name="Person" table="person">
<id name="id" column="personid" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="personName" type="java.lang.String">
<column name="personName" length="64" not-null="true"/>
</property>
</class>
</hibernate-mapping>
<hibernate-mapping package="com.hibernate.domain">
<class name="Address" table="address">
<id name="id" column="addressId" type="java.lang.Integer">
<!-- 因为是基于逐渐的one-to-one,所以我们使用外键策略(重要) -->
<generator class="foreign">
<!-- 这里的值,是指定要跟哪个属性one-to-one (重要)-->
<!-- 具体到该例,是与 private Person person属性对应 -->
<param name="property">person</param>
</generator>
</id>
<property name="addressName" type="java.lang.String">
<column name="addressName" length="128" not-null="true"/>
</property>
<!-- 这里配置Address和person属性是一对一的关系(重要) -->
<one-to-one name="person" constrained="true"/>
</class>
</hibernate-mapping>
进行代码测试如下:
<span style="white-space:pre"> </span>Person p1 = new Person();
p1.setPersonName("Hibernate01");
Address add = new Address();
add.setAddressName("beijing");
add.setPerson(p1); //需要放入Person
session.save(p1);
session.save(add);
通过 show create table address,可以看到 address的主键同时也是外键。
Address添加成功,Person添加成功,两者的id也相同。