关于类继承与数据库表设计的问题

本文探讨了Java类继承设计及其与关系型数据库表之间的映射方式,介绍了三种主要的映射策略:tableperconcreteclass、tablepersubclass 和 tableperclasshierarchy,并详细解释了每种策略的实现方法。

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

现在主流的编程语言是面向对象的编程语言,而主流的数据库都是面向关系的数据库。对象与关系之间存在一定的不匹配性,比如说类存在继承,而数据库表却不存在这样子的做法,这篇文章是关于在进行Java类继承设计的时候,如何去设计相关的数据库表,以及如何配置Hibernate相关的关系映射文件的,所采用的Hibernate版本是3.x。在Hibernate中对于类继承问题提出的解决方案有3种:table per concrete class,table per subclass,table per class hierarchy。

table per concrete class

这种解决方案,就是每一个子类一张单独的表,并且把父类的属性也放到子类的表中去,从而就没有所谓的父类对应的表。如果有一个父类,两个子类,那么对应下来就是应该在数据库中创建两张表。举个例子:现在有一个父类商品Item,它现在派生出两个子类Book和DVD。Item的属性包括了id(Integer),name(String),price(Float),而子类Book新增属性pages(Integer),子类DVD新增属性zones(String)。那么在设计数据库时,就只需要创建两张表t_book和t_dvd,如下:
t_book
idnamepricepages

t_dvd
idnamepricezones

在这里,所使用的数据库是mysql,Java的Integer类型对应mysql的int类型,String对应mysql的varchar,Float对应mysql的float。
类的设计,表的设计都解决了,接下来就是要配置Hibernate关系映射文件,这种情况下不必去配置父类的关系映射文件,你有几个子类,就为几个子类配置关系映射文件就可以了。
Book.hbm.xml
<class name="com.daniel.model.persistence.Book" table="t_book">
	<id name="id" column="id" type="java.lang.Integer">
		<generator class="native"></generator>
	</id>
	<property name="name" column="name" type="java.lang.String"/>
	<property name="price" column="price" type="java.lang.Float"/>
	<property name="pages" column="pages" type="java.lang.Integer"/>
</class>
DVD.hbm.xml
<class name="com.daniel.model.persistence.dvd" table="t_dvd">
	<id name="id" column="id" type="java.lang.Integer">
		<generator class="native"></generator>
	</id>
	<property name="name" column="name" type="java.lang.String"/>
	<property name="price" column="price" type="java.lang.Float"/>
	<property name="zones" column="zones" type="java.lang.String"/>
</class>
如果你使用HQL查询父类,那么Hibernate会发出若干条查询语句去查询子类(具体取决于该父类有几个子类)

table per subclass

这种解决方案,是父类单独使用一张表,这张表记录着父类的属性,每一个子类又单独存在自己的一张表,如果有一个父类,两个子类,那么就存在三张表。此时,子类的表中要设置外键来记录其父类记录,也就是说,这种方案要比第一种方案多用一个列。还是上述的Item类,以及其两个派生类Book和DVD。数据库表的设计如下:
t_item
idnameprice

t_book
book_idpages

t_dvd
dvd_idzones

在这里,book_id,dvd_id既是主键又是外键,这样可以保证父类主键值遇子类主键值一致(虽然说实际上是两个列,但是值一样)。在配置关系映射文件时,只需要配置一个关系映射文件,那就是Item.hbm.xml
<class name="com.daniel.model.persistence.Item" table="t_item">
	<id name="id" column="id" type="java.lang.Integer">
		<generator class="native"></generator>
	</id>
	<property name="name" column="name" type="java.lang.String"/>
	<property name="price" column="price" type="java.lang.Float"/>
	
	<!--配置子类Book-->
	<joined-subclass name="com.daniel.model.persistence.Book" table="t_book">
		<key column="book_id">
		<propery name="pages" column="pages" type="java.lang.Integer"/>
	</joined-subclass>

	<!--配置子类DVD-->
	<joined-subclass name="com.daniel.model.persistence.DVD" table="t_dvd">
		<key column="dvd_id">
		<propery name="zones" column="zones" type="java.lang.String"/>
	</joined-subclass>
</class>
这种解决方案性能上不太被看好,原因是如果插入和删除一条记录,需要分别操作父类表和子类表各一次。即使是查询,也需要分别查询父类表和子类表。

table per class hierarchy

这种解决方案,是把所有的父类属性和子类属性都写到一张表中,然后为了区分不同子类,需要在表中加入一个额外的列。同样的Item父类,两个子类Book和DVD,数据库表设计如下:
t_item
idnamepricepageszonescatalog

其中catalog字段是用来区分不同子类的。
这种解决方案也是只需要配置一个关系映射文件,那就是Item.hbm.xml。
<class name="com.daniel.model.persistence.Item" table="t_item">
	<id name="id" column="id" type="java.lang.Integer">
		<generator class="native"></generator>
	</id>
	<property name="name" column="name" type="java.lang.String"/>
	<property name="price" column="price" type="java.lang.Float"/>
	<!--配置区分子类字段-->
	<discriminator column="catalog" type="string" />
	
	<!--配置子类Book-->
	<subclass name="com.daniel.model.persistence.Book" discriminator="1">
		<propery name="pages" column="pages" type="java.lang.Integer"/>
		
	</subclass>

	<!--配置子类DVD-->
	<joined-subclass name="com.daniel.model.persistence.DVD"  discriminator-value="2">
		<propery name="zones" column="zones" type="java.lang.String"/>
	</joined-subclass>
</class>
这种解决方案中可以能会造成表的空间利用率较低,但是速度确实是非常快,因为所有的记录都在一张表中,不过时间与空间总是两难全的,具体使用哪种方案,得看你是偏重哪方面。




评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值