首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Hibernate应用——实体映射的几种策略

2013-08-01 
Hibernate使用——实体映射的几种策略create table t_person(id int(11) not null auto_increment,name varc

Hibernate使用——实体映射的几种策略
create table t_person( id int(11) not null auto_increment, name varchar(80) not null default '', address varchar(100), tel varchar(12), zipcode varchar(10), primary key (id));

?

?原来的TPerson.java如下:

package learnHibernate.bean;import java.io.Serializable;public class TPerson implements Serializable {private static final long serialVersionUID = -7714660203394864063L;private int id;private String name;private String address;private String tel;private String zipcode;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public String getTel() {return tel;}public void setTel(String tel) {this.tel = tel;}public String getZipcode() {return zipcode;}public void setZipcode(String zipcode) {this.zipcode = zipcode;}}

?

经过重新规划后,决定将联系方式的信息封装到Contact类当中。

变成如下:由Tperson持有Contact对象

package learnHibernate.bean;import java.io.Serializable;public class TPerson implements Serializable {private static final long serialVersionUID = -7714660203394864063L;private int id;private String name;private Contact contact;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Contact getContact() {return contact;}public void setContact(Contact contact) {this.contact = contact;}}

?

Contact.java:

package learnHibernate.bean;import java.io.Serializable;public class Contact implements Serializable {private static final long serialVersionUID = 2372937305763736126L;private String address;private String tel;private String zipcode;public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public String getTel() {return tel;}public void setTel(String tel) {this.tel = tel;}public String getZipcode() {return zipcode;}public void setZipcode(String zipcode) {this.zipcode = zipcode;}}

?

对于上述,hibernate的hbm.xml映射文件用到了component节点:

<?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 package="learnHibernate.bean"><class name="TPerson" table="t_person"><id name="id" column="id" type="java.lang.Integer"><generator column="name" type="java.lang.String"/><component name="contact" column="address" type="java.lang.String"/><property name="tel" column="tel" type="java.lang.String"/><property name="zipcode" column="zipcode" type="java.lang.String"/></component></class></hibernate-mapping>?

上述就ORM这一方面,与普通的类表映射没有太大区别,只是体现在设计上面的改进。

?

?

面向性能的粒度细分

1.情景:现在有一表T_user,其中有一粗大无比的字段resume:

CREATE TABLE  t_user (  id int(11) NOT NULL auto_increment,  name varchar(80) NOT NULL default '',  resume longtext,  PRIMARY KEY  (id));

?有时候,我们只想列出user的name列表,此时若也把resume这个字段一并查出,这无疑造成不必要的性能浪费。。。此时可以用延迟加载的方式解决,此处不赘述。介绍另一种:

?

在继承层次上对粒度进一步细化:

原来的TUser.java:

package learnHibernate.bean;    import java.io.Serializable;    public class TUser implements Serializable{      private static final long serialVersionUID = -2983670695642662371L;            private int id;      private String name;      private String resume;            public int getId() {          return id;      }      public void setId(int id) {          this.id = id;      }      public String getName() {          return name;      }      public void setName(String name) {          this.name = name;      }      public String getResume() {                return resume;      }      public void setResume(String resume) {            this.resume = resume;      }  } 

?

现在将resume从Tuser.java中抽出,移到子类TUserInfo.java当中:

package learnHibernate.bean;public class TuserInfo extends TUser {private static final long serialVersionUID = -7362075358002914585L;private String resume;public String getResume() {return resume;}public void setResume(String resume) {this.resume = resume;}}

?

对应的映射文件如下:

TUser.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 package="learnHibernate.bean"><class name="TUser" table="t_user"><id name="id" column="id" type="java.lang.Integer"><generator column="name" type="java.lang.String"/></class></hibernate-mapping>

?

TUserInfo.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 package="learnHibernate.bean"><class name="TUserInfo" table="t_user" polymorphism="explicit"><id name="id" column="id" type="java.lang.Integer"><generator column="name" type="java.lang.String"/><property name="resume" column="resume" type="java.lang.String"/></class></hibernate-mapping>

?

其中polymorphism="explicit"意思是声明一个显式的多态关系,声明为显式多态的类只有在明确指定类名的时候才会返回此类实例:?

?

String hql1 = "From TUser where name='Oham'";TUser tu = (TUser)session.createQuery(hql1).list().get(0);System.out.println("=============");String hql2 = "From TUserInfo where name='Oham'";TUserInfo ti = (TUserInfo)session.createQuery(hql2).list().get(0);

?

?

若执行类似上述的代码,看后台log出的SQL:

?

Hibernate:     select        tuser0_.id as id0_,        tuser0_.name as name0_     from        t_user tuser0_     where        tuser0_.name='Oham'=============Hibernate:     select        tuserinfo0_.id as id0_,        tuserinfo0_.name as name0_,        tuserinfo0_.resume as resume0_     from        t_user tuserinfo0_     where        tuserinfo0_.name='Oham'

?若执行createQuery("From Object").list(); 则将返回数据库中所有的表记录的数据对象,其中,对应t_user表的记录将以Tuse返回,而不是TUserInfo,也就是说不包含resume字段。

?

?

2.实体层次设计——继承关系是关系型数据与面向对象数据结构之间的主要差异之一,在关系型数据库的基础上,就对象的继承关系进行清晰合理的层次划分。

? ? Hibernate中支持3种类型的继承形式

? ? 1)Table per concrete class ? —— 表与子类之间的独立一对一关系;

? ? 2)Table per subclass ? —— 每个子类对应一张子表,并与主类共享主表;

? ? 3)Table per class hierarchy ?—— 表与类的一对多关系;

?

?现给出如下的类关系:

TMember.java

package learnHibernate.bean;import java.io.Serializable;import java.util.List;public class TMember implements Serializable{private static final long serialVersionUID = -2487367694260008988L;private int id;private String name;private List email;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public List getEmail() {return email;}public void setEmail(List email) {this.email = email;}}

?

?TOham和TLulu都继承TMember,TOham.java:

package learnHibernate.bean;public class TOham extends TMember {private String meditation;public String getMeditation() {return meditation;}public void setMeditation(String meditation) {this.meditation = meditation;}}

?

?TLulu.java:

package learnHibernate.bean;public class TLulu extends TMember {private String sixthSense;public String getSixthSense() {return sixthSense;}public void setSixthSense(String sixthSense) {this.sixthSense = sixthSense;}}

?

?Table per concrete class ? —— 表与子类之间的独立一对一关系:

TOham和TLulu都继承于TMember,所以自然就包含了Tmember的属性了,在Table per concrete class模式当中,每个子类分别对应一个独立的表,表中包含了子类所需的所有字段:

t_oham表:

Hibernate应用——实体映射的几种策略

?

TOham.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 package="learnHibernate.bean"><class name="TOham" table="t_oham"><id name="id" column="id" type="java.lang.Integer"><generator   column="name"   type="java.lang.String"/>  <property name="email"   column="email"   type="learnHibernate.bean.EmailList" />  <property name="meditation"   column="meditation"   type="java.lang.String"/></class></hibernate-mapping>

?

t_lulu表:

Hibernate应用——实体映射的几种策略

?

TLulu.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 package="learnHibernate.bean"><class name="TLulu" table="t_lulu"><id name="id" column="id" type="java.lang.Integer"><generator   column="name"   type="java.lang.String"/>  <property name="email"   column="email"   type="learnHibernate.bean.EmailList" />  <property name="sixthSense"   column="sixthsense"   type="java.lang.String"/></class></hibernate-mapping>

?
?从上述配置可以看出Table per concrete class?模式的映射方式似乎跟普通的映射并无区别,在hibernate的角度,以多态(polymorphism)来描述TOham,TLulu与TMember的继承关系,TOham,TLulu的映射配置文件没有出现polymorphism属性的定义,也就是说采用了默认的隐式多态模式(polymorphism=“implicit”)。

执行:

String hql = "From TMember";List list = session.createQuery(hql).list();

后台 log出的hibernate SQL:

?

Hibernate:     select        tlulu0_.id as id3_,        tlulu0_.name as name3_,        tlulu0_.email as email3_,        tlulu0_.sixthsense as sixthsense3_     from        t_lulu tlulu0_Hibernate:     select        toham0_.id as id2_,        toham0_.name as name2_,        toham0_.email as email2_,        toham0_.meditation as meditation2_     from        t_oham toham0_Hibernate:     select        tmember0_.id as id1_,        tmember0_.name as name1_,        tmember0_.email as email1_     from        t_member tmember0_

?Hibernate会在当前环境中查找所有polymorphism=“implicit”的子类,并返回子类所对应的所有表的记录。

?

可以看出,对象的继承关系在持久层得到了体现,不过此种映射方式也存在着一些局限,如t_oham,t_lulu的父字段必须保持一致,若父类TMember发生变动,子类必须同时修改。有时候我们会根据一个name字段进行查询,此时就可能要对每个子表查询后汇总,于是我们希望有的大表包含所有可能出现的字段。借助这种情形,下面介绍Table per subclass ? —— 每个子类对应一张子表,并与主类共享主表?和?Table per class hierarchy ?—— 表与类的一对多关系

?

Table per subclass ? —— 每个子类对应一张子表

接着上述的例子由于父类TMember发生变动,子类TOham,TLulu必须同时修改,所以重新设计表ER,让t_oham和t_lulu字表只包含子类所扩展的属性,同时子表与父表通过外键相关联:

?
Hibernate应用——实体映射的几种策略
TMember.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 package="learnHibernate.bean"><class name="TMember" table="t_member"><id name="id" column="id" type="java.lang.Integer"><generator column="name" type="java.lang.String"/><property name="email" column="email" type="learnHibernate.bean.EmailList" /><joined-subclass name="TOham" table="t_oham"><key column="id"/><property name="meditation" column="meditation"/></joined-subclass><joined-subclass name="TLulu" table="t_lulu"><key column="id"></key><property name="sixthSense" column="sixthsense"/></joined-subclass></class></hibernate-mapping>

?通过joined-subclass节点在父类映射文件中对子类TOham, TLulu进行配置,joined-subclass节点与class节点类似,且joined-subclass节点可以嵌套。

?

执行:

?

TOham o = new TOham();o.setName("oham2");o.setMeditation("Civilization Rise");session.save(o);TOham o2 = new TOham();o2.setName("oham3");session.save(o2);TLulu l = new TLulu();l.setName("Lulu2");l.setSixthSense("Dancing soul");session.save(l);

?

后台log:

?

Hibernate:     insert     into        t_member        (name, email)     values        (?, ?)Set method executedHibernate:     insert     into        t_oham        (meditation, id)     values        (?, ?)Hibernate:     insert     into        t_member        (name, email)     values        (?, ?)Set method executedHibernate:     insert     into        t_oham        (meditation, id)     values        (?, ?)Hibernate:     insert     into        t_member        (name, email)     values        (?, ?)Set method executedHibernate:     insert     into        t_lulu        (sixthsense, id)     values        (?, ?)

?

再执行:

String hql = "From TMember";session.createQuery(hql).list();

?后台log:

Hibernate:     select        tmember0_.id as id1_,        tmember0_.name as name1_,        tmember0_.email as email1_,        tmember0_1_.meditation as meditation2_,        tmember0_2_.sixthsense as sixthsense3_,        case             when tmember0_1_.id is not null then 1             when tmember0_2_.id is not null then 2             when tmember0_.id is not null then 0         end as clazz_     from        t_member tmember0_     left outer join        t_oham tmember0_1_             on tmember0_.id=tmember0_1_.id     left outer join        t_lulu tmember0_2_             on tmember0_.id=tmember0_2_.id

?

相对于Table per concrete class,Table per subclass 带来了更加清晰的数据逻辑划分,不过跟Table per concrete class类似,当遇到多表操作的时候,系统性能都不太高,对于高并发的数据存取都不利;以此来介绍Table per class hierarchy。

?

实际开发中,通过冗余字段表达同类型数据可能是我们在绝大多数情况下的选择。对于上述的示例,我们可以通过一个包含所有子类字段的t_member表存储所有信息。

对于上述例子,重建t_member表:

Hibernate应用——实体映射的几种策略

这样,数据的存取都能通过一条简单的sql即可完成。在简易和性能两方面考量都能得到一个较为满意的结果。但需要重新设计映射以体现不同子类的差异。

?

此时再对t_member表添加一个字段来标识不同的子类:Category。

Category 为1 是代表TOham记录

Category 为2 是代表TLulu记录。

Hibernate应用——实体映射的几种策略
?

为了hibernate能自动根据category节点识别对应的子类class类型,需要在配置文件中进行配置,而discriminator节点,则定义了这种配置关系。

TMember.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 package="learnHibernate.bean"><class name="TMember" table="t_member"><id name="id" column="id" type="java.lang.Integer"><generator    type="java.lang.String"/><property name="name"   column="name"   type="java.lang.String"/><property name="email"   column="email"   type="learnHibernate.bean.EmailList" /><!-- 辨别标识的字段值为1时,对应子类为TOham --><subclass name="TOham"    discriminator-value="1"><property name="meditation"   column="meditation"/></subclass><!-- 辨别标识的字段值为2时,对应子类为TLulu --><subclass name="TLulu"   discriminator-value="2"><property name="sixthSense"   column="sixthsense"/>    </subclass></class></hibernate-mapping>

?如此,运行期hibernate在读取t_member表数据时,会根据指定的辨别标识进行判断,如果记录的category为1,则映射到TOham,为2映射到TLulu。

?

执行:

String hql1 = "From TOham";String hql2 = "From TLulu";session.createQuery(hql1).list();session.createQuery(hql2).list();

?后台log:

Hibernate:     select        toham0_.id as id1_,        toham0_.name as name1_,        toham0_.email as email1_,        toham0_.meditation as meditation1_     from        t_member toham0_     where        toham0_.category='1'Hibernate:     select        tlulu0_.id as id1_,        tlulu0_.name as name1_,        tlulu0_.email as email1_,        tlulu0_.sixthsense as sixthsense1_     from        t_member tlulu0_     where        tlulu0_.category='2'

?注意一点:discriminator 节点的type貌似不能指定为除String以外的类型,在下试过,说:

? ? ? ? ? ? ? ? ? ? ? ? ? Caused by: org.hibernate.MappingException: Could not format discriminator value to SQL string。

?

?

?

?

?

?

?

?

?

?

?


?

?

?

?

?

热点排行