[转帖]Hibernate关系映射收集、总结整理_Tomcat, WebLogic及J2EE讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Tomcat, WebLogic及J2EE讨论区 »
总帖数
2
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3967 | 回复: 1   主题: [转帖]Hibernate关系映射收集、总结整理        下一篇 
    本主题由 yhl.admin 于 2012-12-27 16:05:02 合并
haili.yang
注册用户
等级:少校
经验:936
发帖:71
精华:1
注册:2012-12-24
状态:离线
发送短消息息给haili.yang 加好友    发送短消息息给haili.yang 发消息
发表于: IP:您无权察看 2012-12-27 14:32:08 | [全部帖] [楼主帖] 楼主

一、概念:
关系:名词,事物之间相互作用、相互联系的状态。
关联:名词:表示对象(数据库表)之间的关系;动词:将对象(数据库表)之间通过某种方式联系起来。
映射:将一种形式转化为另一种形式,包括关系。
级联:动词,有关系的双方中操作一方,另一方也将采取一些动作。
值类型:对象不具备数据库同一性,属于一个实体实例其持久化状态被嵌入到所拥有的实体的表行中,没有标识符。
实体类型:具有数据库标识符。
二、数据库:
1、关系
2.1.1、一对一、一对多、多对多
2.1.2、如何表示? 外键+索引
2、级联:
2.2.1、级联删除
三、面向对象语言中(Java中):
1、关系
3.1.1、一对一、一对多、多对多
3.1.2、如何表示? 实例变量(对象+集合)
2、级联:
3.2.1、级联删除
3.2.2、级联更新
3.2.3、级联保存

四、如何把数据库关系表示为面向对象中的关系:
1、关联:将数据库表之间的关系转化为对象之间的关系;在Hibernate中总指实体之间的关系。
2、映射:完成java对象到数据库表的双向转换。
3、级联(可选):将数据库中的级联转化为对象中的级联(两者(对象和数据库表)没关系)。
4、Hibernate的表和对象的映射:
1、实体类型映射:
4.1.1、主键之间的映射
4.1.2、类属性与表字段的映射
4.1.3、组件映射
4.1.4、集合映射
2、实体关联关系映射:
4.2.1、关联关系映射
五、Hibernate映射示例:
5.1、实现
5.1.1、数据库表定义(主表)
5.1.1.1、用户表

Java代码   北京联动北方科技有限公司


  1. CREATE TABLE TBL_USER (  
  2. UUID NUMBER(10) NOT NULL,   
  3. NAME VARCHAR2(100),  
  4. AGE NUMBER(10) NOT NULL,   
  5. PROVINCE VARCHAR2(100),  
  6. CITY VARCHAR2(100),  
  7. STREET VARCHAR2(100),  
  8. CONSTRAINT  PK_USER PRIMARY KEY(UUID));  



5.1.1.2、用户普通信息表(一个用户有一个资料)

Java代码   北京联动北方科技有限公司


  1. CREATE TABLE TBL_USER_GENERAL (  
  2. UUID NUMBER(10) NOT NULL,  
  3. REALNAME VARCHAR2(10),  
  4. GENDER VARCHAR2(10),  
  5. BIRTHDAY NUMBER(10),  
  6. HEIGHT NUMBER(10),  
  7. WEIGHT NUMBER(10) ,   
  8.     CONSTRAINT PK_USER_GENERAL PRIMARY KEY(UUID),   
  9.     CONSTRAINT FK_USER_GENERAL FOREIGN KEY(UUID)   
  10.     REFERENCES TBL_USER(UUID));    



5.1.1.3、农场表(一个用户有多个农场)

Java代码   北京联动北方科技有限公司


  1. CREATE TABLE TBL_FARM (  
  2. UUID NUMBER(10) NOT NULL,   
  3. NAME VARCHAR2(10),   
  4. FK_USER_ID NUMBER(10),   
  5.     CONSTRAINT PK_FARM PRIMARY KEY(UUID),   
  6.     CONSTRAINT FK_USER_FARM FOREIGN KEY(FK_USER_ID)   
  7.     REFERENCES TBL_USER(UUID));   



5.1.2、对象定义
5.1.2.1、用户地址Model 

Java代码   北京联动北方科技有限公司


  1. package cn.javass.h3test.model; 
  2. public class AddressModel implements java.io.Serializable { 
  3.        private String province;//省 
  4.        private String city;//市 
  5.        private String street;//街道 



5.1.2.2、用户Model

Java代码   北京联动北方科技有限公司


  1. package cn.javass.h3test.model; 
  2. import java.util.HashSet; 
  3. import java.util.Set; 
  4. public class UserModel implements java.io.Serializable { 
  5.        private int uuid; 
  6.        private String name;//名称 
  7.        private int age;//年龄 
  8.        private AddressModel address;//地址 
  9.        private UserGeneralModel userGeneral;//用户普通信息 
  10.        private Set<FarmModel> farms = new HashSet<FarmModel>();//拥有的农场 



5.1.2.3、用户普通信息Model

Java代码   北京联动北方科技有限公司


  1. package cn.javass.h3test.model; 
  2. public class UserGeneralModel implements java.io.Serializable { 
  3.        private int uuid; 
  4.        private String realname;//真实姓名 
  5.        private String gender;//性别 
  6.        private String birthday;//生日 
  7.        private int weight;//体重 
  8.        private int height;//身高 
  9.       private UserModel user;//所属用户 



5.1.2.4、农场Model

Java代码   北京联动北方科技有限公司


  1. package cn.javass.h3test.model; 
  2. public class FarmModel implements java.io.Serializable { 
  3.        private int uuid; 
  4.        private String name;//农场的名称 
  5.        private UserModel user;//所属用户 



5.2、配置
5.2.1、实体类型映射:
5.2.1.1、主键的映射(UserModel.hbm.xml)

Java代码   北京联动北方科技有限公司


  1. <id name="uuid">  
  2. <generator class="sequence">  
  3. <param name="sequence">user_uuid</param>  
  4. </generator>  
  5. </id>  



5.2.1.2、类属性与表字段的映射(UserModel.hbm.xml)

Java代码   北京联动北方科技有限公司


  1. <property name="name"/>  



5.2.1.3、组件映射(UserModel.hbm.xml)

Java代码   北京联动北方科技有限公司


  1. <component name="address"  class="cn.javass.h3test.model.AddressModel">   
  2.     <property name="province"/>  
  3.     <property name="city"/>  
  4.     <property name="street"/>  
  5. </component>  



5.2.1.4、集合映射(Set、List、Map) (都是通过外键连接的,,,默认延迟抓取)

 Set:


Java代码   北京联动北方科技有限公司


  1. private Set<String> farmSet = new HashSet<String>();  



Java代码   北京联动北方科技有限公司


  1. <set name="farmSet"  table="TBL_FARM" >  
  2. <key column="fk_user_id"/><!—该外键是tbl_farm的-->  
  3.     <element type="string" column="name"/>  
  4. </set>  


Java代码   北京联动北方科技有限公司


  1. private List<String> farmList = new ArrayList<String>();  



Java代码   北京联动北方科技有限公司


  1. <list name="farmList" table="TBL_FARM">  
  2.     <key column="fk_user_id"/>  
  3.     <list-index column="uuid"></list-index>  
  4.     <element type="string" column="name"/>  
  5. </list>   



Java代码   北京联动北方科技有限公司


  1. private Map<Integer, String> farmMap = new HashMap<Integer, String>();  



Java代码   北京联动北方科技有限公司


  1. <map name="farmMap" table="TBL_FARM">  
  2. <key column="fk_user_id"/>  
  3. <map-key type="int" column="uuid"/>  
  4. <element type="string" column="name"></element>  
  5. </map>   



对于集合类型默认是延迟加载的,且只能单向导航,不能双向。

5.2.2、实体关联关系映射:
5.2.2.1、单向关联关系映射,不演示。
5.2.2.2、双向关联关系映射

Java代码   北京联动北方科技有限公司


  1. 单向   
  2. 定义:不知道另一端什么情况,获取一端另一端自动获取,因为单向,你不知道另一侧是什么。  
  3.         如 class A{ B b;}   
  4. class B{ }  
  5.         只能从A导航到B,不能从B导航到A  
  6.         ���系维护:另一端维护,如B维护  
  7. 双向  
  8.         定义:知道另一端(两个单向),从一端获取另一端,从另一端也能获取一端  
  9.     如 class A{ B b;}   
  10. class B{ A a;}  
  11.         只能从A导航到B,也能从B导航到A  
  12.         关系维护:两端,对关联的一侧所作的改变,会立即影响到另一侧  
  13. 关联的多样性:  
  14.     从一侧看是多对一,从另一侧看是一对多  
  15.         另外还有一对一、多对多  
  16. EJB CMP:天生双向,对关联的一侧所作的改变,会立即影响到另一侧,   
  17. 如userGeneral.set(user),则自动调用user.setUserGeneral(userGeneral)  
  18. Hibernate、JPA:天生单向,两侧关系的维护是不同的关联,必须手工维护  
  19. 如userGeneral.set(user),则需要手工调用user.setUserGeneral(userGeneral)。  



5.2.2.3、一对一主键关系映射(非延迟抓取)
配置1(UserModel.hbm.xml)

Java代码   北京联动北方科技有限公司


  1. <one-to-one name="userGeneral" cascade="all"/>   



配置2(UserGeneralModel.hbm.xml)

Java代码   北京联动北方科技有限公司


  1. <id name="uuid">  
  2. <generator class="foreign">  
  3.         <param name="property">user</param>  
  4.     </generator>  
  5. </id>  
  6. <one-to-one name="user"   
  7.  class="cn.javass.h3test.model.UserModel"/>  



关联的对象所对应的数据库表之间,通过一个外键引用对主键进行约束。

测试:保存对象,只需保存user,自动级联保存用户信息Model

Java代码   北京联动北方科技有限公司


  1. UserModel user = new UserModel();  
  2. user.setName("昵称");  
  3. UserGeneralModel userGeneral = new UserGeneralModel();  
  4. userGeneral.setRealname("真实姓名");  
  5. userGeneral.setUser(user);  
  6. user.setUserGeneral(userGeneral);  
  7. session.save(user);  
  8. //若没��cascade="all",这句必须  
  9. //session.save(userGeneral);  



1、一对一必须手工维护双向关系。
2、cascade="all":表示保存user时自动保存userGeneral,否则还需要一条save(userGeneral)
3、constrained:添加把userGeneral表的主键映射到user主键的外键约束

5.2.2.4、一对多关系映射(父/子关系映射)
配置1(UserModel.hbm.xml)

Java代码   北京联动北方科技有限公司


  1. <set name="farms" cascade="all">  
  2. <key column="fk_user_id"/>  
  3.     <one-to-many class="cn.javass.h3test.model.FarmModel"/>  
  4. </set>   



配置2(FarmModel.hbm.xml) 

Java代码   北京联动北方科技有限公司


  1. <many-to-one name="user" column="fk_user_id"   
  2. class="cn.javass.h3test.model.UserModel">  



测试:保存对象,只需保存user,自动级联保存用户信息Model

Java代码   北京联动北方科技有限公司


  1. UserModel user = new UserModel();  
  2. user.setName("昵称");  
  3. UserGeneralModel userGeneral = new UserGeneralModel();  
  4. userGeneral.setRealname("真实姓名");  
  5. userGeneral.setUser(user);  
  6. user.setUserGeneral(userGeneral);  
  7. FarmModel farm = new FarmModel();  
  8. farm.setName("farm1");  
  9. farm.setUser(user);  
  10. user.getFarms().add(farm);  
  11. //session.save(farm);//若没有cascade=all的话需要这条语句  
  12. session.save(user);   



以上配置有问题: 

Java代码   北京联动北方科技有限公司


  1. insert into TBL_USER (name, age, province, city, street, uuid) values (?, ?, ?, ?, ?, ?)  
  2. insert into TBL_USER_GENERAL (realname, gender, birthday, weight, height, uuid) values (?, ?, ?, ?, ?, ?)  
  3. insert into TBL_FARM (name, fk_user_id, uuid) values (?, ?, ?)  
  4. update TBL_FARM set fk_user_id=? where uuid=?  



1、持久化user(UserModel);
2、持久化user的一对一关系,即userGeneral(UserGeneralModel);
3、持久化user的一对多关系,即farms(Set<FarmModel>);
3.1、首先发现farm是TO,级联save;(因为在这可能是PO,PO的话就应该update,而不是save);
3.2、其次发现farm在farms集合中,因此需要更新外键(fk_user_id),即执行“update TBL_FARM set fk_user_id=? where uuid=? “。
解决这个问题:
告诉Hibernate应该只有一端来维护关系(外键),另一端不维护;通过指定<set>端的inverse=”true”,表示关系应该由farm端维护。即更新外键(fk_user_id)将由farm端维护。
配置修改(UserModel.hbm.xml)

Java代码   北京联动北方科技有限公司


  1. <set name="farms" cascade="all" inverse="true">  
  2. <key column="fk_user_id"/>  
  3.     <one-to-many class="cn.javass.h3test.model.FarmModel"/>  
  4. </set>  



再测试:保存对象,只需保存user,自动级联保存用户信息Model

Java代码   北京联动北方科技有限公司


  1. UserModel user = new UserModel();  
  2. user.setName("昵称");  
  3. UserGeneralModel userGeneral = new UserGeneralModel();  
  4. userGeneral.setRealname("真实姓名");  
  5. userGeneral.setUser(user);  
  6. user.setUserGeneral(userGeneral);  
  7. FarmModel farm = new FarmModel();  
  8. farm.setName("farm1");  
  9. farm.setUser(user);  
  10. user.getFarms().add(farm);  
  11. //session.save(farm);//若没有cascade=all的话需要这条语句  
  12. session.save(user);  



更新外键,需要修改FarmModel的外键并update:

Java代码   北京联动北方科技有限公司


  1. insert into TBL_USER (name, age, province, city, street, uuid) values (?, ?, ?, ?, ?, ?)  
  2. insert into TBL_USER_GENERAL (realname, gender, birthday, weight, height, uuid) values (?, ?, ?, ?, ?, ?)  
  3. insert into TBL_FARM (name, fk_user_id, uuid) values (?, ?, ?)  



级联删除
1、当删除user时自动删除user下的farm

Java代码   北京联动北方科技有限公司


  1. user = (UserModel) session.get(UserModel.class, 1);  
  2. session.delete(user);  



结果:

Java代码   北京联动北方科技有限公司


  1. Hibernate: delete from TBL_USER_GENERAL where uuid=?  
  2. Hibernate: delete from TBL_FARM where uuid=?  
  3. Hibernate: delete from TBL_USER where uuid=?  



2、删除user中的farms的一个元素

Java代码   北京联动北方科技有限公司


  1. UserModel user =   
  2. (UserModel) session.get(UserModel.class, 118);  
  3. FarmModel farm = (FarmModel) user.getFarms().toArray()[user.getFarms().size() - 1];  
  4. user.getFarms().remove(farm);//1.必须先从集合删除  
  5. session.delete(farm);//2.然后才能删除  



结果:

Java代码   北京联动北方科技有限公司


  1. Hibernate: delete from TBL_FARM where uuid=?  



如果将子对象从集合中移除,实际上我们是想删除它。要实现这种要求,就必须使用cascade="all-delete-orphan"。无需再调用session.delete(farm)

5.2.2.5、多对多关系映射:不用
为什么不使用多对多:当添加新字段时给谁?
那实际项目如何用:拆成两个一对多。
六、涉及的SQL语句会按照下面的顺序发出执行:
1、查询
1、所有对实体进行插入的语句,其顺序按照对象执行Session.save()的时间顺序
2、所有对实体进行更新的语句

3、所有进行集合插入的语句 (实体类型)
4、所有对集合元素进行删除、更新或插入的语句 (值类型)
5、所有进行集合删除的语句 (实体类型)


6、所有对实体进行删除的语句,其顺序按照对象执行Session.delete()的时间顺序
(有一个例外是,如果对象使用native方式来生成ID(持久化标识)的话,它们一执行save就会被插入。)

七、影响关系映射抓取的cfg配置:

 hibernate.max_fetch_depth


为单向关联(一对一, 多对一)的外连接抓取(outer join fetch)树���置最大深度. 值为0意味着将关闭默认的外连接抓取.
取值 建议在0到3之间取值

 hibernate.default_batch_fetch_size


为Hibernate关联的批量抓取设置默认数量.
取值 建议的取值为4, 8, 和16
如果你的数据库支持ANSI, Oracle或Sybase风格的外连接,  外连接抓取

通常能通过限制往返数据库次数 (更多的工作交由数据库自己来完成)来提高效率. 外连接抓取允许在单个SELECTSQL语句中, 通过many-to-one, one-to-many, many-to-many和one-to-one关联获取连接对象的整个对象图.
将hibernate.max_fetch_depth设为0能在

全局

    范围内禁止外连接抓取. 设为1或更高值能启用one-to-one和many-to-one外连接关联的外连接抓取, 它们通过 fetch="join"来映射. 

八、抓取策略1、抓取策略定义

抓取策略(fetching strategy)

    是指:当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候, Hibernate如何获取关联对象的策略。抓取策略可以在O/R映射的元数据中声明,也可以在特定的HQL 或条件查询(Criteria Query)中重载声明。
2、Hibernate3 定义了如下几种抓取策略:

连接抓取(Join fetching)

- Hibernate通过 在SELECT语句使用OUTER JOIN(外连接)来 获得对象的关联实例或者关联集合。 默认非延迟加载

  集合抓取需要通过配置fetch="join"来指定。下行数据太多(冗余),IO

Java代码   北京联动北方科技有限公司


  1. //配置 fetch="join"( lazy="true"不起作用了)  
  2. session.get(UserModel.class, 118);//是获取对象的  
  3. Hibernate: select … from TBL_USER usermodel0_, TBL_FARM farms1_   
  4. where usermodel0_.uuid=farms1_.fk_user_id(+) and usermodel0_.uuid=?  


    查询抓取(Select fetching

    - 另外发送一条 SELECT 语句抓取当前对象的关联实体或集合。除非你显式的指定lazy="false"禁止延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,��会执行第二条select语句。 

Java代码   北京联动北方科技有限公司


  1. ////配置 lazy=”true”默认(或者lazy="false" fetch="select")  
  2. session.get(UserModel.class, 118);//是获取对象的  
  3. Hibernate: select … from TBL_USER usermodel0_ where usermodel0_.uuid=?  
  4. Hibernate: select … from TBL_FARM farms0_ where farms0_.fk_user_id=?  


默认用于lazy="true"情况的集合抓取,如果lazy="false"需要指定fetch="select"来通过查询抓取。会造成DB的CPU利用率非常高,计算密集

     子查询抓取(Subselect fetching

    - 另外发送一条SELECT 语句抓取在前面查询到(或者抓取到)的所有实体对象的关联集合。除非你显式的指定lazy="false" 禁止延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句。 

当通过Query等接口查询多个实体时,如果指定fetch="subselect"则将通过子查询获取集合     

Java代码   北京联动北方科技有限公司


  1. ////配置fetch="subselect"  
  2. Query q = session.createQuery("from UserModel");  
  3. System.out.println(q.list());  
  4. Hibernate: select …… from TBL_USER usermodel0_  
  5. Hibernate: select …… from TBL_FARM farms0_ where farms0_.fk_user_id   
  6. in (select usermodel0_.uuid from TBL_USER usermodel0_)  


    批量抓取(Batch fetching)

- 对查询抓取的优化方案, 通过指定一个主键或外键列表,Hibernate使用单条SELECT语句获取一批对象实例或集合。

当通过Query等接口查询多个实体时,如果指定farm的batch-size="……"则将通过

使用单条SELECT语句获取一批对象实例或集合



Java代码   北京联动北方科技有限公司


  1. Query q = session.createQuery("from UserModel");  
  2. List<UserModel> userList = q.list();            System.out.println(userList);  
  3. Hibernate: select … TBL_USER usermodel0_  
  4. Hibernate: select … from TBL_FARM farms0_ where farms0_.fk_user_id in (?, ?)  



可指定全局批量抓取策略: hibernate.default_batch_fetch_size,取值:建议的取值为4, 8, 和16。
如果batch-size= "4"

,而某个user有19个农场,Hibernate将只需要执行五次查询,分别为4、4、4、4、3。
  测试必须数据量足够多,,如果只有一条不行

3、使用延迟属性抓取(Using lazy property fetching)
属性的延迟载入要求在其代码构建时加入二进制指示指令(bytecode instrumentation),如果你的持久类代码中未含有这些指令, Hibernate将会忽略这些属性的延迟设置,仍然将其直接载入。
Hibernate3对单独的属性支持延迟抓取,这项优化技术也被称为

组抓取(fetch groups)

。 请注意,该技术更多的属于市场特性。在实际应用中,优化行读取比优化列读取更重要。但是,仅载入类的部分属性在某些特定情况下会有用,例如在原有表中拥有几百列数据、数据模型无法改动的情况下。

4、Hibernate在抓取时会lazy区分下列各种情况:
立即抓取 - 当宿主被加载时,关联、集合或属性被立即抓取。
   Lazy collection fetching,延迟集合抓取- 直到应用程序对集合进行了一次操作时,集合才被抓取。(对集合而言这是默认行为。)
Extra-lazy" collection fetching,"Extra-lazy"集合抓取 -对集合类中的每个元素而言,都是���到需要时才去访问数据库。除非绝对必要,Hibernate不会试图去把整个集合都抓取到内存里来(适用于非常大的集合)。

Java代码   北京联动北方科技有限公司


  1. // lazy="extra"  
  2. Query q = session.createQuery("from UserModel");  
  3. Iterator it = q.iterate();            System.out.println(((UserModel)it.next()).getFarms().size());  
  4. //或              
  5. List<UserModel> userList = q.list();            System.out.println(userList.get(0).getFarms().size());  
  6. Hibernate: select usermodel0_.uuid as col_0_0_ from TBL_USER usermodel0_  
  7. Hibernate: select … from TBL_USER usermodel0_ where usermodel0_.uuid=?  
  8. Hibernate: select count(uuid) from TBL_FARM where fk_user_id =?  
  9. //或  
  10. Hibernate: select … from TBL_USER usermodel0_  
  11. Hibernate: select count(uuid) from TBL_FARM where fk_user_id =?  



对于调用size()、contains、isEmpty是一种优化,不读取所有级联,而是按条件生产不同的sql。
   Proxy fetching,代理抓取 - 对返回单值的关联而言,当其某个方法被调用,而非对其关键字进行get操作时才抓取。

Java代码   北京联动北方科技有限公司


  1. //默认 <many-to-one name="user" ……lazy="false"/>  
  2. FarmModel farm = (FarmModel) session.get(FarmModel.class, 121);  
  3. System.out.println(farm.getUser().getUuid());  
  4. Hibernate: select … from TBL_FARM farmmodel0_ where farmmodel0_.uuid=?  
  5. Hibernate: select … from TBL_USER usermodel0_ where usermodel0_.uuid=?  
  6. 118   


Java代码   北京联动北方科技有限公司


  1. // <many-to-one name="user" ……lazy="proxy"/>  
  2. FarmModel farm = (FarmModel) session.get(FarmModel.class, 121);  
  3. System.out.println(farm.getUser().getUuid());  
  4. Hibernate: select … from TBL_FARM farmmodel0_ where farmmodel0_.uuid=?  
  5. 118  



 注: 如果constrained="false"或基于主键的一对一, 不可能使用代理,Hibernate会采取预先抓取!


"No-proxy" fetching,非代理抓取 - 对返回单值的关联而言,当实例变量被访问的时候进行抓取。与上面的代理抓取相比,这种方法没有那么“延迟”得厉害(就算只访问标识符,也会导致关联抓取)但是更加透明,因为对应用程序来说,不再看到proxy。这种方法需要在编译期间进行字节码增强操作,因此很少需要用到。

Lazy attribute fetching,属性延迟加载

    - 对属性或返回单值的关联而言,当其实例变量被访问的时候进行抓取。需要编译期字节码强化,因此这一方法很少是必要的。
这里有两个正交的概念:关联

何时

被抓取,以及被

如何

抓取(会采用什么样的SQL语句)。不要混淆它们!我们使用抓取来改善性能。我们使用延迟来定义一些契约,对某特定类的某个脱管的实例,知道有哪些数据是可以使用的。 

九、抓取优化
1、集合N+1:
可以使用batch-size来减少获取次数,即如batch-size=”10”,则是N/10+1。
      开启二级缓存。
       对于集合比较小且一定会用到的可采用fetch=”join”,这样只需一条语句。
  2、笛卡尔积问题:

Java代码   北京联动北方科技有限公司


  1. <set name="farms" cascade="all,all-delete-orphan" inverse="true" fetch="join">  
  2. <key column="fk_user_id"/>  
  3.  <one-to-many class="cn.javass.h3test.model.FarmModel"/>  
  4. </set>  
  5. <set name="hourses" cascade="all,all-delete-orphan" inverse="true" fetch="join">  
  6. <key column="fk_user_id"/>  
  7.  <one-to-many class="cn.javass.h3test.model.HourseModel"/>  
  8. </set>  



   如上配置产生笛卡尔积问题。

 select user.*,farm.*,hourse.* from UserModel user, FarmModel farm, HourseModel hourse
where user.uuid=farm.fk_user.uuid(+) and
user.uuid=hourse.fk_user.uuid(+)


解决方案:
1、fetch=”subselect”,子查询,每个User查询一套笛卡尔积
2、完全不采用关系映射。
3、大集合采用批处理,按块获取集合数据
            4、复杂SQL太复杂太慢:找DBA优化,索引等是否有效,是否加载了过多的无用数据,拆分SQL,按需获取数据。
            5、按需获取1对多中的集合。
6、缓存

 ……




赞(0)    操作        顶端 
haili.yang
注册用户
等级:少校
经验:936
发帖:71
精华:1
注册:2012-12-24
状态:离线
发送短消息息给haili.yang 加好友    发送短消息息给haili.yang 发消息
发表于: IP:您无权察看 2012-12-27 14:33:28 | [全部帖] [楼主帖] 2  楼

-----------------续主题帖---------------------

六、涉及的SQL语句会按照下面的顺序发出执行:
1、查询
1、所有对实体进行插入的语句,其顺序按照对象执行Session.save()的时间顺序
2、所有对实体进行更新的语句
3、所有进行集合插入的语句 (实体类型)
4、所有对集合元素进行删除、更新或插入的语句 (值类型)
5、所有进行集合删除的语句 (实体类型)
6、所有对实体进行删除的语句,其顺序按照对象执行Session.delete()的时间顺序
(有一个例外是,如果对象使用native方式来生成ID(持久化标识)的话,它们一执行save就会被插入。)

 
七、影响关系映射抓取的cfg配置:

 hibernate.max_fetch_depth


为单向关联(一对一, 多对一)的外连接抓取(outer join fetch)树设置最大深度. 值为0意味着将关闭默认的外连接抓取.
取值 建议在0到3之间取值

 hibernate.default_batch_fetch_size


为Hibernate关联的批量抓取设置默认数量.
取值 建议的取值为4, 8, 和16
如果你的数据库支持ANSI, Oracle或Sybase风格的外连接, 外连接抓取通常能通过限制往返数据库次数 (更多的工作交由数据库自己来完成)来提高效率. 外连接抓取允许在单个SELECTSQL语句中, 通过many-to-one, one-to-many, many-to-many和one-to-one关联获取连接对象的整个对象图.
将hibernate.max_fetch_depth设为0能在全局 范围内禁止外连接抓取. 设为1或更高值能启用one-to-one和many-to-one外连接关联的外连接抓取, 它们通过 fetch="join"来映射. 
八、抓取策略1、抓取策略定义
抓取策略(fetching strategy) 是指:当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候, Hibernate如何获取关联对象的策略。抓取策略可以在O/R映射的元数据中声明,也可以在特定的HQL 或条件查询(Criteria Query)中重载声明。
2、Hibernate3 定义了如下几种抓取策略:
连接抓取(Join fetching) - Hibernate通过 在SELECT语句使用OUTER JOIN(外连接)来 获得对象的关联实例或者关联集合。 默认非延迟加载
集合抓取需要通过配置fetch="join"来指定。下行数据太多(冗余),IO
Java代码 北京联动北方科技有限公司//配置 fetch="join"( lazy="true"不起作用了)  

session.get(UserModel.class, 118);//是获取对象的  
Hibernate: select … from TBL_USER usermodel0_, TBL_FARM farms1_
where usermodel0_.uuid=farms1_.fk_user_id(+) and usermodel0_.uuid=?


  查询抓取(Select fetching) - 另外发送一条 SELECT 语句抓取当前对象的关联实体或集合。除非你显式的指定lazy="false"禁止延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句。 
Java代码北京联动北方科技有限公司

////配置 lazy=”true”默认(或者lazy="false" fetch="select")  
session.get(UserModel.class, 118);//是获取对象的  
Hibernate: select … from TBL_USER usermodel0_ where usermodel0_.uuid=?
Hibernate: select … from TBL_FARM farms0_ where farms0_.fk_user_id=?


默认用于lazy="true"情况的集合抓取,如果lazy="false",需要指定fetch="select"来通过查询抓取。会造成DB的CPU利用率非常高,计算密集

     子查询抓取(Subselect fetching) - 另外发送一条SELECT 语句抓取在前面查询到(或者抓取到)的所有实体对象的关联集合。除非你显式的指定lazy="false" 禁止延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句。 
当通过Query等接口查询多个实体时,如果指定fetch="subselect"则将通过子查询获取集合     
Java代码北京联动北方科技有限公司

////配置fetch="subselect"  
Query q = session.createQuery("from UserModel");
System.out.println(q.list());
Hibernate: select …… from TBL_USER usermodel0_
Hibernate: select …… from TBL_FARM farms0_ where farms0_.fk_user_id
in (select usermodel0_.uuid from TBL_USER usermodel0_)


   批量抓取(Batch fetching) - 对查询抓取的优化方案, 通过指定一个主键或外键列表,Hibernate使用单条SELECT语句获取一批对象实例或集合。
当通过Query等接口查询多个实体时,如果指定farm的batch-size="……"则将通过使用单条SELECT语句获取一批对象实例或集合。
Java代码北京联动北方科技有限公司

Query q = session.createQuery("from UserModel");
List<UserModel> userList = q.list();            System.out.println(userList);
Hibernate: select … TBL_USER usermodel0_
Hibernate: select … from TBL_FARM farms0_ where farms0_.fk_user_id in (?, ?)


可指定全局批量抓取策略: hibernate.default_batch_fetch_size,取值:建议的取值为4, 8, 和16。
如果batch-size="4",而某个user有19个农场,Hibernate将只需要执行五次查询,分别为4、4、4、4、3。
  测试必须数据量足够多,,如果只有一条不行

3、使用延迟属性抓取(Using lazy property fetching)
属性的延迟载入要求在其代码构建时加入二进制指示指令(bytecode instrumentation),如果你的持久类代码中未含有这些指令, Hibernate将会忽略这些属性的延迟设置,仍然将其直接载入。
Hibernate3对单独的属性支持延迟抓取,这项优化技术也被称为组抓取(fetch groups)。 请注意,该技术更多的属于市场特性。在实际应用中,优化行读取比优化列读取更重要。但是,仅载入类的部分属性在某些特定情况下会有用,例如在原有表中拥有几百列数据、数据模型无法改动的情况下。

4、Hibernate在抓取时会lazy区分下列各种情况:
立即抓取 - 当宿主被加载时,关联、集合或属性被立即抓取。
   Lazy collection fetching,延迟集合抓取- 直到应用程序对集合进行了一次操作时,集合才被抓取。(对集合而言这是默认行为。)
Extra-lazy" collection fetching,"Extra-lazy"集合抓取 -对集合类中的每个元素而言,都是直到需要时才去访问数据库。除非绝对必要,Hibernate不会试图去把整个集合都抓取到内存里来(适用于非常大的集合)。
Java代码北京联动北方科技有限公司

// lazy="extra"
Query q = session.createQuery("from UserModel");
Iterator it = q.iterate();            System.out.println(((UserModel)it.next()).getFarms().size());
//或              
List<UserModel> userList = q.list();            System.out.println(userList.get(0).getFarms().size());
Hibernate: select usermodel0_.uuid as col_0_0_ from TBL_USER usermodel0_
Hibernate: select … from TBL_USER usermodel0_ where usermodel0_.uuid=?
Hibernate: select count(uuid) from TBL_FARM where fk_user_id =?
//或  
Hibernate: select … from TBL_USER usermodel0_
Hibernate: select count(uuid) from TBL_FARM where fk_user_id =?


对于调用size()、contains、isEmpty是一种优化,不读取所有级联,而是按条件生产不同的sql。
   Proxy fetching,代理抓取 - 对返回单值的关联而言,当其某个方法被调用,而非对其关键字进行get操作时才抓取。
Java代码北京联动北方科技有限公司

//默认 <many-to-one name="user" ……lazy="false"/>  
FarmModel farm = (FarmModel) session.get(FarmModel.class, 121);
System.out.println(farm.getUser().getUuid());
Hibernate: select … from TBL_FARM farmmodel0_ where farmmodel0_.uuid=?
Hibernate: select … from TBL_USER usermodel0_ where usermodel0_.uuid=?
118


Java代码北京联动北方科技有限公司

// <many-to-one name="user" ……lazy="proxy"/>
FarmModel farm = (FarmModel) session.get(FarmModel.class, 121);
System.out.println(farm.getUser().getUuid());
Hibernate: select … from TBL_FARM farmmodel0_ where farmmodel0_.uuid=?
118


 注:如果constrained="false"或基于主键的一对一, 不可能使用代理,Hibernate会采取预先抓取!
"No-proxy" fetching,非代理抓取 - 对返回单值的关联而言,当实例变量被访问的时候进行抓取。与上面的代理抓取相比,这种方法没有那么“延迟”得厉害(就算只访问标识符,也会导致关联抓取)但是更加透明,因为对应用程序来说,不再看到proxy。这种方法需要在编译期间进行字节码增强操作,因此很少需要用到。
Lazy attribute fetching,属性延迟加载 - 对属性或返回单值的关联而言,当其实例变量被访问的时候进行抓取。需要编译期字节码强化,因此这一方法很少是必要的。
这里有两个正交的概念:关联何时被抓取,以及被如何抓取(会采用什么样的SQL语句)。不要混淆它们!我们使用抓取来改善性能。我们使用延迟来定义一些契约,对某特定类的某个脱管的实例,知道有哪些数据是可以使用的。 

九、抓取优化
1、集合N+1:
可以使用batch-size来减少获取次数,即如batch-size=”10”,则是N/10+1。
      开启二级缓存。
       对于集合比较小且一定会用到的可采用fetch=”join”,这样只需一条语句。
  2、笛卡尔积问题:
Java代码北京联动北方科技有限公司

<set name="farms" cascade="all,all-delete-orphan" inverse="true" fetch="join">
<key column="fk_user_id"/>
<one-to-many class="cn.javass.h3test.model.FarmModel"/>
</set>
<set name="hourses" cascade="all,all-delete-orphan" inverse="true" fetch="join">
<key column="fk_user_id"/>
<one-to-many class="cn.javass.h3test.model.HourseModel"/>
</set>


   如上配置产生笛卡尔积问题。

select user.*,farm.*,hourse.* from UserModel user, FarmModel farm, HourseModel hourse
where user.uuid=farm.fk_user.uuid(+) and
user.uuid=hourse.fk_user.uuid(+)


解决方案:
1、fetch=”subselect”,子查询,每个User查询一套笛卡尔积
2、完全不采用关系映射。
3、大集合采用批处理,按块获取集合数据
            4、复杂SQL太复杂太慢:找DBA优化,索引等是否有效,是否加载了过多的无用数据,拆分SQL,按需获取数据。
            5、按需获取1对多中的集合。
6、缓存

……

该贴被yhl.admin编辑于2012-12-27 14:40:23



赞(0)    操作        顶端 
总帖数
2
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论