继承是关系型数据与面向对象数据结构之间的主要差异之一,如何在关系型的数据之上可以像面向对象数据那样清晰的表达之间的逻辑关系;Hiberante中支持3种类型的继承形式:
在面向对象的设计中会有继承关系,在hibernate可以这么来实现:比如,一个电子商务网站会出售DVD和Book,它们的共性就是会有名字和厂商,而不同之处在于DVD会有区域码,而book会有作者;那么在面向对象的设计中,我们会想将共性构造一个基类,然后在对基类进行扩展如:
父类TItem:
public class TItem implements java.io.Serializable{ private Integer id; private String name; private String company; get()/set() }
子类TBook:
public class TBook extends TItem { private String author; get()/set() }
子类TDvd:
public class TDvd extends TItem { private String regionCode; get()/set() }
在*.hbm.xml中直接这么定义:
TBook.hbm.xml:
<hibernate-mapping package="com.keith.tablePerConcreteClass"> <class name="TBook" table="TBook"> <id name="id"> <generator class="native"/> </id> <property name="name" /> <property name="company" /> <property name="author" /> </class> </hibernate-mapping>
TDvd.hbm.xml:
<hibernate-mapping package="com.keith.tablePerConcreteClass"> <class name="TDvd" table="TDvd"> <id name="id"> <generator class="native"/> </id> <property name="name" /> <property name="company" /> <property name="regionCode" /> </class> </hibernate-mapping>
测试下:添加数据:
Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); TBook book = new TBook(); book.setName("java Code"); book.setAuthor("king"); book.setCompany("EN"); TDvd dvd = new TDvd(); dvd.setName("2012"); dvd.setRegionCode("0404040404"); dvd.setCompany("ZH"); session.save(book); session.save(dvd); session.getTransaction().commit();
日志中这样输出:
create table TBook ( id integer not null auto_increment, name varchar(255), company varchar(255), author varchar(255), primary key (id) )
create table TDvd ( id integer not null auto_increment, name varchar(255), company varchar(255), regionCode varchar(255), primary key (id) )
Hibernate: insert into TBook(name, company, author) values (?, ?, ?)
Hibernate:insert into TDvd (name, company, regionCode) values (?, ?, ?)
党我们查询时:
Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); List list =session.createQuery("from com.keith.tablePerConcreteClass.TItem").list(); Iterator it = list.iterator(); while (it.hasNext()) { TItem item = (TItem) it.next(); System.out.println(item.getName()); } session.getTransaction().commit();
看下输出的日志:
Hibernate: select tbook0_.id as id0_, tbook0_.name as name0_, tbook0_.company as company0_, tbook0_.author as author0_ from TBook tbook0_ Hibernate: select tdvd0_.id as id1_, tdvd0_.name as name1_, tdvd0_.company as company1_, tdvd0_.regionCode as regionCode1_ from TDvd tdvd0_
查询了2个表的内容,结果是:
java Code 2012
通过这样的方式我们将继承关系在持久层得到体现,可是如果我们要根据name类查询某件商品,这是会将所有的表全部查询一遍,然后进行汇总,如果这样的,性能大大的降低!
如果父类表发生变化,那么其子类就得跟着变化,这样太不灵活,我们可以将父类表(TItem)单独映射到一个主表中,而为TBook,TDvd分别单独设立一张子表,子表中包含所扩展的属性,同时子表和父表可以通过关系型数据库的外键想关联;
对象没有变化,还是上面那么种写法,既然父类是一个主表了,子类是扩展属性的那么我们就根据外键来执行相应的操作;
这里只需要一个TItem.hbm.xml:
<hibernate-mapping package="com.keith.tablePerSubclass"> <class name="TItem" table="TItem"> <id name="id" type="java.lang.Integer" column="TItem_id"> <generator class="native"/> </id> <property name="name" /> <property name="company" /> <joined-subclass name="TBook" table="tBook"> <key column="TItem_id"></key> <property name="author" /> </joined-subclass> <joined-subclass name="TDvd" table="tDvd"> <key column="TItem_id"></key> <property name="regionCode" /> </joined-subclass> </class> </hibernate-mapping>
添加数据也后我们看下日志信息:首先会创建一个主表:
create table TItem ( TItem_id integer not null auto_increment, name varchar(255), company varchar(255), primary key (TItem_id) )
tBook:
create table tBook ( TItem_id integer not null, author varchar(255), primary key (TItem_id) )
tDvd:
create table tDvd ( TItem_id integer not null, regionCode varchar(255), primary key (TItem_id) )
并对其子类进行关联:
23:00:33,791 DEBUG SchemaExport:377 - alter table tBook add index FK682557D53D2350B (TItem_id), add constraint FK682557D53D2350B foreign key (TItem_id) references TItem (TItem_id) 23:00:33,842 DEBUG SchemaExport:377 - alter table tDvd add index FK35C8FE53D2350B (TItem_id), add constraint FK35C8FE53D2350B foreign key (TItem_id) references TItem (TItem_id)
添加时候也会按照下面的顺序来执行:
Hibernate: insert into TItem(name, company) values(?, ?) Hibernate:insert into tBook(author, TItem_id) values (?, ?) Hibernate: insert into TItem(name, company) values(?, ?) Hibernate: insert into tDvd(regionCode, TItem_id) values(?, ?)
当我们查询的时候:
Hibernate: select titem0_.TItem_id as TItem1_0_, titem0_.name as name0_, titem0_.company as company0_, titem0_1_.author as author1_, titem0_2_.regionCode as regionCode2_, case when titem0_1_.TItem_id is not null then 1 when titem0_2_.TItem_id is not null then 2 when titem0_.TItem_id is not null then 0 end as clazz_ from TItem titem0_ left outer join tBook titem0_1_ on titem0_.TItem_id=titem0_1_.TItem_id left outer join tDvd titem0_2_ on titem0_.TItem_id=titem0_2_.TItem_id
会比上一种方法减少能源消耗!但是对于性能有很高的要求系统而言,以上2中方式都不可选,多表操作的性能消耗是十分可观的!
综合以上的考虑,我们可以这样设计,将两种实体映射到一个对象里,比如可以用category来区分;1就是dvd,2就是book;其中实体类应该这样写:
TItem:
public class TItem implements java.io.Serializable { private Integer id; private Integer category; private String name; private String company; private String regionCode; private String author; public TItem() { } get()/set() }
其映射文件可以这样TItem.hbm.xml:
<hibernate-mapping package="com.keith.tablePerClassHierarchy"> <class name="TItem" table="TItem"> <id name="id" type="java.lang.Integer" column="TItem_id"> <generator class="native"/> </id> <discriminator column="category" type="string"></discriminator> <property name="name" /> <property name="company" /> <subclass name="TDvd" discriminator-value="1"> <property name="regionCode" /> </subclass> <subclass name="TBook" discriminator-value="2"> <property name="author" /> </subclass> </class> </hibernate-mapping>
当我们添加一个对象时:
先看下建表SQL:
create table TItem ( TItem_id integer not null auto_increment, category varchar(255) not null, name varchar(255), company varchar(255), regionCode varchar(255), author varchar(255), primary key (TItem_id) )
添加数据的SQL:
Hibernate: insert into TItem (name, company, category) values (?, ?, 'com.keith.tablePerClassHierarchy.TItem') Hibernate: insert into TItem (name, company, category) values (?, ?, 'com.keith.tablePerClassHierarchy.TItem')
当我们查询的时候:
Hibernate: select tbook0_.TItem_id as TItem1_0_, tbook0_.name as name0_, tbook0_.company as company0_, tbook0_.author as author0_ from TItem tbook0_ where tbook0_.category='2' Hibernate: select tdvd0_.TItem_id as TItem1_0_, tdvd0_.name as name0_, tdvd0_.company as company0_, tdvd0_.regionCode as regionCode0_ from TItem tdvd0_ where tdvd0_.category='1'
自己就默认找到了!
附上源文件,及测试代码!