环境: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。
相关推荐
源码可能包含了`Employee`、`Address`以及其他相关类,以及对应的配置文件,如`hibernate.cfg.xml`,用以配置Hibernate的会话工厂和实体映射。 在配置文件中,组件映射不会像实体那样定义一个单独的表,而是将组件...
在 Hibernate 中,为了处理对象模型中的继承关系,提供了以下几种继承策略: 1. **单表继承(Single Table Inheritance)**:所有子类共享同一张数据库表,使用一个标识字段来区分不同的子类实例。优点是简单,但...
Hibernate Annotation几种关联映射 一对一(One-To-One) ...以上是整理的一点简单的几种映射,可参考EJB3.pdf中P111——P131,hibernate_annotation.pdf 第二章 在这里没有具体的例子,有很多内容还需要仔细查看文档。
本文通过对给定部分内容的分析,介绍了Hibernate实体层设计的基本原理和技巧,包括实体类的定义、映射文件的设计以及单一表设计模式的优缺点。通过学习这些内容,开发者可以更好地掌握Hibernate的使用方法,从而提高...
首先,我们要理解Hibernate的核心概念——对象关系映射(ORM)。ORM允许我们将数据库表结构映射到Java类,每个表对应一个类,表中的字段对应类的属性。这种映射是通过Hibernate的配置文件(如.hbm.xml)或者使用注解...
Struts2和Hibernate3是两种在Java EE开发中广泛应用的开源框架。Struts2作为MVC(模型-视图-控制器)架构的实现,主要用于处理Web应用中的业务逻辑和控制流程,而Hibernate3则是一个对象关系映射(ORM)工具,简化了...
在“Hibernate快速入门”中,我们将深入理解Hibernate的核心概念,包括实体映射、对象关系映射(ORM)、Session管理以及查询语言。 首先,Hibernate通过ORM机制将Java对象与数据库表进行映射,使得我们可以在Java...
本话题主要探讨的是Hibernate中的一种关联映射方式——一对一(One-to-One)单向外键关联。这种关联模式通常用于两个实体之间存在唯一对应的关系,例如一个用户对应一个唯一的账户。 在Hibernate中,一对一关联可以...
Hibernate是一个开源的对象关系映射(ORM)框架,它允许开发者使用面向对象的编程方式来操作数据库,极大地简化了Java应用程序中的数据持久化过程。在Java开发中,Hibernate扮演着一个重要的角色,它通过提供一种...
本文档将详细介绍如何使用XML配置文件实现Struts2、Spring以及Hibernate(通常称为SSH)这三种技术的整合,以支持一个简单的商品管理系统。这个系统不仅包含了基本的商品管理功能,还实现了图片上传与淘宝风格的分页...
Hibernate使用XML或注解配置来定义这些实体与数据库表之间的映射关系,这被称为Hibernate配置文件或映射文件。 课程内容可能包括以下几个关键知识点: 1. **安装与配置**: 学习如何将Hibernate库添加到项目中,...
但这种方式可能不适用于所有场景,特别是在需要一次性获取所有关联数据时,可以使用以下几种抓取策略: 1. **FetchType.EAGER**:设置关联为预加载,意味着当主实体加载时,其关联的对象也会一起加载。这减少了...
使用Hibernate Annotation时,需要在实体类中使用特定的注解来定义与数据库表之间的映射规则。以下是几种常用的注解: - **@Entity**:标记类为Hibernate的实体类。 - **@Table**:指定实体类所对应的数据库表名...
实体映射是Hibernate的核心部分,通过Java注解或XML文件(如`hibernate-mapping.xml`)将Java类与数据库表关联。例如,`@Entity`标识一个类为实体,`@Table`指定对应的数据库表,`@Id`标记主键,`@GeneratedValue`...
在Hibernate中,当一个类继承体系映射到数据库时,有几种策略可以采用。其中一种是单表继承策略,即"整个继承树映射到一张表"。这种策略意味着所有子类的对象数据都将存储在同一个数据库表中,通过一个特定的字段来...
- 详细解释如何使用XML映射文件或注解来定义实体类与数据库表之间的映射关系。 - **Entity:** - 定义映射的基本单位——Entity。 - **Identifiers:** - 主键的生成策略和映射方式。 - **Optimistic locking ...
2. **表映射(Mapping)**:Hibernate使用XML文件(hibernate.cfg.xml)或注解来定义实体类与数据库表的映射关系。XML配置文件中包含了数据源、SessionFactory等配置,而实体类的注解则用于指定字段与数据库列的对应...
1. **命名策略(NamingStrategy)**:Hibernate默认使用一种命名策略来转换实体类属性名到数据库列名。如果希望自定义这种映射规则,可以通过实现`NamingStrategy`接口来自定义命名策略。 2. **拦截器(Interceptor...