环境:DB——MySQL,hibernate4.1.4
面向设计的粒度细分
通过对象细化,实现更加清晰的系统逻辑划分——情景:重新规划已有系统,通过case分析得出新的类设计,但相应地数据库的表的ER不希望改变。现有一表:t_person
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 class="native"/> </id> <property name="name" column="name" type="java.lang.String"/> <component name="contact" class="learnHibernate.bean.Contact"> <property name="address" 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 class="native"/> </id> <property name="name" 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 class="native"/> </id> <property name="name" 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表:
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 class="native"/> </id> <property name="name" 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表:
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 class="native"/> </id> <property name="name" 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字表只包含子类所扩展的属性,同时子表与父表通过外键相关联:
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 class="native"/> </id> <property name="name" 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 executed Hibernate: insert into t_oham (meditation, id) values (?, ?) Hibernate: insert into t_member (name, email) values (?, ?) Set method executed Hibernate: insert into t_oham (meditation, id) values (?, ?) Hibernate: insert into t_member (name, email) values (?, ?) Set method executed Hibernate: 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表:
这样,数据的存取都能通过一条简单的sql即可完成。在简易和性能两方面考量都能得到一个较为满意的结果。但需要重新设计映射以体现不同子类的差异。
此时再对t_member表添加一个字段来标识不同的子类:Category。
Category 为1 是代表TOham记录
Category 为2 是代表TLulu记录。
为了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 class="native"/> </id> <!-- 通过discriminator节点,声明了用于子类辨别标识的表字段名 --> <discriminator column="category" 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。
相关推荐
Hibernate 4——Hello World
NULL 博文链接:https://gwoham-163-com.iteye.com/blog/1895101
NULL 博文链接:https://gwoham-163-com.iteye.com/blog/1885253
自动生成hibernate映射文件和实体类
Hibernate实体关联关系映射--学习总结,让同仁们更好的学习Hiebernate
hibernate实体映射文件字段设置默认值
注:为节省空间,程序中需要的jar包,均在HibernateManytoManyMapCascadingSave.zip\HibernateManytoManyMapCascadingSave\lib\ <br>Hibernate 多对多实体映射实例 <br>学习Hibernate 实体映射的映射的好帮手...
里面包含Hibernate实体映射的具体实例代码,还有相关教程笔记,喜欢的可以学习学习。
hibernate 映射关系学习入门 多对多实体映射 源码
根据数据库表生成实体、hibernate配置文件和实体映射文件
Hibernate案例与专题-继承映射策略。详细介绍了Hibernate继承映射策略。
Hibernate映射解析 七种映射关系
简单的Hibernate实现Java通过多对多关联映射,进行学生课程管理。
hibernate 对象关系映射总结hibernate 对象关系映射总结hibernate 对象关系映射总结
Hibernate XML配置表映射实例
主要介绍通过Hibernate不用别的插件,逆向生成实体类的方法,包括标签注解映射和hbm.xml文件映射两种
hibernate初学者学习入门之一对多实体映射关系源码