??xml version="1.0" encoding="utf-8" standalone="yes"?> Join fetching : Hibernate取关联数据或集合是通过OUTER JOIN的方式,通过同一条select 语句来实现?/p>
Select fetching:在没有指定了(jin)lazy = "false"Q既延迟加蝲有效Q的情况下,通过另一条select 语句来获得与已经获得的实体相关的实体或集合。当?br />q种情况发生在用L(fng)正要获得兌对象的时候?/p>
Subselect fetching:在没有指定了(jin)lazy = "false"Q既延迟加蝲有效Q的情况下,先通过一条查询语句获得了(jin)一个实体集Q?然后对这个实体集中的每一个对象通过另一条select 语句来获得与它相关的实体或集合?br />当然q种情况发生在用L(fng)正要获得兌对象的时候?/p>
Batch fetching Q它是ؓ(f)查询数据提供的一U优化策略。通过指定主键或外键的列表的方式来实现一条select 语句获得一批实体或集合?/p>
从另一个角度来看,hibernate的fetching 分成以下几种?/p>
Immediate fetching: 如果实体已经被加载了(jin)Q他的关联对象,兌集合Q属性也要及(qing)时加栽?/p>
lazy collection fetching: 只有应用E序真正使用q个集合的时候,才加栽这个集合?/p>
"Extra-lazy" collection fetching : hibernate 不加载一个集合的所有对象到内存里,需要哪个个体,加蝲哪个?/p>
Proxy fetching :当前对象的单值相兛_象只有在调用它的主键外的其他属性的getҎ(gu)时才加蝲它?/p>
"NO-proxy"fetching :当前对象的单值相兛_象在它的实体变量被访问的时候就被加载。相对于Proxy fetching来说QProxy fetching更gq?br />Q因?NO-proxy"fetching即是访问关联对象的dQ关联对象都要被加蝲Q?NO-proxy"fetching对于应用来说更条理清晰。因为在应用 Lazy attribute fetching Q当前对象的某个属性或单值相兛_象只有在与它对应的实体变量被讉K的时候才加蝲?/p>
Working with lazy associations 如果你设|了(jin)hibernate.default_batch_fetch_size,Hibernate׃(x)通过扚w获取来优化lazy fetching. lazy fetching ?x)引起一个问题。就是关闭了(jin)hibernate session以后加蝲延迟加蝲的对象。这样会(x)引v异常。如下:(x) s = sessions.openSession(); ׃在session被关闭之前,permissions 没有被初始化Q所以它的数据没有被加蝲。hibernate不支持已l被分离的对?br />的gq加载。修改的Ҏ(gu)是把相关代码Udtx.commit()之前?/p>
或者我们可以在配置文g里通过在关联对象那里指?lazy="false"来兌集合或对象不被gq加载。但是如果你定义太多?br />非gq加载对象,hibernate 在一ơ事务中可以需要把整个数据库加载到内存?sh)?/p>
从另一个角度来_(d)在一ơ事务中Q我们经怋用joint fetching q种方式Q它天生׃是gq加载)(j)来代替select fetching q种方式?br />下边我们p看到怎么自定?取策略。在hibernate3?单值和集合兌对象的取{略的指定方式是一致的?/p>
Tuning fetch strategies 一般来_(d)我们不是通过在映配|文件自定义取策略,而是通过在一个事务里Q通过在特定的HQL里?left join 来覆盖默认的?br />{略。对于Criteria 来说Q提供了(jin)setFetchMode(FetchMode.JOIN) API.如下Q?/p>
User user = (User) session.createCriteria(User.class) 另一U完全不同的避免N+1ơselects 的方式是使用second-level cache. Single-ended association proxies 默认的情况下,Hibernate3为所有的持久化类生成代理,通过q些代理来完?many-to-one ? 在映文件中可以为类声明一个接口做为它的代理接?通过proxy属性指定。实际上,hibernate真正代理的是 <class name="Cat" proxy="Cat"> 首先要注意的是,Cat的实例不能当作DomesticCat实例使用。即使Cat和DomesticCat对应的是同一条数据?br />Cat cat = (Cat) session.load(Cat.class, id); // instantiate a proxy (does not hit the db) 其次Q两者之间不能?= 实际情况q如我们看到的那么p糕。即使我们引用了(jin)两个不同的代理对象,实际的对象却是相同的?br />cat.setWeight(11.0); // hit the db to initialize the proxy q需注意的是如果一个类是final class,或者它有finalҎ(gu)。我们就不能使用CGLIB代理. 最后,如果你的持久化对象在实例化的q程中获得的M资源(例如 在initializers或者默认的构造函数里),q些 q些问题的根源是java不能多重l承.如果你想避免q些问题,你应该让每一个类(子类和父c?实现一个声明了(jin)业务Ҏ(gu)的接? Cat cat = (Cat) session.load(CatImpl.class, catid); 以下Ҏ(gu)不需要代理的初始倹{?br />equals() (tng) 此方法没有被覆盖的时候?br />hashCode() 此方法没有被覆盖的时候?br />主键对应的getҎ(gu)?/p>
Initializing collections and proxies 有时候我们需要在session关闭之前保一个代理或集合被初始化。当然我们可以通过cat.getSex()或cat.getKittents().size() ?rn)态方法Hibernate.initialize() 和Hibernate.isInitialized()为应用提供了(jin)处理延迟加蝲集合或代理的一U便h式?br />Hibernate.initialize(Cat) ?x)强制加载代?cat. Hibernate.initialize(cat.getKittens())初始化kittens集合。当然这些方法要?br />session关闭之前执行?/p>
另一U方式是在所有需要的集合和代理对象都被加载之后再关闭session. 在一些应用中Q尤其是当应用用hibernate来获?br />数据Q却在其他的应用层处理这些数据。或是这些数据是在其他的处理q程中用。ؓ(f)?jin)确保这些集合在初始化的时候session 1 Z web 的应用可以通过filter 在一ơ请求的最后关闭session.当然q样做是Z你的应用可以正确处理异常。非帔R要的一Ҏ(gu)要确保把信息q回l用户之前把事务l束和把sessionx(chng)Q即使是在你的页面处理发生异常的情况下?spring 的OpenSessionInViewFilter是Z此开发出来的) 2 如果你的应用有一个单独的业务层。在业务逻辑q里要保证在q回lweb 层信息之前完成所有的集合初始化工作。这意味着你的 3 你也可以在访问没有初试化的集合(或代理)(j)之前把先前加载的一个对象通过merge()或lock()攑ֈ新的Session里。但是hibernate ?br />?x)也不应该自动完成这L(fng)工作Q因样需要用特D的事务处理语法?/p>
有时候,你需要获得集合中数据的个敎ͼ或者集合数据的一部分׃需要初始话整个集合。你可以通过Collection filter来获得集合中数据 Using batch fetching 扚w获取c?实体Ҏ(gu)理解Q假设有如下情况Q?br /> (tng) (tng) (tng) (tng) (tng) 在你的session里加载了(jin)25个Cat实例。每一个Cat都有一个own的引用指向一个person.在这里这个关联的person是通过代理的方式gq加?br /> (tng)Q单值关联对象)(j)。如果你现在要通过循环调用所有cat的getOwner()Ҏ(gu)。hibernate?x)默认的执?5个select 语句来获得被代理的owner对象?br />我们可以通过在Personq个表的映射文g中指定batch-size来实现批量取数据?br /><class name="Person" batch-size="10">...</class> 你也可以寚w合进行批量取操作.例如,每一个person都有一个被延迟加蝲的集合Cats.现在在session中已l加载了(jin)10?person实例.循环调用 Using subselect fetching Using lazy property fetching Z(jin)使某个属性被延迟加蝲Q只需要在q个属性的影射文g中加上lazy属性即可?/p>
<class name="Document"> 属性的延迟加蝲需要用运行时的字节设备来处理。如果你的持久化c还没有被这个设备处理。hibernate ?x)忽略这个设|?br />采用?qing)时加蝲的方式?/p>
要想使用此字节设备处理持久化c,使用如下的Ant d?/p>
<target name="instrument" depends="compile"> 另一U避免加载不需要的列的方式Q至在只读事务中,是通过使用HQL或Criteria查询属性。这样可以避免用字?br />处理工具?/p>
你可以通过在HQL指定fetch all properties 来加载全部属性?/p>
Fetching stategies是指hibernate在需要关联数据的时候所采用的取兌数据的策略。这个策略既可以在O/R映射文g里配Q也可以通过Ҏ(gu)?br />HQL:或Criteria语句实现?br />Hibernate定义?jin)以下取{略Q?/p>
中没有一个可见的proxy.
(tng) (tng) 个h认ؓ(f)可以q样理解上述情况Q假如在数据库中存在两张表 A,B.表A中有一个指向表Bd的外键。如果想知道A表中的某?br /> (tng) (tng) (tng) 数据对应B表中的那条记录的主键。完全不用访问B表,A表中的此条数据的外键值就是B表中对应数据的主键。所有只有访问B表中
(tng) (tng) (tng) 对应数据的主键外其他属性时Q才需要加载B表中的这条数据?/p>
默认的情况下QHibernate3 在获取关联对象集合的时候用的是lazy{略Q获得单值关联对象的时候用的是lazy proxy{略。这L(fng){略
几乎适用所有的应用?/p>
Transaction tx = s.beginTransaction();
User u = (User) s.createQuery("from User u where u.name=:userName")
.setString("userName", userName).uniqueResult();
Map permissions = u.getPermissions();
tx.commit();
s.close();
Integer accessLevel = (Integer) permissions.get("accounts"); // Error!
默认的select fetching q种取策略很容器DN+1ơselect 操作q样的问题。所以我们可以在配置文g里指定join fetching {略。如下:(x)
Cat对应的配|文Ӟ(x)
<set name="permissions"
fetch="join">
<key column="userId"/>
<one-to-many class="Permission"/>
</set>
Permission对应的配|文Ӟ(x)
<many-to-one name="mother" class="Cat" fetch="join"/>
在映文仉定义的取{略?x)?jing)响如下操作:(x)
?get() 或load()执行的取操作?br /> 操作兌对象而引发的取操作?br /> Criteria查询?br /> 如果使用?jin)subselect q种取策略还?sh)(x)?jing)响HQLq种查询方式?/p>
.setFetchMode("permissions", FetchMode.JOIN)
.add( Restrictions.idEq(userId) )
.uniqueResult();
集合的gq加载是通过Hibernate自己的持久化集合实现?但是对于单个相关对象的gq加?br />需要一个不同的机制.相关的对象必被代理.Hibernate Ҏ(gu)久化对象的代理的延迟加蝲是通过
对运行时字节的动态注入实现的(通过CGLIB实现).
one-to-one 兌对象的gq加?
q个cȝ子类。需要注意的是,被代理的cddC个默认的构造函敎ͼ此构造函数的范围臛_是包内可见的Q?br />推荐所有的持久化类使用q种构造函数。我们现在可以看到的是在cȝ多态的时候会(x)采用q种方式Q?/p>
......
<subclass name="DomesticCat">
.....
</subclass>
</class>
if ( cat.isDomesticCat() ) { // hit the db to initialize the proxy
DomesticCat dc = (DomesticCat) cat; // Error!
....
}
Cat cat = (Cat) session.load(Cat.class, id); // instantiate a Cat proxy
DomesticCat dc =
(DomesticCat) session.load(DomesticCat.class, id); // acquire new DomesticCat proxy!
System.out.println(cat==dc);
System.out.println( dc.getWeight() ); // 11.0
资源也将被proxy获得.实际上代理的是这个类的子cR?/p>
在你的映文件中指定q些接口,如下:
<class name="CatImpl" proxy="Cat">
......
<subclass name="DomesticCatImpl" proxy="DomesticCat">
.....
</subclass>
</class>
CatImpl实现?jin)接口Cat,DomesticCatImpl实现?jin)接口DomesticCat.Cat和DomesticCat实例的代理可?br />被load()或iterator()Ҏ(gu)q回.(list()Ҏ(gu)一般不q回代理).
Iterator iter = session.iterate("from CatImpl as cat where cat.name='fritz'");
Cat fritz = (Cat) iter.next();
关系也被延迟加蝲.q意呌你必dCat中声明所有的属性,而不仅仅是CatImpl.
如果在session的外边访问一个没有初始化的集合或代理Q会(x)抛出一个LazyInitializationException异常。例如在分离?br />状态下Qsession 已经close的情况下Q访问一个实体的延迟加蝲的集合或代理对象?/p>
q种方式来强q初试化。但是这样会(x)使代码阅读者迷茫而且不是一U通用的方便的~码格式?/p>
q处于打开状态,可以通过以下两种方式Q?/p>
业务层需要加载所有的数据q且把这些包括gq加载的数据传给与一个特定的用户h的相兛_现部分。一般来说这是通过在session
关闭之前针对相关的集合调用Hibernate.initialize()Ҏ(gu)或者是采用Criteria 的FetchMode.JOIN 方式。采用命令模式往往比采用session
FacadeҎ(gu)一些?/p>
的个敎ͼ不需要初始化整个集合Q?br />( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()?br />当然Collection filter也可以获取集合的一部分数据
s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();
扚w获取数据可以提高Hibernate的效?扚w获取是gqselect fetching{略的一U优?我们可以对类或者集合两个角度采用批量取数据.
Hibernate 现在?x)执行三条查询语句来完成查询Q模式是10,10,5.
所有的person的getCats()Ҏ(gu)?x)?0条select 语句.如果你在person的映文件中定义?jin)批量获取模?
<class name="Person">
<set name="cats" batch-size="3">
...
</set>
</class>
通过讄batch-size设ؓ(f)3QHibernate ?x)?,3,3,1的模式通过四条select语句加蝲集合?/p>
如果要加载一个gq加载的集合或一个单值的代理QHibernate通过一个subselect q行原来的查询语句,q种情况和batch-fetching是异曲同工的?/p>
Hibernate支持对单个属性的延迟加蝲。这个优化技术也?UCؓ(f)fetch groups. 需要注意的是,q个技术还处于推销阶段。因为在实际中,对行的读?br />优化比对列的优化更重要。然而在一些特D情况下Q加载一个类的部分属性还是有必要的,比如一个承的表有几百列而且数据模型q(sh)能改变?/p>
<id name="id">
<generator class="native"/>
</id>
<property name="name" not-null="true" length="50"/>
<property name="summary" not-null="true" length="200" lazy="true"/>
<property name="text" not-null="true" length="2000" lazy="true"/>
</class>
<taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
<classpath path="${jar.path}"/>
<classpath path="${classes.dir}"/>
<classpath refid="lib.class.path"/>
</taskdef>
<instrument verbose="true">
<fileset dir="${testclasses.dir}/org/hibernate/auction/model">
<include name="*.class"/>
</fileset>
</instrument>
</target>
1 2 3 持久化层的缓存的q发讉K{略
Repeatable Read Read Committed 什么样的数据适合存放到第二~存?sh)?/span>
很少被修改的数据
不是很重要的数据Q允许出现偶?dng)ƈ发的数?/span>
不会(x)被ƈ发访问的数据
不适合存放到第二~存的数据?
l常被修改的数据
财务数据Q绝对不允许出现q发
与其他应用共享的数据?/span>
Hibernate Hibernate Session Session OID SessionFactory SessionFactory Hibernate Hibernate 1) select * from table_name where ? SQL 2) ID 3) Hibernate ID Session ID 4) Hibernate ID Hibernate Query Hibernate Query 1) Hibernate Query Key Query Key SQL, SQL rowStart maxRows)
2)
Hibernate
Query Key
Query
Query Key
Query
3) Query Key
SQL
Query Key
]]>
վ֩ģ壺
|
|
|
ʤ|
|
|
|
|
|
ն|
մ|
|
Ͻ|
ɫ|
|
|
۷|
|
|
|
|
̨|
ľ|
|
ĩ|
|
|
İ|
|
ٰ|
Ļ|
|
ٹ|
|
|
|
κ|
뽭|
|
齭|
佭|