一对一关联关系的概述图如下所示:
1. 基于外键映射的1-1关联关系
对于基于外键映射的1-1关联关系,其外键可以放在任意一端,但不能两边都使用外键映射。
在需要存放外键的一端:增加many-to-one元素,并设置其unique属性为true来表示1-1关联,示例代码如下:
<!-- 配置多对一映射,并添加唯一性约束 -->
<many-to-one name="manager" class="Manager" column="MGR_ID" unique="true"></many-to-one>
在不需存放外键的一端:增加one-to-one元素,且必须使用property-ref属性指定使用被关联实体主键以外的字段作为关联字段,示例代码如下:
<!-- 配置一对一映射 -->
<!-- 若不配置property-ref属性,则在左外连接查询时,将使用被关联实体的主键作为关联字段 -->
<one-to-one name="department" class="Department" property-ref="manager"></one-to-one>
基于外键映射的1-1关联关系的数据库操作测试代码如下所示:
@Test
public void testGet1() {
// 4. 在查询没有外键的实体对象时,使用左外连接一并查询其关联的对象,并进行初始化
Manager mgr = (Manager) session.get(Manager.class, 5);
System.out.println(mgr.getName());
System.out.println(mgr.getDepartment().getName());
}
@Test
public void testGet() {
// 1. 默认情况下对关联属性使用懒加载,所以可能会出现懒加载异常的问题
Department dept = (Department) session.get(Department.class, 5);
System.out.println(dept.getName());
// 2. org.hibernate.LazyInitializationException
// session.close();
// System.out.println(dept.getManager().getName());
// 3. 查询Manager对象时左外连接条件有误,需在one-to-one中配置property-ref属性
System.out.println(dept.getManager().getName());
}
@Test
public void testSave() {
Department dept = new Department();
dept.setName("DEPT-AA");
Manager mgr = new Manager();
mgr.setName("MGR-AA");
// 设置1-1关联关系
dept.setManager(mgr);
mgr.setDepartment(dept);
// 注意:建议先保存没有外键列的对象,以减少UPDATE语句
session.save(dept);
session.save(mgr);
}
2. 基于主键映射的1-1关联关系
主要策略:指一端的主键生成器使用foreign策略,即根据”对方”的主键来生成自己的主键,本身不能自动生成主键。
在使用foreign主键生成器的一端:增加one-to-one元素,并设置其constrained属性为true,即当前持久化类对应数据库表的主键添加一个外键约束,示例代码如下:
<id name="id" column="MGR_ID" type="java.lang.Integer">
<!-- 使用外键的方式来生成当前主键 -->
<generator class="foreign">
<!-- property属性指定使用当前持久化类的哪一个属性的主键做外键 -->
<param name="property">department</param>
</generator>
</id>
<!-- 配置一对一映射:需要为其添加外键约束 -->
<one-to-one name="department" class="Department" constrained="true"></one-to-one>
在不用foreign主键生成器的一端:增加one-to-one元素映射关联属性即可,示例代码如下:
<!-- 配置一对一映射 -->
<one-to-one name="manager" class="Manager"></one-to-one>
基于主键映射的1-1关联关系的数据库操作测试代码如下所示:
package com.qiaobc.hibernate.one2one.primary;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class TestHibernate {
private SessionFactory sessionFactory; // 单实例的
private Session session; // 项目中不能声明为成员变量,可能会有并发问题
private Transaction transaction; // 项目中不能声明为成员变量,可能会有并发问题
@Before
public void init() {
// 创建Session对象并开启事务
Configuration configuration = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
session = sessionFactory.openSession();
transaction = session.beginTransaction();
}
@Test
public void testGet1() {
// 4. 在查询有主键约束的实体对象时,使用左外连接一并查询其关联的对象,并进行初始化
Department dept = (Department) session.get(Department.class, 1);
System.out.println(dept.getName());
System.out.println(dept.getManager().getName());
}
@Test
public void testGet() {
// 1. 默认情况下对关联属性使用懒加载,所以可能会出现懒加载异常的问题
Manager mgr = (Manager) session.get(Manager.class, 1);
System.out.println(mgr.getName());
// 2. org.hibernate.LazyInitializationException
// session.close();
// System.out.println(dept.getManager().getName());
// 3. 查询Department对象时左外连接
System.out.println(mgr.getDepartment().getName());
}
@Test
public void testSave() {
Department dept = new Department();
dept.setName("DEPT-AA");
Manager mgr = new Manager();
mgr.setName("MGR-AA");
// 设置1-1关联关系
dept.setManager(mgr);
mgr.setDepartment(dept);
// 注意:无论先插入哪一个对象都不会有多余的UPDATE
session.save(dept);
session.save(mgr);
}
@After
public void destory() {
transaction.commit();
session.close();
sessionFactory.close();
}
}