Hibernate4-8 映射继承关系

本文介绍了Hibernate支持的三种继承映射策略:subclass、joined-subclass和union-subclass,并通过示例详细阐述了每种策略的特点及适用场景。

  对于面向对象的程序设计语言而言,继承和多态是两个最基本的概念。而Hibernate的继承映射可以理解持久化类之间的继承关系,其支持subclass、joined-subclass和union-subclass共三种继承映射策略。


1. 采用subclass元素的继承映射

  主要特点:
  (1). 对于继承关系中的父类和子类使用同一张数据表;
  (2). 需要在该数据表中添加辨别者列以区分记录所属哪个类的实例;
  (3). 所有子类定义的字段均不能添加非空约束。
  对象关系映射文件示例如下:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2017-2-23 21:43:52 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping package="com.qiaobc.hibernate.subclass">

    <class name="Person" table="PERSONS" discriminator-value="PERSON">

        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>

        <!-- 配置辨别者列 -->
        <discriminator column="TYPE" type="string"></discriminator>

        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>

        <property name="age" type="java.lang.Integer">
            <column name="AGE" />
        </property>

        <!-- 配置继承映射 -->
        <subclass name="Student" discriminator-value="STUDENT">
            <property name="school" column="SCHOOL" type="string"></property>
        </subclass>

    </class>

</hibernate-mapping>

  采用subclass元素实现继承映射的单元测试代码见附录1所示。


2. 采用joined-subclass元素的继承映射

  主要特点:
  (1). 对于继承关系中的父类和子类各使用一张数据表;
  (2). 父类实例保存在父类表中,子类实例由父类表和子类表共同存储;
  (3). 无须使用辩别者列,但需要为每个子类使用key元素映射共有主键;
  (4). 子类的独有属性可以添加非空约束,因为子类独有属性和父类属性没有保存在一张数据表中。
  对象关系映射文件核心代码示例如下:

<!-- 配置继承映射 -->
<joined-subclass name="Student" table="STUDENTS">
    <key column="STUDENT_ID"></key>
    <property name="school" column="SCHOOL" type="string"></property>
</joined-subclass>

  采用joined-subclass元素实现继承映射的单元测试代码见附录2所示。


3. 采用union-subclass元素的继承映射

  主要特点:
  (1). 将每一个实体对象映射到一张独立的数据表中;
  (2). 子类实例的数据仅保存在子类表中,子类的独有属性可以添加非空约束;
  (3). 既不需要使用辩别者列,也无须使用key元素来映射共有主键;
  (4). 不可使用identity的主键生成策略,因为同一类继承层次中所有实体类都需要使用同一个主键种子,即多个持久化实体对应记录的主键应该是连续的。
  对象关系映射文件核心代码示例如下:

<!-- 配置继承映射 -->
<union-subclass name="Student" table="STUDENTS">
    <property name="school" column="SCHOOL" type="string"></property>
</union-subclass>

  采用union-subclass元素实现继承映射的单元测试代码见附录3所示。


4. 三种继承映射方式对比

这里写图片描述


附录:单元测试代码

附1 采用subclass元素的继承映射:
// TestHibernate.java
package com.qiaobc.hibernate.subclass;

import java.util.List;

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();
    }

    /**
     * 缺点:
     * 1. 使用了辨别者列;
     * 2. 子类独有的字段不能添加非空约束;
     * 3. 若继承层次较深,则数据表的字段也会较多。
     */

    /**
     * 查询操作
     * 1. 查询父类记录,只需要查询一张数据表;
     * 2. 查询子类记录,也只需要查询一张数据表。
     */
    @Test
    public void testQuery() {
        List<Person> persons = session.createQuery("FROM Person").list();
        System.out.println(persons.size());

        List<Student> students = session.createQuery("FROM Student").list();
        System.out.println(students.size());
    }

    /**
     * 插入操作
     * 1. 对于子类对象只需将记录插入到一张数据表中;
     * 2. 辨别者列由Hibernate自动维护。
     */
    @Test
    public void testSave() {
        Person person = new Person();
        person.setName("qiaobc");
        person.setAge(23);

        Student student = new Student();
        student.setName("qiaob");
        student.setAge(22);
        student.setSchool("WHUT");

        session.save(person);
        session.save(student);
    }

    @After
    public void destory() {
        transaction.commit();
        session.close();
        sessionFactory.close();
    }
}
附2 采用joined-subclass元素的继承映射:
// TestHibernate.java
package com.qiaobc.hibernate.joined.subclass;

import java.util.List;

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();
    }

    /**
     * 优点:
     * 1. 不需要使用辨别者列;
     * 2. 子类独有的字段能添加非空约束;
     * 3. 没有冗余的字段。
     */

    /**
     * 查询操作:需要查询多张表
     * 1. 查询父类记录,做一个左外连接查询;
     * 2. 查询子类记录,做一个内连接查询。
     */
    @Test
    public void testQuery() {
        List<Person> persons = session.createQuery("FROM Person").list();
        System.out.println(persons.size());

        List<Student> students = session.createQuery("FROM Student").list();
        System.out.println(students.size());
    }

    /**
     * 插入操作
     * 1. 对于子类对象至少需要插入到两张数据表中;
     */
    @Test
    public void testSave() {
        Person person = new Person();
        person.setName("qiaobc");
        person.setAge(23);

        Student student = new Student();
        student.setName("qiaob");
        student.setAge(22);
        student.setSchool("WHUT");

        session.save(person);
        session.save(student);
    }

    @After
    public void destory() {
        transaction.commit();
        session.close();
        sessionFactory.close();
    }
}
附3 采用union-subclass元素的继承映射:
// TestHibernate.java
package com.qiaobc.hibernate.union.subclass;

import java.util.List;

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();
    }

    /**
     * 优点:
     * 1. 不需要使用辨别者列;
     * 2. 子类独有的字段能添加非空约束;
     * 
     * 缺点:
     * 1. 存在冗余的字段;
     * 2. 若更新父表的字段,则更新效率较低。
     */

    @Test
    public void testUpdate() {
        String hql = "UPDATE Person p SET p.age = 20";
        session.createQuery(hql).executeUpdate();
    }

    /**
     * 查询操作:需要查询多张表
     * 1. 查询父类记录,需把父表和字表记录汇总到一起再做查询,性能稍差;
     * 2. 查询子类记录,性能较好。
     */
    @Test
    public void testQuery() {
        List<Person> persons = session.createQuery("FROM Person").list();
        System.out.println(persons.size());

        List<Student> students = session.createQuery("FROM Student").list();
        System.out.println(students.size());
    }

    /**
     * 插入操作:性能不错
     */
    @Test
    public void testSave() {
        Person person = new Person();
        person.setName("qiaobc");
        person.setAge(23);

        Student student = new Student();
        student.setName("qiaob");
        student.setAge(22);
        student.setSchool("WHUT");

        session.save(person);
        session.save(student);
    }

    @After
    public void destory() {
        transaction.commit();
        session.close();
        sessionFactory.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值