基于外键的一对一映射关系(双方互相持有对方的引用)
- 对于基于外键的1-1关联,其外键可以存放在任意一边,在需要存放外键一端,增加many-to-one元素。为many-to-one元素增加unique=“true” 属性来表示为1-1关联
<!-- 使用many-to-one的方式来映射1-1关联关系,重点是unique属性要设置为true -->
<many-to-one name="manager" class="Manager" column="MGR_ID" unique="true">
</many-to-one>
- 另一端需要使用one-to-one元素,该元素使用 property-ref 属性指定使用被关联实体主键以外的字段作为关联字段
<!-- 映射一对一关系:另外的一已经有了外键,这个一就使用 one-to-one 进行映射
property-ref="manager"表示指定使用被关联实体主键以外的字段作为关联字段(一般是外键字段)
-->
<one-to-one name="dept" class="Dept" property-ref="manager"></one-to-one>
- 完整代码如下:
Dept.java
public class Dept {
private Integer id;
private String name;
private Manager manager;
//getter,setter略
}
Manager.java
public class Manager {
private Integer id;
private String name;
private Dept dept;
//getter,setter略
}
Dept.hbm.xml
<hibernate-mapping package="com.zc.cris.onetonoe.foreign">
<class name="Dept" table="DEPTS">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<!-- 使用many-to-one的方式来映射1-1关联关系,重点是unique属性要设置为true -->
<many-to-one name="manager" class="Manager" column="MGR_ID" unique="true">
</many-to-one>
</class>
</hibernate-mapping>
Manager.hbm.xml
<hibernate-mapping package="com.zc.cris.onetonoe.foreign">
<class name="Manager" table="MANAGERS">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<!--
映射一对一关系:另外的一已经有了外键,这个一就使用 one-to-one 进行映射
property-ref="manager"表示指定使用被关联实体主键以外的字段作为关联字段(一般是外键字段)
-->
<one-to-one name="dept" class="Dept" property-ref="manager"></one-to-one>
</class>
</hibernate-mapping>
核心配置文件略
TestHibernate2.java
class TestHibernate2 {
private SessionFactory sessionFactory = null;
private Session session = null;
private Transaction transaction = null;
//init和destroy方法略
@Test
void testGet() {
//默认情况下查询有外键一端同样会出现懒加载
// Dept dept = this.session.get(Dept.class, 1);
// System.out.println(dept.getName());
// this.session.close();
//
//默认情况下查询没有外键一端会把对应的另外一端查询出来,因为对于hibernate来说,它并不知道
//这个没有外键的表的对应关联关系,所以会把关联的数据一并查询出来
// select
// manager0_.ID as ID1_1_0_,
// manager0_.NAME as NAME2_1_0_,
// dept1_.ID as ID1_0_1_,
// dept1_.NAME as NAME2_0_1_,
// dept1_.MGR_ID as MGR_ID3_0_1_
// from
// MANAGERS manager0_
// left outer join
// DEPTS dept1_
// on manager0_.ID=dept1_.ID
// where
// manager0_.ID=?
//仔细观察,以上sql语句是有问题的,左外连接的条件应该是:manager.id = dept.manager_id
//而不应该是manager.id = dept.id
//所以需要在Manager对应的映射文件中的one-to-one节点设置属性property-ref="manager"
Manager manager = this.session.get(Manager.class, 1);
System.out.println(manager.getName());
// System.out.println(manager.getDept().getName());
}
@Test
void testSave() {
Manager manager = new Manager();
manager.setName("aa");
Dept dept = new Dept();
dept.setName("bb");
manager.setDept(dept);
dept.setManager(manager);
//建议先保存没有外键的一方,防止发送多余的update语句
this.session.save(manager);
this.session.save(dept);
}
为什么不使用两边都使用外键的一对一映射?
如果是两个对象都使用外键来映射一对一关系,是有可能出现逻辑上的问题(类似于两男两女,男A喜欢的对象是女A,但是女A喜欢的对象却不一定是男A,有可能是男B,无法正确显示一一对应的关系)
基于主键的一对一映射
和基于外键的一对一映射差不多,无非就是将某个一的主键设置为另外一个一的主键,并为其添加外键约束即可
代码:
Dept.hbm.xml
<hibernate-mapping package="com.zc.cris.onetonoe.primary">
<class name="Dept" table="DEPTS">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<!-- 表示当前一端的主键依赖于manager属性的主键 -->
<generator class="foreign" >
<param name="property">manager</param>
</generator>
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<!-- one-to-one的方式来映射1-1关联关系,需要为当前主键设置外键约束 -->
<one-to-one name="manager" class="Manager" constrained="true"></one-to-one>
</class>
</hibernate-mapping>
Manager.hbm.xml
<hibernate-mapping package="com.zc.cris.onetonoe.primary">
<class name="Manager" table="MANAGERS">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<!--
映射一对一关系:另外的一的主键引用当前一的主键作为外键
-->
<one-to-one name="dept" class="Dept"></one-to-one>
</class>
</hibernate-mapping>
TestHibernate2.java
class TestHibernate2 {
private SessionFactory sessionFactory = null;
private Session session = null;
private Transaction transaction = null;
//init和destroy方法略
@Test
void testGet() {
//默认情况下查询有外键一端同样会出现懒加载
// Dept dept = this.session.get(Dept.class, 1);
// System.out.println(dept.getName());
// this.session.close();
//
//默认情况下查询没有外键一端会把对应的另外一端查询出来,因为对于hibernate来说,它并不知道
//这个没有外键的表的对应关联关系,所以会把关联的数据一并查询出来
// select
// manager0_.ID as ID1_1_0_,
// manager0_.NAME as NAME2_1_0_,
// dept1_.ID as ID1_0_1_,
// dept1_.NAME as NAME2_0_1_,
// dept1_.MGR_ID as MGR_ID3_0_1_
// from
// MANAGERS manager0_
// left outer join
// DEPTS dept1_
// on manager0_.ID=dept1_.ID
// where
// manager0_.ID=?
Manager manager = this.session.get(Manager.class, 1);
System.out.println(manager.getName());
// System.out.println(manager.getDept().getName());
}
@Test
void testSave() {
Manager manager = new Manager();
manager.setName("aa");
Dept dept = new Dept();
dept.setName("bb");
manager.setDept(dept);
dept.setManager(manager);
//保存顺序无所谓,因为一定会先插入被依赖的一端的数据,即使先保存dept,也会先执行manager的insert语句
this.session.save(manager);
this.session.save(dept);
}
}