Hibernate复杂的关系映射包含组成关系与继承关系。
组成关系
一个表对应一个类维护,当表中出现复杂结构重复结果,就可以将类进行拆分,抽取出单独组件 (component),有多个类来维护一张表
例:一个人存在家庭地址和工作地址。
public class Person {
privateInteger id;
privateString name;
//之前需要记录家庭地址的省和市
//private String homeProvince;
//private String homeCity;
将家庭地址抽取为一个单独组件 Address
privateAddress homeAddress;
//之前需要记录工作地址的省和市
//private String workProvince;
//private String workCity;
将家工作地址抽取为一个单独组件 Address
privateAddress workAddress;
}
地址类,这个类是依存person类的,这两个类是依存一张表的
public class Address { // 是Person 一个组成部分
privateString province;
privateString city;
}
配置文件只需要写person类就可以了,因为address不是一个实体类,没有单独的表。
配置 Person.hbm.xml
<hibernate-mapping>
<class name="cn.itcast.domain.component.Person" table="person" catalog="hibernate3day3">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"></property>
家庭地址的组件配置
<!-- name 就是Person 类的组件属性的名称
class 组件类型为address类 -->
<component name="homeAddress" class="cn.itcast.domain.component.Address">
<!-- parent 是组件类中所对应实体类的属性名称(谁的组件)-->
<parent name="person"/>
<property name="province" column="home_province"></property>
<property name="city" column="home_city"></property>
</component>
工作地址的组件配置
<component name="workAddress" class="cn.itcast.domain.component.Address">
<parent name="person"/>
<property name="province" column="work_province"></property>
<property name="city" column="work_city"></property>
</component>
</class>
</hibernate-mapping>
在hibernate.cfg.xml中进行映射
常见操作就是单表操作
1) 测试插入
public void testInsert() {
Session session = HibernateUtil.openSession();
Transaction transaction = session.beginTransaction();
//设置人
Person person = new Person();
person.setName("小明");
//设置家庭地址
Address homeAddress = new Address();
homeAddress.setProvince("辽宁");
homeAddress.setCity("沈阳");
person.setHomeAddress(homeAddress);
//设置家庭地址
Address workAddress = new Address();
workAddress.setProvince("河北");
workAddress.setCity("秦皇岛");
person.setWorkAddress(workAddress);
//保存person,与单表一样
session.save(person);
transaction.commit();
session.close();
}
2) 测试查询
Person person = (Person) session.get(Person.class, 1);
System.out.println(person);
3) 测试修改 (注意 空指针问题 )
public void testUpdate2() {
Session session = HibernateUtil.openSession();
Transaction transaction = session.beginTransaction();
// 将二号数据 工作城市 修改为 石家庄(但是二号数据之前就没有工作地址)
Person person = (Person) session.get(Person.class, 2);
// 可能出现 工作地址为空 , workAddress 为 null,需要添加判断
if (person.getWorkAddress() == null) {
Address workAddress = new Address();
person.setWorkAddress(workAddress);
}
person.getWorkAddress().setCity("石家庄");
transaction.commit();
session.close();
}
3) 测试删除
//删除持久对象与托管对象都行,这里就演示删除托管的对象
Person person = new Person();
person.setId(2);
session.delete(person);
Hibernate 把持久化类的属性分为两种:
值(value)类型: 没有 OID, 不能被单独持久化, 生命周期依赖于所属的持久化类的对象的生命周期,组 件类型就是一种值类型 (简单说就是属性只是目标表一部分数据 (组件映射例如 Ad dress是 Person一个组成部分))
实体(entity)类型: 有 OID, 可以被单独持久化, 有独立的生命周期(简单说就是属性是存在一个单独表 映射(一对多 多对多 ,例如 Order类 关联Customer 类,customer是独立表 ))
继承关系
hibernate 描述类之间继承关系,有三种描述方式
第一种 父类和子类使用同一张表,在表中增加一列区分字段,区分数据是父类数据还是子类数据
第二种 父类和每个子类都对应单独一张数据表,将公共属性数据放入父类表,将子类个性属性存放到子类表,子类表通过外键引用父类表主键
第三种 父类和每个子类都对应单独一张数据表,表与表之间是无关的,父类表存放父类数据,子类表存放子类数据
1、使用 subclass方式进行继承映射(父类数据和子类数据在同一张数据表,引入辨别者列 )
案例:员工分为钟点工与正式工,都继承员工类
配置 Employee.hbm.xml
<hibernate-mapping>
父类的辨别者列放到class上
<class name="cn.itcast.domain.subclass.Employee" table="employee" discriminator-value="ee">
<id name="id">
<generator class="native"></generator>
</id>
<!-- 引入辨别者列 discriminator 列明可以任意指定-->
<discriminator column="etype"></discriminator>辨别者列必须写在属性之前
<property name="name"></property>
<!-- 配置子类,一个subclass代表一个子类-->
<subclass name="cn.itcast.domain.subclass.HourEmployee" discriminator-value="he">
<property name="rate"></property>
</subclass>
<subclass name="cn.itcast.domain.subclass.SalaryEmployee" discriminator-value="se">
<property name="salary"></property>
</subclass>
</class>
</hibernate-mapping>
继承映射的编程和单表类似
1) 测试保存
// 保存员工
Employee employee = new Employee();
employee.setName("张三");
session.save(employee);
// 保存钟点工
HourEmployee employee2 = new HourEmployee();
employee2.setName("李四");
employee2.setRate(10d);
session.save(employee2);
2) 测试查询
查询所有钟点工(from HourEmployee),hibernate框架会根据辨别者列where etype='he'来查询。
//查询子类对象时,hibernate 生成SQL ,根据区分者 列值 进行查询
Query query = session.createQuery("from HourEmployee");
List<HourEmployee> employees = query.list();
2、使用joinedsubclass方式 进行继承映射(通用数据在父类表,个性数据在子类表,子类表主键通过外键方式 引用父类表的主键,无需区分者列)
配置 Employee.hbm.xml
<hibernate-mapping>
<class name="cn.itcast.domain.joinedsubclass.Employee" table="employee" >
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"></property>
<!-- 配置子类,使用 joined-subclass 指定表名 -->
<joined-subclass name="cn.itcast.domain.joinedsubclass.HourEmployee" table="houremployee">
<!-- 子类表主键就是eid ,本身也是一个外键,引用 employee表主键 -->
<key column="eid"></key>
<property name="rate"></property>
</joined-subclass>
<joined-subclass name="cn.itcast.domain.joinedsubclass.SalaryEmployee" table="salaryemployee">
<key column="eid"></key>
<property name="salary"></property>
</joined-subclass>
</class>
</hibernate-mapping>
3、 使用union-subclass 方式进行继承映射(父类和子类数据 分别在各自数据表,数据表直接没有关系,hibernate负责表之间主键连续自增)几乎不用
保证多表键主键连续自增 hibernate 通过 increment 策略
配置Employee.hbm.xml
<hibernate-mapping>
<class name="cn.itcast.domain.unionsubclass.Employee" table="employee" >
<id name="id">
<!-- 保证主键在父子表之间 连续自增 -->
<generator class="increment"></generator>
</id>
<property name="name"></property>
<!-- 配置子类 使用 union-subclass -->
<union-subclass name="cn.itcast.domain.unionsubclass.HourEmployee" table="houremployee">
<property name="rate"></property>
</union-subclass>
<union-subclass name="cn.itcast.domain.unionsubclass.SalaryEmployee" table="salaryemployee">
<property name="salary"></property>
</union-subclass>
</class>
</hibernate-mapping>
三种方式的建议
首先推荐joninedsubclass 方式(数据库没有冗余),如果子类数据不是很复杂情况,也可以使用s ubclass方式 ,union-subclass 在开发中 基本不用