Hibernate 继承关系映射

本文详细介绍了三种ORM映射策略:TPS(每子类一张表)、TPH(每个类层级一张表)和TPC(每个具体类一张表)。通过具体示例展示了不同策略下表结构的设计方法及Hibernate配置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

业务需求:

某网上影像店,专门经营图书和DVD。图书和DVD需要管理的信息非常类似,都包括产品编号、名称、生产厂家;

但是二者也存在差别,图书需要管理页数信息,而DVD需要管理区域代码信息。

分析该需求,得到两个对象,即图书和DVD,因此应该创建两个实体类,即Book类和DVD类。

由于Book类和DVD类存在相同的属性,因此应该抽象出一个父类,为Product产品类,Book和DVD类基于Product类进行扩展。


创建实体类,并分别扩展两个子类,Book和DVD

Product类

package vo;

public class Product {

	private Integer id;
	private String name;
	private String manufacturer;
	public Product() {
		super();
	}
	public Product(Integer id) {
		super();
		this.id = id;
	}
	public Product(Integer id, String name, String manufacturer) {
		super();
		this.id = id;
		this.name = name;
		this.manufacturer = manufacturer;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getManufacturer() {
		return manufacturer;
	}
	public void setManufacturer(String manufacturer) {
		this.manufacturer = manufacturer;
	}
	
}
Book类

package vo;

public class Book extends Product{

	private Integer pagecount;
	
	public Book() {
		// TODO Auto-generated constructor stub
	}

	public Book(Integer id, String name, String manufacturer,Integer pagecount) {
		super(id, name, manufacturer);
		this.pagecount=pagecount;
	}

	public Integer getPagecount() {
		return pagecount;
	}

	public void setPagecount(Integer pagecount) {
		this.pagecount = pagecount;
	}

	
}
DVD类

package vo;

public class DVD extends Product{

	private String regioncode;
	
	public DVD() {
		// TODO Auto-generated constructor stub
	}

	public DVD(Integer id, String name, String manufacturer,String regioncode) {
		super(id, name, manufacturer);
		this.regioncode=regioncode;
	}

	public String getRegioncode() {
		return regioncode;
	}

	public void setRegioncode(String regioncode) {
		this.regioncode = regioncode;
	}

}

1、TPS(Table Per SubClass)每个子类一张表

创建表结构

create table product(
	id int not null primary key,
	name varchar(50),
	manufacturer varchar(50)
);
create table book(
	id int not null primary key,
	pagecount int,
	foreign key(id) references product(id)
);
create table dvd(
	id int not null primary key,
	regioncode varchar(20),
	foreign key(id) references product(id)
)
上述的SQL语句将创建3张表,其中product是主表,存储所有产品的共有字段;

book和dvd是从表,通过主键参考product表,扩展了book和dvd的独有字段。


基于这样的设计,只需要创建一个与父类同名的hbm.xml文件即可,即Product.hbm.xml,子类采用<joined-subclass>属性进行配置。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="vo.Product" table="product" catalog="mldn">
		<id name="id" type="java.lang.Integer" >
			<column name="id" />
			<generator class="assigned"/>
		</id>
		<property name="name" type="java.lang.String">
			<column name="name" length ="50" />
		</property>
		<property name="manufacturer" type="java.lang.String">
			<column name="manufacturer" length="50"/>
		</property>
		<joined-subclass name="vo.Book" table="book" catalog="mldn">
		<key column="id"></key>
		<property name="pagecount" column ="pagecount"></property>
		</joined-subclass>
		<joined-subclass name="vo.DVD" table="dvd" catalog="mldn">
		<key column="id"></key>
		<property name="regioncode" column ="regioncode"></property>
		</joined-subclass>
	</class>
</hibernate-mapping>
测试类:

TestTPS

	public static void main(String[] args) {
		Book book = new Book(10,"SSH","KuangJia",300);
		DVD dvd = new DVD(20,"Java Web","coding","Asia");
		Session session = HibernateSessionFactory.getSession();
		Transaction tran = session.beginTransaction();
		session.save(book);
		session.save(dvd);
		tran.commit();
	}
执行成功,插入数据,显示结果:

使用如下方式测试:

	public static void main(String[] args) {
		Session session = HibernateSessionFactory.getSession();
		String hql="from Book";
		List<Book> list = session.createQuery(hql).list();
		for(Book b:list){
			System.out.println(b.getId()+" "+b.getName()+" "+b.getManufacturer()+" "+b.getPagecount());
		}
	}

上述代码中,使用from Book 语句查询Book实例,因为Book作为Product的<joined-subclass>存在,

所以运行上述代码,将连接查询product和book表,得到Book实例的所有属性。


2、TPH(Table Per Class Hierarchy)每个类分层结构一张表

创建表:

create table product(
	id int not null primary key,
	category varchar(10),
	name varchar(50),
	manufacture varchar(50),
	pagecount int,
	regioncode varchar(20)
);
上述SQL将之创建一张表product,将book和dvd记录存储到一张表中,使用category字段作为区分值字段,区别book和dvd。

修改配置文件Product.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="vo.Product" table="product" catalog="mldn">
		<id name="id" type="java.lang.Integer" >
			<column name="id" />
			<generator class="assigned"/>
		</id>
		<discriminator column="category" type="java.lang.String"></discriminator>
		<property name="name" type="java.lang.String">
			<column name="name" length ="50" />
		</property>
		<property name="manufacturer" type="java.lang.String">
			<column name="manufacturer" length="50"/>
		</property>
		<subclass name="vo.Book"  discriminator-value="1">
		<property name="pagecount" type="java.lang.Integer">
			<column name="pagecount"/>
		</property>
		</subclass>
		<subclass name="vo.DVD"  discriminator-value="2">
		<property name="regioncode" type="java.lang.String">
			<column name="regioncode"/>
		</property>
		</subclass>
	</class>
</hibernate-mapping>

上述hbm.xml 文件中的关键点是<discriminator>和<subclass>,

使用<discriminator column="category" type="java.lang.String"></discriminator>将category字段指定为区分值字段,

进一步使用<subclass name="vo.Book"  discriminator-value="1">配置了子类Book的区分值为1,

并在<subclass>标签体中配置了子类扩展的属性pagecount的映射关系。

测试类:

TestTPH.java

	public static void main(String[] args) {
		Book book = new Book(10,"SSH","KuangJia",300);
		DVD dvd = new DVD(20,"Java Web","coding","Asia");
		Session session = HibernateSessionFactory.getSession();
		Transaction tran = session.beginTransaction();
		session.save(book);
		session.save(dvd);
		tran.commit();
	}
运行结果:


使用如下代码进行测试:

	public static void main(String[] args) {
		Session session = HibernateSessionFactory.getSession();
		String hql="from Book";
		List<Book> list = session.createQuery(hql).list();
		for(Book b:list){
			System.out.println(b.getId()+" "+b.getName()+" "+b.getManufacturer()+" "+b.getPagecount());
		}
	}

上述代码中,使用from Book语句查询所有的Book实例,由于Book类的区分值是1,所有Hibernate将查询product表中category字段值为1的所有记录,并封装成Book实例返回

显示结果:


3、TPC(Table Per Concrete Class)每个具体类一个表

创建表:

create table book(
<span style="white-space:pre">	</span>id int not null primary key,
<span style="white-space:pre">	</span>name varchar(50),
<span style="white-space:pre">	</span>manufacturer varchar(50),
<span style="white-space:pre">	</span>pagecount int
);
create table dvd(
<span style="white-space:pre">	</span>id int not null primary key,
<span style="white-space:pre">	</span>name varchar(50),
<span style="white-space:pre">	</span>manufacturer varchar(50),
<span style="white-space:pre">	</span>regioncode varchar(30)
)
上述SQL语句,将创建两张毫无关系的表book和dvd,分别存储book和dvd的记录。

然而,实体类层次上依然应该采用继承关系,因为book和dvd中存在相同的字段id、name、manufacturer。

基于这样的设计,只提供与父类Product对应的hbm.xml文件即可,即Product.hbm.xml。

该文件中将Product类使用abstract=true设置为抽象的,因为Product类没有对应的表与其映射。

进一步使用<union-subclass>将子类扩展的属性进行配置。


Product.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="vo.Product" table="product"  abstract ="true" catalog="mldn">
		<id name="id" type="java.lang.Integer" >
			<column name="id" />
			<generator class="assigned"/>
		</id>
		<property name="name" type="java.lang.String">
			<column name="name" length ="50" />
		</property>
		<property name="manufacturer" type="java.lang.String">
			<column name="manufacturer" length="50"/>
		</property>
		<union-subclass name="vo.Book" table="book">
			<property name="pagecount" type="java.lang.Integer">
				<column name="pagecount"></column>
			</property>
		</union-subclass>
		<union-subclass name="vo.DVD" table="dvd">
			<property name="regioncode" type="java.lang.String">
				<column name="regioncode" length="20"></column>
			</property>
		</union-subclass>
	</class>
</hibernate-mapping>
上述hbm.xml文件中的关键点是<union-subclass>元素的配置,

使用name配置了子类的名称,使用table配置了子类映射的表,并通过<property>标签配置了子类中扩展属性的映射关系。


测试类

TestTPC.java

	public static void main(String[] args) {
		Book book = new Book(10,"SSH","KuangJia",300);
		DVD dvd = new DVD(20,"Java Web","coding","Asia");
		Session session = HibernateSessionFactory.getSession();
		Transaction tran = session.beginTransaction();
		session.save(book);
		session.save(dvd);
		tran.commit();
	}
显示结果:


使用如下代码测试:

	public static void main(String[] args) {
		Session session = HibernateSessionFactory.getSession();
		String hql="from Book";
		List<Book> list = session.createQuery(hql).list();
		for(Book b:list){
			System.out.println(b.getId()+" "+b.getName()+" "+b.getManufacturer()+" "+b.getPagecount());
		}
	}
运行上述代码,Hibernate将从book表中查询所有记录,封装为Book实例返回,

结果如下:




多态查询

HQL中的查询语句支持多态查询,如下:

from Product;
上述语句不仅能返回Product的实例,还将返回Product子类的实例。

使用TPC的实例测试多态查询的使用,代码如下:

	public static void main(String[] args) {
		Session session = HibernateSessionFactory.getSession();
		String hql = "from Product";
		List<Product> list = session.createQuery(hql).list();
		for(Product p:list){
			if(p instanceof Book){
				Book b = (Book)p;
				System.out.println(b.getId()+" "+b.getName()+" "+b.getManufacturer()+" "+b.getPagecount());
			}
			if(p instanceof DVD){
				DVD d= (DVD)p;
				System.out.println(d.getId()+" "+d.getName()+" "+d.getManufacturer()+" "+d.getRegioncode());
			}
		}
	}
上述代码中,使用from Product进行查询,将返回Product类所有子类的实例,也就是讲查询到Book和DVD的实例,即为多态查询。

执行后,显示结果:











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值