【原】Hibernate数据检索策略和.hbm.xml映射文件
一、Hibernate的数据检索策略是面向对象的策略,对与不同的检索情况,应使用不同的策略,以提高效率节省资源。经常使用的是立即检索和延迟检索,预先抓取和批量检索都是为了优化加载性能而设计的策略。检索一般是建立在表关联的基础上的所以我们先建立三张表Team与Student是一对多关系,Student和Certificate是一对一关系。
Team→id:String、teamName:String
Student→id:String、team_id、name、cardid、age
Certificate→id:String、describe
在这里Team相当于原对象Student相当于Team的附属物,Certificate相当于Student的附属物。当加载一个原对象时,原对象对附属物的使用有2中情况:(1)马上就用附属物的数据(2)可能一会用,可能不用。第一种情况适用于立即检索,第二种情况适用于延迟检索。
1.立即检索
立即检索的配置文件标识符为lazy="false"。
一般情况下我们把一对一和多对一这样后者为一的对象用于立即检索,因为是“一”的这段,所带来的性能消耗也有限。立即装载附属物有个好处,原对象脱离管理成为VO以后,仍然可以使用这些附属物。而对一对多和多对多关联对象则不适合立即检索。比如班级和学生是一对多的关系,当取得班级对象时,如过是立即检索,就会把班级的所有学生对象组装起来,这种资源消耗在这没有必要。
2.延迟检索
延迟检索的配置文件标识符为lazy="true"。
延迟检索就是等到需要用到时(比如调用了Team.getTeamName()。调用stu.getTeam()不行,必须以Team为主体取得附属物时,才会实现延迟加载)才会去取被关联对象Team。因此如果在Student对象的配置文件中设置了对Team进行延迟检索,则在取的学生1对象时,Hibernate仅把学生1对象设置为立即检索的对象和属性装配进学生1对象,而并不会立即去组装设为延迟检索的附属物,只有在同一个Session中使用到班级1时,Hibernate才会从数据库中查找数据并装配成班级1对象。
如果把班级对象纳入另一个Session中,使用org.hibernate.Hibernate这个类的initialize()方法,这时脱离Session管理仍然可以从班级对象中取出数据。
3.预先抓取
预先抓取的配置文件标识符为fetch="join"
如果把三个表之间的关系都设为立即检索,取得班级对象时,从而递归的得到学生和身份证对象。如果有100个学生,则要发送100条SQL语句去取100个身份证。这样会很大程度的降低性能。所以必须减少发送的SQL语句的数量。使用预先抓取只是用一条SQL语句就能代替立即检索的100条SQL语句。再具体点比如有一堆货物,原先有100个人排队顺序去背,现在让一个人一次背回来,反而影响性能。每次背的数量可用hibernate.max_fetch_depth来控制。还有一点需要注意,HQL使用外连接查询时忽略配置文件中配置的预先抓取策略,如:Query q = session .createQuery("from Strudent as s left join s.team");所以我们应该改为Query q = session .createQuery("from Strudent as s left join fetch s.team");
仅从使用角度来说。预先抓取和立即检索的效果一样,只不过预先抓取能减少SQL语句的条数。
4.批量加载
批量加载总是和立即或延迟加载联系在一起,分为批量立即加载和批量延迟加载。主要是控制发送SQL语句的条数,较少资源的消耗。
对于一对多、多对多在集合的配置中设置,以set为例:
<set name = "students" inverse="true" batch-size="3">
<key column="team_id"/>
<one-to-many class="model.Student"/>
</set>
一对一、多对一比如学生对班级是多对一关系,要实现对班级的批量加载,如下:
<class name="model.Team" table="team" batch-size="3">
二、下面分析一下.hbm.xml文件
有3个实体,Team、Student、Certificate,Team与Student是一对多关系,Student和Certificate是一对一。
(1).Student.hbm.xml配置文件源码:
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4<hibernate-mapping>
5 <class name="model.Student" table="student" lazy="true">
6 <id name="id" unsaved-value="null">
7 <generator class="uuid.hex"/>
8 </id>
9 <property name="cardid" type="string"/><!--映射号-->
10 <property name="name" type="string"/><!--映射学生名-->
11 <property name="age" type="int"/><!--映射学生年龄-->
12 <one-to-one name="cer" class="model.Certificate" fetch="join" cascade="all"/><!--映射对应的身份证对象-->
13 <many-to-one name="team" class="model.Team" column="team_id" fetch="join"/><!--映射班级-->
14 </class>
15</hibernate-mapping>Student.hbm.xml配置文件信息的解释如下:
第5行:model.Student类对应的数据库表是student表。lazy="true"当使用一对一、多对一方式加载Student类时,将采用延迟加载(前提是设置预先抓取为false)。
第6、7、8行:Student类有一个主键id,id由Hibernate自动生成,生成算法是uuid.hex。在内存中有很多对象,可以通过unsaved-value="null"来判断对象是持久化的还是临时状态。当对象的id值为unsaved-value指定的"null"时,认为未持久化,否则认为此对象是持久化或托管状态。注意int、long型的主键id的默认unsaved-value="0"。
第12行:一个一对一关联的对象属性,名为cer,fetch="join"在得到Student对象时。采用预先抓取得到cer对象。cascade="all"表示级联为all,说明Student的crud操作都会影响cer对象。
第13行:一个多对一关联的对象属性,名为Team,在Student表中通过team_id与Team对象发生关联,引用team中id成外键,没个Student对象对应唯一的team。team的实体类是model.Team。在取得Student实例时。使用预先抓取得到team对象。
(2).Certificate.hbm.xml配置文件源码:
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4<hibernate-mapping>
5 <class name="model.Certificate" table="certificate" lazy="true">
6 <id name="id">
7 <generator class="foreign">
8 <param name="property">stu</param>
9 </generator>
10 </id>
11 <one-to-one name="stu" class="model.Student" fetch="join" constrained="true">
12 </one-to-one>
13 <property name="describe" column="describes" type="string"></property>
14 </class>
15</hibernate-mapping>Certificate.hbm.xml配置文件信息的解释如下:
第6、7、8、9、10行:Certificate和Student是一对一关联,使用的是以主键关联,即Certificate和Student公用一个主键值。Certificate的主键id生成使用外键方式(generator class="foreign"),此外键参考一对一引用中的"stu"变量。
第11行:一对一关联model.Student类,当取Certificate对象时,预先抓取Student类(fetch="join"),声明了constrained="true"。这个选项影响save()和delete()在级联执行的先后顺序以及决定该关联类能否被延迟加载(true时,此是的Student类才可被延迟加载)。
(3).Team.hbm.xml配置文件源码:
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4<hibernate-mapping>
5 <class name="model.Team" table="team" lazy="true">
6 <id name="id" unsaved-value="null">
7 <generator class="uuid.hex">
8 </generator>
9 </id>
10 <property name="teamName" type="string"/>
11 <set name="students" inverse="true" fetch="select" lazy="true">
12 <key column="team_id"/>
13 <one-to-many class="model.Student"/>
14 </set>
15 </class>
16</hibernate-mapping>Team.hbm.xml配置文件信息的解释如下:
第11、12、13、14行:一个Set集合名为students,采用延迟加载,在实体对象Student对象的student表中。一team_id列对应Team对象。
(4)需要在说一下的问题:
cascade属性的用法:
save-update:在执行save/update/saveOrUpdate时进行关联操作。
delete:在执行delete时进行关联操作。
none:所有情况下均不进行关联操作。这是默认值。
all的意思是save-update + delete 。
all-delete-orphan 的意思是当对象图中产生孤儿节点时,在数据库中删除该节点 。
举个例子说一下all-delete-orphan,Category与Item是一对多的关系,也就是说Category类中有个Set类型的变量items.
举个例子,现items中存两个Item, item1,item2,如果定义关系为all-delete-orphan ,当items中删除掉一个item(比如用remove()方法删除item1),那么被删除的Item类实例 ,将变成孤儿节点,当执行category.update(),或session.flush()时
hibernate同步缓存和数据库,会把数据库中item1对应的记录删掉。
在多对多关系中不可以把cascade设置为all和delete,这样问题就大了,想想会有什么后果。
unique属性的用法:
以外间关联的一对一,本质上变成了一对多的双向关联了,编写以一对多和多对一要求编写,最后在many-to-one这一边加上unique="true"即说明它只不过是一对多的特例(“多”这一方只有一个对象)。
constrained属性的用法:
对one-to-one关系进行延迟加载和其他关系相比稍微有些不同。many-to-one的延迟加载是在配置文件的class标签设置lazy="true",one-to-many和many-tomany的延迟加载在Set标签设置lazy="true",而one-to-one不只要在class标签设置lazy="true",而且要在one-to-one标签中设置constrained="true"。也就是说one-to-one关系,constrained="true"和附属物的lazy="true"才能实现延迟加载,并忽略默认的预先抓取。(这里说的是默认,如果指定fetch="join",则无论如何都是预先抓取)。
inverse属性的用法:
两个表设置了双相关联,inverse="true"表示双方的关系将由对方去维护。
一对多应该让“多”的这一端有主动权,也就是在“一”这一端设置inverse="true"。
多对多关联关系中,如果双方都有控制权,这时会有一些问题,比如双方都引起插入语句,这就会违反数据库的主键约束,原因是试图插入重复主键。说以在某一方设置inverse="true",然后在执行语句时使用主控方进行操作(也就是没有设置inverse="true"的一方)。这样就解决了。
一、Hibernate的数据检索策略是面向对象的策略,对与不同的检索情况,应使用不同的策略,以提高效率节省资源。经常使用的是立即检索和延迟检索,预先抓取和批量检索都是为了优化加载性能而设计的策略。检索一般是建立在表关联的基础上的所以我们先建立三张表Team与Student是一对多关系,Student和Certificate是一对一关系。
Team→id:String、teamName:String
Student→id:String、team_id、name、cardid、age
Certificate→id:String、describe
在这里Team相当于原对象Student相当于Team的附属物,Certificate相当于Student的附属物。当加载一个原对象时,原对象对附属物的使用有2中情况:(1)马上就用附属物的数据(2)可能一会用,可能不用。第一种情况适用于立即检索,第二种情况适用于延迟检索。
1.立即检索
立即检索的配置文件标识符为lazy="false"。
一般情况下我们把一对一和多对一这样后者为一的对象用于立即检索,因为是“一”的这段,所带来的性能消耗也有限。立即装载附属物有个好处,原对象脱离管理成为VO以后,仍然可以使用这些附属物。而对一对多和多对多关联对象则不适合立即检索。比如班级和学生是一对多的关系,当取得班级对象时,如过是立即检索,就会把班级的所有学生对象组装起来,这种资源消耗在这没有必要。
2.延迟检索
延迟检索的配置文件标识符为lazy="true"。
延迟检索就是等到需要用到时(比如调用了Team.getTeamName()。调用stu.getTeam()不行,必须以Team为主体取得附属物时,才会实现延迟加载)才会去取被关联对象Team。因此如果在Student对象的配置文件中设置了对Team进行延迟检索,则在取的学生1对象时,Hibernate仅把学生1对象设置为立即检索的对象和属性装配进学生1对象,而并不会立即去组装设为延迟检索的附属物,只有在同一个Session中使用到班级1时,Hibernate才会从数据库中查找数据并装配成班级1对象。
如果把班级对象纳入另一个Session中,使用org.hibernate.Hibernate这个类的initialize()方法,这时脱离Session管理仍然可以从班级对象中取出数据。
3.预先抓取
预先抓取的配置文件标识符为fetch="join"
如果把三个表之间的关系都设为立即检索,取得班级对象时,从而递归的得到学生和身份证对象。如果有100个学生,则要发送100条SQL语句去取100个身份证。这样会很大程度的降低性能。所以必须减少发送的SQL语句的数量。使用预先抓取只是用一条SQL语句就能代替立即检索的100条SQL语句。再具体点比如有一堆货物,原先有100个人排队顺序去背,现在让一个人一次背回来,反而影响性能。每次背的数量可用hibernate.max_fetch_depth来控制。还有一点需要注意,HQL使用外连接查询时忽略配置文件中配置的预先抓取策略,如:Query q = session .createQuery("from Strudent as s left join s.team");所以我们应该改为Query q = session .createQuery("from Strudent as s left join fetch s.team");
仅从使用角度来说。预先抓取和立即检索的效果一样,只不过预先抓取能减少SQL语句的条数。
4.批量加载
批量加载总是和立即或延迟加载联系在一起,分为批量立即加载和批量延迟加载。主要是控制发送SQL语句的条数,较少资源的消耗。
对于一对多、多对多在集合的配置中设置,以set为例:
<set name = "students" inverse="true" batch-size="3">
<key column="team_id"/>
<one-to-many class="model.Student"/>
</set>
一对一、多对一比如学生对班级是多对一关系,要实现对班级的批量加载,如下:
<class name="model.Team" table="team" batch-size="3">
二、下面分析一下.hbm.xml文件
有3个实体,Team、Student、Certificate,Team与Student是一对多关系,Student和Certificate是一对一。
(1).Student.hbm.xml配置文件源码:
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4<hibernate-mapping>
5 <class name="model.Student" table="student" lazy="true">
6 <id name="id" unsaved-value="null">
7 <generator class="uuid.hex"/>
8 </id>
9 <property name="cardid" type="string"/><!--映射号-->
10 <property name="name" type="string"/><!--映射学生名-->
11 <property name="age" type="int"/><!--映射学生年龄-->
12 <one-to-one name="cer" class="model.Certificate" fetch="join" cascade="all"/><!--映射对应的身份证对象-->
13 <many-to-one name="team" class="model.Team" column="team_id" fetch="join"/><!--映射班级-->
14 </class>
15</hibernate-mapping>Student.hbm.xml配置文件信息的解释如下:
第5行:model.Student类对应的数据库表是student表。lazy="true"当使用一对一、多对一方式加载Student类时,将采用延迟加载(前提是设置预先抓取为false)。
第6、7、8行:Student类有一个主键id,id由Hibernate自动生成,生成算法是uuid.hex。在内存中有很多对象,可以通过unsaved-value="null"来判断对象是持久化的还是临时状态。当对象的id值为unsaved-value指定的"null"时,认为未持久化,否则认为此对象是持久化或托管状态。注意int、long型的主键id的默认unsaved-value="0"。
第12行:一个一对一关联的对象属性,名为cer,fetch="join"在得到Student对象时。采用预先抓取得到cer对象。cascade="all"表示级联为all,说明Student的crud操作都会影响cer对象。
第13行:一个多对一关联的对象属性,名为Team,在Student表中通过team_id与Team对象发生关联,引用team中id成外键,没个Student对象对应唯一的team。team的实体类是model.Team。在取得Student实例时。使用预先抓取得到team对象。
(2).Certificate.hbm.xml配置文件源码:
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4<hibernate-mapping>
5 <class name="model.Certificate" table="certificate" lazy="true">
6 <id name="id">
7 <generator class="foreign">
8 <param name="property">stu</param>
9 </generator>
10 </id>
11 <one-to-one name="stu" class="model.Student" fetch="join" constrained="true">
12 </one-to-one>
13 <property name="describe" column="describes" type="string"></property>
14 </class>
15</hibernate-mapping>Certificate.hbm.xml配置文件信息的解释如下:
第6、7、8、9、10行:Certificate和Student是一对一关联,使用的是以主键关联,即Certificate和Student公用一个主键值。Certificate的主键id生成使用外键方式(generator class="foreign"),此外键参考一对一引用中的"stu"变量。
第11行:一对一关联model.Student类,当取Certificate对象时,预先抓取Student类(fetch="join"),声明了constrained="true"。这个选项影响save()和delete()在级联执行的先后顺序以及决定该关联类能否被延迟加载(true时,此是的Student类才可被延迟加载)。
(3).Team.hbm.xml配置文件源码:
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4<hibernate-mapping>
5 <class name="model.Team" table="team" lazy="true">
6 <id name="id" unsaved-value="null">
7 <generator class="uuid.hex">
8 </generator>
9 </id>
10 <property name="teamName" type="string"/>
11 <set name="students" inverse="true" fetch="select" lazy="true">
12 <key column="team_id"/>
13 <one-to-many class="model.Student"/>
14 </set>
15 </class>
16</hibernate-mapping>Team.hbm.xml配置文件信息的解释如下:
第11、12、13、14行:一个Set集合名为students,采用延迟加载,在实体对象Student对象的student表中。一team_id列对应Team对象。
(4)需要在说一下的问题:
cascade属性的用法:
save-update:在执行save/update/saveOrUpdate时进行关联操作。
delete:在执行delete时进行关联操作。
none:所有情况下均不进行关联操作。这是默认值。
all的意思是save-update + delete 。
all-delete-orphan 的意思是当对象图中产生孤儿节点时,在数据库中删除该节点 。
举个例子说一下all-delete-orphan,Category与Item是一对多的关系,也就是说Category类中有个Set类型的变量items.
举个例子,现items中存两个Item, item1,item2,如果定义关系为all-delete-orphan ,当items中删除掉一个item(比如用remove()方法删除item1),那么被删除的Item类实例 ,将变成孤儿节点,当执行category.update(),或session.flush()时
hibernate同步缓存和数据库,会把数据库中item1对应的记录删掉。
在多对多关系中不可以把cascade设置为all和delete,这样问题就大了,想想会有什么后果。
unique属性的用法:
以外间关联的一对一,本质上变成了一对多的双向关联了,编写以一对多和多对一要求编写,最后在many-to-one这一边加上unique="true"即说明它只不过是一对多的特例(“多”这一方只有一个对象)。
constrained属性的用法:
对one-to-one关系进行延迟加载和其他关系相比稍微有些不同。many-to-one的延迟加载是在配置文件的class标签设置lazy="true",one-to-many和many-tomany的延迟加载在Set标签设置lazy="true",而one-to-one不只要在class标签设置lazy="true",而且要在one-to-one标签中设置constrained="true"。也就是说one-to-one关系,constrained="true"和附属物的lazy="true"才能实现延迟加载,并忽略默认的预先抓取。(这里说的是默认,如果指定fetch="join",则无论如何都是预先抓取)。
inverse属性的用法:
两个表设置了双相关联,inverse="true"表示双方的关系将由对方去维护。
一对多应该让“多”的这一端有主动权,也就是在“一”这一端设置inverse="true"。
多对多关联关系中,如果双方都有控制权,这时会有一些问题,比如双方都引起插入语句,这就会违反数据库的主键约束,原因是试图插入重复主键。说以在某一方设置inverse="true",然后在执行语句时使用主控方进行操作(也就是没有设置inverse="true"的一方)。这样就解决了。