﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-study-随笔分类-Hibernate</title><link>http://www.blogjava.net/xixidabao/category/24795.html</link><description>GROW WITH JAVA</description><language>zh-cn</language><lastBuildDate>Fri, 31 Aug 2007 03:14:31 GMT</lastBuildDate><pubDate>Fri, 31 Aug 2007 03:14:31 GMT</pubDate><ttl>60</ttl><item><title>Hibernate实践 </title><link>http://www.blogjava.net/xixidabao/archive/2007/08/29/140929.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Wed, 29 Aug 2007 07:18:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/08/29/140929.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转:http://www.blogjava.net/BlueDavy/archive/2006/03/27/37582.html一. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 序 在实际项目中使用Hibernate有两年多了，在两年多的实践过程中既体验到了Hibernate带来的N多好处，同...&nbsp;&nbsp;<a href='http://www.blogjava.net/xixidabao/archive/2007/08/29/140929.html'>阅读全文</a><img src ="http://www.blogjava.net/xixidabao/aggbug/140929.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-08-29 15:18 <a href="http://www.blogjava.net/xixidabao/archive/2007/08/29/140929.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>XXXX项目缓存方案总结</title><link>http://www.blogjava.net/xixidabao/archive/2007/08/28/140497.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Tue, 28 Aug 2007 06:35:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/08/28/140497.html</guid><description><![CDATA[<div>转:http://dev.csdn.net/author/bromon/8737868d27c74af7b4dbfc4d0497f4fa.html<br><br><br><br>XXXX项目缓存方案总结</div>
<div>&nbsp;</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XXXX</span>项目是目前在实际工作中正在做的事情，该项目是一个大型系统的内容管理内核，负责最核心的meta data的集中管理，性能有较高的要求，设计初期就要求能够支持cluster。项目使用hibernate 3.2，针对开发过程中对于各种缓存的不同看法，撰写了本文。重点在于澄清一些hibernate的缓存细节，纠正一些错误的缓存用法。</div>
<div>&nbsp;</div>
<div style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt"><span>一、</span>hibernate的二级缓存</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>如果开启了二级缓存，hibernate在执行任何一次查询的之后，都会把得到的结果集放到缓存中，缓存结构可以看作是一个hash table，key是数据库记录的id，value是id对应的pojo对象。当用户根据id查询对象的时候（load、iterator方法），会首先在缓存中查找，如果没有找到再发起数据库查询。但是如果使用hql发起查询（find, query方法）则不会利用二级缓存，而是直接从数据库获得数据，但是它会把得到的数据放到二级缓存备用。也就是说，基于hql的查询，对二级缓存是只写不读的。</div>
<div>&nbsp;</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>针对二级缓存的工作原理，采用iterator取代list来提高二级缓存命中率的想法是不可行的。Iterator的工作方式是根据检索条件从数据库中选取所有目标数据的id，然后用这些id一个一个的到二级缓存里面做检索，如果找到就直接加载，找不到就向数据库做查询。因此假如iterator检索100条数据的话，最好情况是100%全部命中，最坏情况是0%命中，执行101条sql把所有数据选出来。而list虽然不利用缓存，但是它只会发起1条sql取得所有数据。在合理利用分页查询的情况下，list整体效率高于iterator。</div>
<div>&nbsp;</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>二级缓存的失效机制由hibernate控制，当某条数据被修改之后，hibernate会根据它的id去做缓存失效操作。基于此机制，如果数据表不是被hibernate独占（比如同时使用jdbc或者ado等），那么二级缓存无法得到有效控制。</div>
<div>&nbsp;</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>由于hibernate的缓存接口很灵活，cache provider可以方便的切换，因此支持cluster环境不是大问题，通过使用swarmcache、jboss cache等支持分布式的缓存方案，可以实现。但是问题在于:</div>
<div style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt"><span>1、&nbsp;</span>分布式缓存本身成本偏高（比如使用同步复制模式的jboss cache）</div>
<div style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt"><span>2、&nbsp;</span>分布式环境通常对事务控制有较高要求，而目前的开源缓存方案对事务缓存（transaction cache）支持得不够好。当jta事务发生会滚，缓存的最后更新结果很难预料。这一点会带来很大的部署成本，甚至得不偿失。</div>
<div style="MARGIN-LEFT: 21pt">&nbsp;</div>
<div>结论：XXXX不应把hibernate二级缓存作为优化的主要手段，一般情况下建议不要使用。</div>
<div>&nbsp;</div>
<div>原因如下：</div>
<div style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt"><span>1、&nbsp;</span>XXXX的DAO类大部分是从1.0升级过来，由于1.0采用的是hibernate 2.1，所以在批量删除数据的时候采用了native sql的方式。虽然XXXX2.0已经完全升级到hibernate 3.2，支持hibernate原生的批量删改，但是由于hibernate批量操作的性能不如sql，而且为了兼容1.0的dao类，所以很多地方保留了sql操作。哪些数据表是单纯被hibernate独占无法统计，而且随着将来业务的发展可能会有很大变数。因此不宜采用二级缓存。</div>
<div style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt"><span>2、&nbsp;</span>针对系统业务来说，基于id检索的二级缓存命中率极为有限，hql被大量采用，二级缓存对性能的提升很有限。</div>
<div style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt"><span>3、&nbsp;</span>hibernate 3.0在做批量修改、批量更新的时候，是不会同步更新二级缓存的，该问题在hibernate 3.2中是否仍然存在尚不确定。</div>
<div style="MARGIN-LEFT: 21pt">&nbsp;</div>
<div>&nbsp;</div>
<div style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt"><span>二、</span>hibernate的查询缓存</div>
<div>&nbsp;</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>查询缓存的实现机制与二级缓存基本一致，最大的差异在于放入缓存中的key是查询的语句，value是查询之后得到的结果集的id列表。表面看来这样的方案似乎能解决hql利用缓存的问题，但是需要注意的是，构成key的是：hql生成的sql、sql的参数、排序、分页信息等。也就是说如果你的hql有小小的差异，比如第一条hql取1-50条数据，第二条hql取20-60条数据，那么hibernate会认为这是两个完全不同的key，无法重复利用缓存。因此利用率也不高。</div>
<div>&nbsp;</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>另外一个需要注意的问题是，查询缓存和二级缓存是有关联关系的，他们不是完全独立的两套东西。假如一个查询条件hql_1，第一次被执行的时候，它会从数据库取得数据，然后把查询条件作为key，把返回数据的所有id列表作为value（请注意仅仅是id）放到查询缓存中，同时整个结果集放到class缓存（也就是二级缓存），key是id，value是pojo对象。当你再次执行hql_1，它会从缓存中得到id列表，然后根据这些列表一个一个的到class缓存里面去找pojo对象，如果找不到就向数据库发起查询。也就是说，如果二级缓存配置了超时时间（或者发呆时间），就有可能出现查询缓存命中了，获得了id列表，但是class里面相应的pojo已经因为超时(或发呆)被失效，hibernate就会根据id清单，一个一个的去向数据库查询，有多少个id，就执行多少个sql。该情况将导致性能下降严重。</div>
<div>&nbsp;</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>查询缓存的失效机制也由hibernate控制，数据进入缓存时会有一个timestamp，它和数据表的timestamp对应。当hibernate环境内发生save、update等操作时，会更新被操作数据表的timestamp。用户在获取缓存的时候，一旦命中就会检查它的timestamp是否和数据表的timestamp匹配，如果不，缓存会被失效。因此查询缓存的失效控制是以数据表为粒度的，只要数据表中任何一条记录发生一点修改，整个表相关的所有查询缓存就都无效了。因此查询缓存的命中率可能会很低。</div>
<div>&nbsp;</div>
<div>结论：XXXX不应把hibernate二级缓存作为优化的主要手段，一般情况下建议不要使用。</div>
<div>&nbsp;</div>
<div>原因如下：</div>
<div style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt"><span>1、&nbsp;</span>XXXX的上层业务中检索条件都比较复杂，尤其是涉及多表操作的地方。很少出现重复执行一个排序、分页、参数一致的查询，因此命中率很难提高。</div>
<div style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt"><span>2、&nbsp;</span>查询缓存必须配合二级缓存一起使用，否则极易出现1+N的情况，否则性能不升反降</div>
<div style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt"><span>3、&nbsp;</span>使用查询缓存必须在执行查询之前显示调用Query.setCacheable(true)才能激活缓存，这势必会对已有的hibernate封装类带来问题。</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>总结</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>详细分析hibernate的二级缓存和查询缓存之后，针对XXXX项目的具体情况做出结论，在底层使用通用缓存方案的想法基本上是不可取的。比较好的做法是在高层次中（业务逻辑层面），针对具体的业务逻辑状况手动使用数据缓存，不仅可以完全控制缓存的生命周期，还可以针对业务具体调整缓存方案提交命中率。Cluster中的缓存同步可以完全交给缓存本身的同步机制来完成。比如开源缓存swarmcache采用invalidate的机制，可以根据用户指定的策略，在需要的时候向网络中的其他swarmcache节点发送失效消息，这一机制和XXXX1.0中已经采用的MappingCache的同步方案基本一致。建议采用。</div>
<img src ="http://www.blogjava.net/xixidabao/aggbug/140497.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-08-28 14:35 <a href="http://www.blogjava.net/xixidabao/archive/2007/08/28/140497.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate二级缓存攻略</title><link>http://www.blogjava.net/xixidabao/archive/2007/04/08/109196.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sun, 08 Apr 2007 02:04:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/04/08/109196.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<div id=contitle>
<h1>Hibernate二级缓存攻略</h1>
</div>
<div id=conauthor><span>2006-10-27 15:59 </span><span>作者： AreYouOK </span><span>出处： JAVAEYE </span><span>责任编辑：<a title=向本编辑提问 href="http://comments.yesky.com/t/%B7%BD%D6%DB/6,324/2612396.shtml" target=_blank>方舟</a> </span></div>
<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 很多人对二级缓存都不太了解，或者是有错误的认识，我一直想写一篇文章介绍一下hibernate的二级缓存的，今天终于忍不住了。 <br><br>　　我的经验主要来自hibernate2.1版本，基本原理和3.0、3.1是一样的，请原谅我的顽固不化。 <br><br>　　hibernate的session提供了一级缓存，每个session，对同一个id进行两次load，不会发送两条sql给数据库，但是session关闭的时候，一级缓存就失效了。<br><br>　　二级缓存是SessionFactory级别的全局缓存，它底下可以使用不同的缓存类库，比如ehcache、oscache等，需要设置hibernate.cache.provider_class，我们这里用ehcache，在2.1中就是 hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider如果使用查询缓存，加上hibernate.cache.use_query_cache=true<br><br>　　缓存可以简单的看成一个Map，通过key在缓存里面找value。<br><br>　　<strong>Class的缓存</strong> <br><br>　　对于一条记录，也就是一个PO来说，是根据ID来找的，缓存的key就是ID，value是POJO。无论list，load还是iterate，只要读出一个对象，都会填充缓存。但是list不会使用缓存，而iterate会先取数据库select id出来，然后一个id一个id的load，如果在缓存里面有，就从缓存取，没有的话就去数据库load。假设是读写缓存，需要设置： <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>＜cache usage="read-write"/＞ </td>
        </tr>
    </tbody>
</table>
<br>　　如果你使用的二级缓存实现是ehcache的话，需要配置ehcache.xml <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>＜cache name="com.xxx.pojo.Foo" maxElementsInMemory="500" eternal="false" timeToLiveSeconds="7200" timeToIdleSeconds="3600" overflowToDisk="true" /＞ </td>
        </tr>
    </tbody>
</table>
<br>　　其中eternal表示缓存是不是永远不超时，timeToLiveSeconds是缓存中每个元素（这里也就是一个POJO）的超时时间，如果eternal="false"，超过指定的时间，这个元素就被移走了。timeToIdleSeconds是发呆时间，是可选的。当往缓存里面put的元素超过500个时，如果overflowToDisk="true"，就会把缓存中的部分数据保存在硬盘上的临时文件里面。 <br><br>　　每个需要缓存的class都要这样配置。如果你没有配置，hibernate会在启动的时候警告你，然后使用defaultCache的配置，这样多个class会共享一个配置。 <br><br>　　当某个ID通过hibernate修改时，hibernate会知道，于是移除缓存。 <br><br>　　这样大家可能会想，同样的查询条件，第一次先list，第二次再iterate，就可以使用到缓存了。实际上这是很难的，因为你无法判断什么时候是第一次，而且每次查询的条件通常是不一样的，假如数据库里面有100条记录，id从1到100，第一次list的时候出了前50个id，第二次iterate的时候却查询到30至70号id，那么30-50是从缓存里面取的，51到70是从数据库取的，共发送1+20条sql。所以我一直认为iterate没有什么用，总是会有1+N的问题。 <br><br>　　（题外话：有说法说大型查询用list会把整个结果集装入内存，很慢，而iterate只select id比较好，但是大型查询总是要分页查的，谁也不会真的把整个结果集装进来，假如一页20条的话，iterate共需要执行21条语句，list虽然选择若干字段，比iterate第一条select id语句慢一些，但只有一条语句，不装入整个结果集hibernate还会根据数据库方言做优化，比如使用mysql的limit，整体看来应该还是list快。） <br><br>　　如果想要对list或者iterate查询的结果缓存，就要用到查询缓存了<br><br>　　<strong>查询缓存 </strong><br><br>　　首先需要配置hibernate.cache.use_query_cache=true <br><br>　　如果用ehcache，配置ehcache.xml，注意hibernate3.0以后不是net.sf的包名了：<br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>＜cache name="net.sf.hibernate.cache.StandardQueryCache" <br>maxElementsInMemory="50" eternal="false" timeToIdleSeconds="3600" <br>timeToLiveSeconds="7200" overflowToDisk="true"/＞ <br>＜cache name="net.sf.hibernate.cache.UpdateTimestampsCache" <br>maxElementsInMemory="5000" eternal="true" overflowToDisk="true"/＞ </td>
        </tr>
    </tbody>
</table>
<br>　　然后 <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>query.setCacheable(true);//激活查询缓存 <br>query.setCacheRegion("myCacheRegion");//指定要使用的cacheRegion，可选 </td>
        </tr>
    </tbody>
</table>
<br>　　第二行指定要使用的cacheRegion是myCacheRegion，即你可以给每个查询缓存做一个单独的配置，使用setCacheRegion来做这个指定，需要在ehcache.xml里面配置它： <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>＜cache name="myCacheRegion" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" overflowToDisk="true" /＞ </td>
        </tr>
    </tbody>
</table>
<br>　　如果省略第二行，不设置cacheRegion的话，那么会使用上面提到的标准查询缓存的配置，也就是：net.sf.hibernate.cache.StandardQueryCache<br><br>　　对于查询缓存来说，缓存的key是根据hql生成的sql，再加上参数，分页等信息（可以通过日志输出看到，不过它的输出不是很可读，最好改一下它的代码）。 <br><br>　　比如hql： <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>from Cat c where c.name like ? </td>
        </tr>
    </tbody>
</table>
<br>　　生成大致如下的sql： <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>select * from cat c where c.name like ? </td>
        </tr>
    </tbody>
</table>
<br>　　参数是"tiger%"，那么查询缓存的key*大约*是这样的字符串（我是凭记忆写的，并不精确，不过看了也该明白了）： <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>select * from cat c where c.name like ? , parameter:tiger% </td>
        </tr>
    </tbody>
</table>
<br>　　这样，保证了同样的查询、同样的参数等条件下具有一样的key。 <br><br>　　现在说说缓存的value，如果是list方式的话，value在这里并不是整个结果集，而是查询出来的这一串ID。也就是说，不管是list方法还是iterate方法，第一次查询的时候，它们的查询方式很它们平时的方式是一样的，list执行一条sql，iterate执行1+N条，多出来的行为是它们填充了缓存。但是到同样条件第二次查询的时候，就都和iterate的行为一样了，根据缓存的key去缓存里面查到了value，value是一串id，然后在到class的缓存里面去一个一个的load出来。这样做是为了节约内存。 <br><br>　　可以看出来，查询缓存需要打开相关类的class缓存。list和iterate方法第一次执行的时候，都是既填充查询缓存又填充class缓存的。 <br>这里还有一个很容易被忽视的重要问题，即打开查询缓存以后，即使是list方法也可能遇到1+N的问题！相同条件第一次list的时候，因为查询缓存中找不到，不管class缓存是否存在数据，总是发送一条sql语句到数据库获取全部数据，然后填充查询缓存和class缓存。但是第二次执行的时候，问题就来了，如果你的class缓存的超时时间比较短，现在class缓存都超时了，但是查询缓存还在，那么list方法在获取id串以后，将会一个一个去数据库load！因此，class缓存的超时时间一定不能短于查询缓存设置的超时时间！如果还设置了发呆时间的话，保证class缓存的发呆时间也大于查询的缓存的生存时间。这里还有其他情况，比如class缓存被程序强制evict了，这种情况就请自己注意了。<br><br>　　另外，如果hql查询包含select字句，那么查询缓存里面的value就是整个结果集了。<br><br>　　当hibernate更新数据库的时候，它怎么知道更新哪些查询缓存呢？ <br><br>　　hibernate在一个地方维护每个表的最后更新时间，其实也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的缓存配置里面。 <br><br>　　当通过hibernate更新的时候，hibernate会知道这次更新影响了哪些表。然后它更新这些表的最后更新时间。每个缓存都有一个生成时间和这个缓存所查询的表，当hibernate查询一个缓存是否存在的时候，如果缓存存在，它还要取出缓存的生成时间和这个缓存所查询的表，然后去查找这些表的最后更新时间，如果有一个表在生成时间后更新过了，那么这个缓存是无效的。 <br><br>　　可以看出，只要更新过一个表，那么凡是涉及到这个表的查询缓存就失效了，因此查询缓存的命中率可能会比较低。<br><br><strong>Collection缓存 <br><br></strong>　　需要在hbm的collection里面设置：<br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>＜cache usage="read-write"/＞ </td>
        </tr>
    </tbody>
</table>
<br>　　假如class是Cat，collection叫children，那么ehcache里面配置 <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>＜cache name="com.xxx.pojo.Cat.children" <br>maxElementsInMemory="20" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" <br>overflowToDisk="true" /＞ </td>
        </tr>
    </tbody>
</table>
<br>　　Collection的缓存和前面查询缓存的list一样，也是只保持一串id，但它不会因为这个表更新过就失效，一个collection缓存仅在这个collection里面的元素有增删时才失效。 <br><br>　　这样有一个问题，如果你的collection是根据某个字段排序的，当其中一个元素更新了该字段时，导致顺序改变时，collection缓存里面的顺序没有做更新。<br><br>　　<strong>缓存策略</strong> <br><br>　　只读缓存（read-only）：没有什么好说的 <br><br>　　读/写缓存（read-write）:程序可能要的更新数据 <br><br>　　不严格的读/写缓存（nonstrict-read-write）：需要更新数据，但是两个事务更新同一条记录的可能性很小，性能比读写缓存好 <br>事务缓存（transactional）：缓存支持事务，发生异常的时候，缓存也能够回滚，只支持jta环境，这个我没有怎么研究过<br><br>　　读写缓存和不严格读写缓存在实现上的区别在于，读写缓存更新缓存的时候会把缓存里面的数据换成一个锁，其他事务如果去取相应的缓存数据，发现被锁住了，然后就直接取数据库查询。 <br><br>　　在hibernate2.1的ehcache实现中，如果锁住部分缓存的事务发生了异常，那么缓存会一直被锁住，直到60秒后超时。 <br><br>　　不严格读写缓存不锁定缓存中的数据。 使用二级缓存的前置条件。<br><br>　　你的hibernate程序对数据库有独占的写访问权，其他的进程更新了数据库，hibernate是不可能知道的。你操作数据库必需直接通过hibernate，如果你调用存储过程，或者自己使用jdbc更新数据库，hibernate也是不知道的。hibernate3.0的大批量更新和删除是不更新二级缓存的，但是据说3.1已经解决了这个问题。 <br><br>　　这个限制相当的棘手，有时候hibernate做批量更新、删除很慢，但是你却不能自己写jdbc来优化，很郁闷吧。 <br><br>　　SessionFactory也提供了移除缓存的方法，你一定要自己写一些JDBC的话，可以调用这些方法移除缓存，这些方法是： <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>void evict(Class persistentClass) <br>Evict all entries from the second-level cache. <br>void evict(Class persistentClass, Serializable id) <br>Evict an entry from the second-level cache. <br>void evictCollection(String roleName) <br>Evict all entries from the second-level cache. <br>void evictCollection(String roleName, Serializable id) <br>Evict an entry from the second-level cache. <br>void evictQueries() <br>Evict any query result sets cached in the default query cache region. <br>void evictQueries(String cacheRegion) <br>Evict any query result sets cached in the named query cache region. </td>
        </tr>
    </tbody>
</table>
<br>　　不过我不建议这样做，因为这样很难维护。比如你现在用JDBC批量更新了某个表，有3个查询缓存会用到这个表，用evictQueries(String cacheRegion)移除了3个查询缓存，然后用evict(Class persistentClass)移除了class缓存，看上去好像完整了。不过哪天你添加了一个相关查询缓存，可能会忘记更新这里的移除代码。如果你的jdbc代码到处都是，在你添加一个查询缓存的时候，还知道其他什么地方也要做相应的改动吗？<br><br>　　<strong>总结</strong>： <br><br>　　不要想当然的以为缓存一定能提高性能，仅仅在你能够驾驭它并且条件合适的情况下才是这样的。hibernate的二级缓存限制还是比较多的，不方便用jdbc可能会大大的降低更新性能。在不了解原理的情况下乱用，可能会有1+N的问题。不当的使用还可能导致读出脏数据。 <br>如果受不了hibernate的诸多限制，那么还是自己在应用程序的层面上做缓存吧。 <br><br>　　在越高的层面上做缓存，效果就会越好。就好像尽管磁盘有缓存，数据库还是要实现自己的缓存，尽管数据库有缓存，咱们的应用程序还是要做缓存。因为底层的缓存它并不知道高层要用这些数据干什么，只能做的比较通用，而高层可以有针对性的实现缓存，所以在更高的级别上做缓存，效果也要好些吧。<br>
<img src ="http://www.blogjava.net/xixidabao/aggbug/109196.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-04-08 10:04 <a href="http://www.blogjava.net/xixidabao/archive/2007/04/08/109196.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate 的原理与配置快速入门</title><link>http://www.blogjava.net/xixidabao/archive/2006/05/17/46596.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Wed, 17 May 2006 03:46:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/05/17/46596.html</guid><description><![CDATA[　　也许你听说过Hibernate的大名，但可能一直不了解它，也许你一直渴望使用它进行开发，那么本文正是你所需要的！在本文中，我向大家重点介绍Hibernate的核心API调用库，并讲解一下它的基本配置。<br><br>　　看完本文后，我相信你对什么是ORM（对像/关系映射）以及它的优点会有一个深刻的认识，我们先通过一个简单的例子开始来展现它的威力。<br><br>　　正如一些传统的经典计算机文章大都会通过一个&#8220;hello,world&#8221;的例子开始讲解一样，我们也不例外，我们也将从一个相对简单的例子来阐述Hibernate的开发方法，但如果要真正阐述Hibernate的一些重要思想，仅仅靠在屏幕上打印一些字符是远远不够的，在我们的示例程序中，我们将创建一些对象，并将其保存在数据库中，然后对它们进行更新和查询。<br><br>
<table borderColor=#ffcc00 cellSpacing=4 width="90%" align=center border=1>
    <tbody>
        <tr>
            <td colSpan=2>
            <div align=center><font color=#ff0000><strong>阅读导航</strong></font></div>
            </td>
        </tr>
        <tr>
            <td width="35%"><a href="http://dev.yesky.com/SoftChannel/72342371961929728/20041026/1868578_1.shtml" target=_blank><font color=#0000ff>&#8220;Hello World&#8221;</font></a></td>
            <td width="65%">&#8220;Hello world&#8221;示例程序让您对Hibernate有一个简单的认识<strong>。</strong></td>
        </tr>
        <tr>
            <td><a href="http://dev.yesky.com/SoftChannel/72342371961929728/20041026/1868578_2.shtml" target=_blank><font color=#0000ff>理解Hibernate的架构</font></a></td>
            <td>介绍Hibernate接口的主要功能。</td>
        </tr>
        <tr>
            <td><a href="http://dev.yesky.com/SoftChannel/72342371961929728/20041026/1868578_2.shtml" target=_blank><font color=#0000ff>核心接口</font></a></td>
            <td>Hibernate有5个核心接口，通过这几个接口开发人员可以存储和获得持久对象，并且能够进行事务控制</td>
        </tr>
        <tr>
            <td><a href="http://dev.yesky.com/SoftChannel/72342371961929728/20041026/1868578_3.shtml" target=_blank><font color=#0000ff>一个重要的术语：Type</font></a></td>
            <td>Type是Hibernate发明者发明的一个术语，它在整个构架中是一个非常基础、有着强大功能的元素，一个Type对象能将一个Java类型映射到数据库中一个表的字段中去。</td>
        </tr>
        <tr>
            <td><a href="http://dev.yesky.com/SoftChannel/72342371961929728/20041026/1868578_3.shtml" target=_blank><font color=#0000ff>策略接口</font></a></td>
            <td>Hibernate与某些其它开源软件不同的还有一点――高度的可扩展性，这通过它的内置策略机制来实现。</td>
        </tr>
        <tr>
            <td><a href="http://dev.yesky.com/SoftChannel/72342371961929728/20041026/1868578_3.shtml" target=_blank><font color=#0000ff>基础配置</font></a></td>
            <td>Hibernate可以配置成可在任何Java环境中运行，一般说来，它通常被用在2－3层的C/S模式的项目中，并被部署在服务端。</td>
        </tr>
        <tr>
            <td height=36><a href="http://dev.yesky.com/SoftChannel/72342371961929728/20041026/1868578_4.shtml" target=_blank><font color=#0000ff>创建一个SessionFactory对象</font></a></td>
            <td>要创建一个SessionFactory对象，必须在Hibernate初始化时创建一个Configuration类的实例，并将已写好的映射文件交由它处理。</td>
        </tr>
    </tbody>
    <strong>&#8220;Hello World&#8221;</strong><br><br>　　Hibernate应用程序定义了一些持久类，并且定义了这些类与数据库表格的映射关系。在我们这个&#8220;Hello world&#8221;示例程序中包含了一个类和一个映射文件。让我们看看这个简单的持久类包含有一些什么？映射文件是怎样定义的？另外，我们该怎样用Hibernate来操作这个持久类。<br><br>　　我们这个简单示例程序的目的是将一些持久类存储在数据库中，然后从数据库取出来，并将其信息正文显示给用户。其中Message正是一个简单的持久类：，它包含我们要显示的信息，其源代码如下：<br><br>　　列表1　Message.Java　一个简单的持久类<br><br>
</table>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
    <tbody>
        <tr>
            <td>package hello;<br>public class Message {<br>　private Long id;<br>　private String text;<br>　private Message nextMessage;<br>　private Message() {}<br>　public Message(String text) {<br>　　this.text = text;<br>　}<br>　public Long getId() {<br>　　return id;<br>　}<br>　private void setId(Long id) {<br>　　this.id = id;<br>　}<br>　public String getText() {<br>　　return text; <br>　}<br>　public void setText(String text) {<br>　　this.text = text;<br>　}<br>　public Message getNextMessage() {<br>　　return nextMessage;<br>　}<br>　public void setNextMessage(Message nextMessage) {<br>　　this.nextMessage = nextMessage;<br>　}<br>} </td>
        </tr>
    </tbody>
</table>
<br>　　Message类有三个属性：Message的id 、消息正文、以及一个指向下一条消息的指针。其中id属性让我们的应用程序能够唯一的识别这条消息，通常它等同于数据库中的主键，如果多个Message类的实例对象拥有相同的id，那它们代表数据库某个表的同一个记录。在这里我们选择了长整型作为我们的id值，但这不是必需的。Hibernate允许我们使用任意的类型来作为对象的id值，在后面我们会对此作详细描述。<br><br>　　你可能注意到Message类的代码类似于JavaBean的代码风格，并且它有一个没有参数的构造函数，在我们以后的代码中我将继续使用这种风格来编写持久类的代码。<br><br>　　Hibernate会自动管理Message类的实例，并通过内部机制使其持久化，但实际上Message对象并没有实现任何关于Hibernate的类或接口，因此我们也可以将它作为一个普通的Java类来使用：<br><br>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
    <tbody>
        <tr>
            <td>Message message = new Message("Hello World");<br>System.out.println( message.getText() ); </td>
        </tr>
    </tbody>
</table>
<br>　　以上这段代码正是我们所期望的结果：它打印&#8220;hello world&#8221;到屏幕上。但这并不是我们的最终目标；实际上Hibernate与诸如EJB容器这样的环境在持久层实现的方式上有很大的不同。我们的持久类(Message类)可以用在与容器无关的环境中，不像EJB必须要有EJB容器才能执行。为了能更清楚地表现这点，以下代码将我们的一个新消息保存到数据库中去：<br><br>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
    <tbody>
        <tr>
            <td>Session session = getSessionFactory().openSession();<br>Transaction tx = session.beginTransaction();<br>Message message = new Message("Hello World");<br>session.save(message);<br>tx.commit();<br>session.close(); </td>
        </tr>
    </tbody>
</table>
<br>　　以上这段代码调用了Hibernate的Session和Transaction接口（关于getSessionFactory()方法我们将会马上提到）。它相当于我们执行了以下SQL语句：<br><br>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
    <tbody>
        <tr>
            <td>insert into MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID)<br>values (1, 'Hello World', null) </td>
        </tr>
    </tbody>
</table>
<br>　　在以上的SQL语句中，MESSAGE_ID字段到底被初始化成了什么值呢？由于我们并没有在先前的代码中为message对象的id属性赋与初始值，那它是否为null呢？实际上Hibernate对id属性作了特殊处理：由于它是一个对象的唯一标识，因此当我们进行save()调用时，Hibernate会为它自动赋予一个唯一的值（我们将在后面内容中讲述它是如何生成这个值的）。<br><br>　　我们假设你已经在数据库中创建了一个名为MESSAGE的表，那么既然前面这段代码让我们将Message对象存入了数据库中，那么现在我们就要将它们一一取出来。下面这段代码将按照字母顺序，将数据库中的所有Message对象取出来，并将它们的消息正文打印到屏幕上：<br><br>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
    <tbody>
        <tr>
            <td>Session newSession = getSessionFactory().openSession();<br>Transaction newTransaction = newSession.beginTransaction();<br>List messages =newSession.find("from Message as m order by m.text asc");<br>System.out.println( messages.size() + " message(s) found:" );<br>for ( Iterator iter = messages.iterator(); iter.hasNext(); ) {<br>　Message message = (Message) iter.next();<br>　System.out.println( message.getText() );<br>}<br>newTransaction.commit();<br>newSession.close(); </td>
        </tr>
    </tbody>
</table>
<br>　　在以上这段代码中，你可能被find()方法的这个参数困扰着："from Message as m order by m.text asc"，其实它是Hibernate自己定义的查询语言，全称叫Hibernate Query Language(HQL)。通俗地讲HQL与SQL的关系差不多就是方言与普通话之间的关系，咋一看，你会觉得它有点类似于SQL语句。其实在find()调用时，Hibernate会将这段HQL语言翻译成如下的SQL语句：<br><br>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
    <tbody>
        <tr>
            <td>select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID<br>from MESSAGES m<br>order by m.MESSAGE_TEXT asc </td>
        </tr>
    </tbody>
</table>
<br>　　以下就是运行结果： <br><br>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
    <tbody>
        <tr>
            <td>1 message(s) found:<br>Hello World </td>
        </tr>
    </tbody>
</table>
<br>　　如果你以前没有ORM（对象－关系映射）的开发经验，那你可能想在代码的某个地方去寻找这段SQL语句，但在Hibernate中你可能会失望：它根本不存在！所有就SQL语句都是Hibernate动态生成的。 <br><br>　　也许你会觉得还缺点什么，对！仅凭以上代码Hibernate是无法将我们的Message类持久化的。我们还需要一些更多的信息，这就是映射定义表！这个表在Hibernate中是以XML格式来体现的，它定义了Message类的属性是怎样与数据库中的MESSAGES表的字段进行一一对应的，列表2是这个示例程序的映射配置文件清单：<br><br>　　列表2：示例程序的对象－关系映射表 <br><br>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
    <tbody>
        <tr>
            <td>＜?xml version="1.0"?＞<br>＜!DOCTYPE hibernate-mapping PUBLIC<br>"-//Hibernate/Hibernate Mapping DTD//EN"<br>"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"＞<br>＜hibernate-mapping＞<br>＜class name="hello.Message" table="MESSAGES"＞<br>　＜id name="id" column="MESSAGE_ID"＞<br>　　＜generator class="increment"/＞<br>　＜/id＞<br>　＜property name="text" column="MESSAGE_TEXT"/＞<br>　＜many-to-one name="nextMessage" cascade="all" column="NEXT_MESSAGE_ID"/＞<br>＜/class＞<br>＜/hibernate-mapping＞ </td>
        </tr>
    </tbody>
</table>
<br>　　以上这个文档告诉Hibernate怎样将Message类映射到MESSAGES表中，其中Message类的id属性与表的MESSAGE_ID字段对应，text属性与表的MESSAGE_TEXT字段对应，nextMessage属性是一个多对一的关系，它与表中的NEXT_MESSAGE_ID相对应。 <br><br>　　相对于有些开源项目来说，Hibernate的配置文件其实是很容易理解的。你可以轻松地修改与维护它。只要你定义好了持久类与数据库中表字段的对应关系就行了，Hibernate会自动帮你生成SQL语句来对Message对象进行插入、更新、删除、查找工作，你可以不写一句SQL语句，甚至不需要懂得SQL语言！<br><br>　　现在让我们做一个新的试验，我们先取出第一个Message对象，然后修改它的消息正文，最后我们再生成一个新的Message对象，并将它作为第一个Message对象的下一条消息，其代码如下：<br><br>　　列表3　更新一条消息<br><br>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
    <tbody>
        <tr>
            <td>Session session = getSessionFactory().openSession();<br>Transaction tx = session.beginTransaction();<br>// 1 is the generated id of the first message<br>Message message =(Message) session.load( Message.class, new Long(1) );<br>message.setText("Greetings Earthling");<br>Message nextMessage = new Message("Take me to your leader (please)");<br>message.setNextMessage( nextMessage );<br>tx.commit();<br>session.close(); </td>
        </tr>
    </tbody>
</table>
<br>　　以上这段代码在调用时，Hibernate内部自动生成如下的SQL语句：<br><br>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
    <tbody>
        <tr>
            <td>select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID<br>from MESSAGES m<br>where m.MESSAGE_ID = 1<br><br>insert into MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID)<br>values (2, 'Take me to your leader (please)', null)<br><br>update MESSAGES<br>set MESSAGE_TEXT = 'Greetings Earthling', NEXT_MESSAGE_ID = 2<br>where MESSAGE_ID = 1 </td>
        </tr>
    </tbody>
</table>
<br>　　当第一个Message对象的text属性和nextMessage被程序修改时，请注意Hibernate是如何检测到这种变化，并如何在数据库中自动对它更新的。这实际上是Hibernate的一个很有价值的特色，我们把它称为&#8220;自动脏数据检测&#8221;，Hibernate的这个特色使得当我们修改一个持久对象的属性后，不必显式地通知Hibernate去将它在数据库中进行更新。同样的，当第一个Message对象调用setNextMessage()方法将第二个Message对象作为它的下一条消息的引用时，第二条消息会无需调用save()方法，便可以自动地保存在数据库中。这种特色被称为&#8220;级联保存&#8221;，它也免去了我们显式地对第二个Message对象调用save()方法之苦。<br><br>　　如果我们再运行先前的那段将数据库中所有的Message对象都打印出来的代码，那它的运行结果如下：<br><br>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
    <tbody>
        <tr>
            <td>2 message(s) found:<br>Greetings Earthling<br>Take me to your leader (please) </td>
        </tr>
    </tbody>
</table>
<br>　　&#8220;Hello world&#8221;示例程序现在介绍完毕。我们总算对Hibernate有了一个简单的认识，下面我们将回过头来，对Hibernate的主要API调用作一下简要的介绍：<br><br><br><strong>理解Hibernate的架构</strong><br><br>　　当你想用Hibernate开发自己的基于持久层的应用时，第一件事情应当是熟悉它的编程接口。Hibernate的API接口设计得尽量简洁明了，以方便开发人员。然而实际上由于ORM的复杂性，它的API一般都不可能设计得很简单。但是别担心，你没有必要一下子了解所有的Hibernate的API接口。下面这张图描述了Hibernate在应用层和持久层中的一些重要的接口类：<br><br>
<table width="90%" align=center border=0>
    <tbody>
        <tr>
            <td>
            <div align=center><img onerror="this.src='http://www.yesky.com/image20010518/189639.jpg';" hspace=3 src="http://www.blogjava.net/image20010518/189639.jpg" align=center vspace=1 border=1></div>
            </td>
        </tr>
    </tbody>
</table>
<br>　　在上图中，我们将应用层放在了持久层的上部，实际上在传统的项目中，应用层充当着持久层的一个客户端角色。但对于一些简单的项目来说，应用层和持久层并没有区分得那么清楚，这也没什么，在这种情况下你可以将应用层和持久层合并成了一层。<br><br>　　在上图中，Hibernate的接口大致可以分为以下几种类型：<br><br>　　&#183; 一些被用户的应用程序调用的，用来完成基本的创建、读取、更新、删除操作以及查询操作的接口。这些接口是Hibernate实现用户程序的商业逻辑的主要接口，它们包括Session、Transaction和Query。<br><br>　　&#183; Hibernate用来读取诸如映射表这类配置文件的接口，典型的代表有Configuration类。<br><br>　　&#183; 回调(Callback)接口。它允许应用程序能对一些事件的发生作出相应的操作，例如Interceptor、Lifecycle和Validatable都是这一类接口。<br><br>　　&#183; 一些可以用来扩展Hibernate的映射机制的接口，例如UserType、CompositeUserType和IdentifierGenerator。这些接口可由用户程序来实现（如果有必要）。<br><br>　　Hibernate使用了J2EE架构中的如下技术：JDBC、JTA、JNDI。其中JDBC是一个支持关系数据库操作的一个基础层；它与JNDI和JTA一起结合，使得Hibernate可以方便地集成到J2EE应用服务器中去。<br><br>　　在这里，我们不会详细地去讨论Hibernate API接口中的所有方法，我们只简要讲一下每个主要接口的功能，如果你想了解得更多的话，你可以在Hibernate的源码包中的net.sf.hibernate子包中去查看这些接口的源代码。下面我们依次讲一下所有的主要接口：<br><br>　　<strong>核心接口</strong><br><br>　　以下5个核心接口几乎在任何实际开发中都会用到。通过这些接口，你不仅可以存储和获得持久对象，并且能够进行事务控制。<br><br>　　Session接口<br><br>　　Session接口对于Hibernate 开发人员来说是一个最重要的接口。然而在Hibernate中，实例化的Session是一个轻量级的类，创建和销毁它都不会占用很多资源。这在实际项目中确实很重要，因为在客户程序中，可能会不断地创建以及销毁Session对象，如果Session的开销太大，会给系统带来不良影响。但值得注意的是Session对象是非线程安全的，因此在你的设计中，最好是一个线程只创建一个Session对象。<br><br>　　在Hibernate的设计者的头脑中，他们将session看作介于数据连接与事务管理一种中间接口。我们可以将session想象成一个持久对象的缓冲区，Hibernate能检测到这些持久对象的改变，并及时刷新数据库。我们有时也称Session是一个持久层管理器，因为它包含这一些持久层相关的操作，诸如存储持久对象至数据库，以及从数据库从获得它们。请注意，Hibernate 的session不同于JSP应用中的HttpSession。当我们使用session这个术语时，我们指的是Hibernate中的session，而我们以后会将HttpSesion对象称为用户session。<br><br>　　SessionFactory 接口<br><br>　　这里用到了一个设计模式――工厂模式，用户程序从工厂类SessionFactory中取得Session的实例。<br><br>　　令你感到奇怪的是SessionFactory并不是轻量级的！实际上它的设计者的意图是让它能在整个应用中共享。典型地来说，一个项目通常只需要一个SessionFactory就够了，但是当你的项目要操作多个数据库时，那你必须为每个数据库指定一个SessionFactory。<br>SessionFactory在Hibernate中实际起到了一个缓冲区的作用，它缓冲了Hibernate自动生成的SQL语句和一些其它的映射数据，还缓冲了一些将来有可能重复利用的数据。<br><br>　　Configuration 接口<br><br>　　Configuration接口的作用是对Hibernate进行配置，以及对它进行启动。在Hibernate的启动过程中，Configuration类的实例首先定位映射文档的位置，读取这些配置，然后创建一个SessionFactory对象。<br><br>　　虽然Configuration接口在整个Hibernate项目中只扮演着一个很小的角色，但它是启动hibernate时你所遇到的每一个对象。<br><br>　　Transaction 接口<br><br>　　Transaction接口是一个可选的API，你可以选择不使用这个接口，取而代之的是Hibernate的设计者自己写的底层事务处理代码。 Transaction接口是对实际事务实现的一个抽象，这些实现包括JDBC的事务、JTA中的UserTransaction、甚至可以是CORBA事务。之所以这样设计是能让开发者能够使用一个统一事务的操作界面，使得自己的项目可以在不同的环境和容器之间方便地移值。<br><br>　　Query和Criteria接口<br><br>　　Query接口让你方便地对数据库及持久对象进行查询，它可以有两种表达方式：HQL语言或本地数据库的SQL语句。Query经常被用来绑定查询参数、限制查询记录数量，并最终执行查询操作。<br><br>　　Criteria接口与Query接口非常类似，它允许你创建并执行面向对象的标准化查询。<br><br>　　值得注意的是Query接口也是轻量级的，它不能在Session之外使用。<br><br>　　Callback 接口 <br><br>　　当一些有用的事件发生时――例如持久对象的载入、存储、删除时，Callback接口会通知Hibernate去接收一个通知消息。一般而言，Callback接口在用户程序中并不是必须的，但你要在你的项目中创建审计日志时，你可能会用到它。<br><br><br><br><br><br>　　<strong>一个重要的术语：Type</strong><br><br>　　Hibernate的设计者们发明了一个术语：Type，它在整个构架中是一个非常基础、有着强大功能的元素。一个Type对象能将一个Java类型映射到数据库中一个表的字段中去（实际上，它可以映射到表的多个字段中去）。持久类的所有属性都对应一个type。这种设计思想使用Hibernate有着高度的灵活性和扩展性。<br><br>　　Hibernate内置很多type类型，几乎包括所有的Java基本类型，例如Java.util.Currency、Java.util.calendar、byte[]和Java.io.Serializable。<br><br>　　不仅如此，Hibernate还支持用户自定义的type，通过实现接口UserType和接口CompositeUserType，你可以加入自己的type。你可以利用这种特色让你的项目中使用自定义的诸如Address、Name这样的type，这样你就可以获得更大的便利，让你的代码更优雅。自定义type在Hibernate中是一项核心特色，它的设计者鼓励你多多使用它来创建一个灵活、优雅的项目！<br><br>　　<strong>策略接口</strong><br><br>　　Hibernate与某些其它开源软件不同的还有一点――高度的可扩展性，这通过它的内置策略机制来实现。当你感觉到Hibernate的某些功能不足，或者有某些缺陷时，你可以开发一个自己的策略来替换它，而你所要做的仅仅只是继承它的某个策略接口，然后实现你的新策略就可以了，以下是它的策略接口：<br><br>　　&#183; 主键的生成 (IdentifierGenerator 接口) <br><br>　　&#183; 本地SQL语言支持 (Dialect 抽象类) <br><br>　　&#183; 缓冲机制 (Cache 和CacheProvider 接口) <br><br>　　&#183; JDBC 连接管理 (ConnectionProvider接口) <br><br>　　&#183; 事务管理 (TransactionFactory, Transaction, 和 TransactionManagerLookup 接口) <br><br>　　&#183; ORM 策略 (ClassPersister 接口) <br><br>　　&#183; 属性访问策略 (PropertyAccessor 接口) <br><br>　　&#183; 代理对象的创建 (ProxyFactory接口)<br><br>　　Hibernate为以上所列的机制分别创建了一个缺省的实现，因此如果你只是要增强它的某个策略的功能的话，只需简单地继承这个类就可以了，没有必要从头开始写代码。<br><br>　　以上就是Hibernate的一些核心接口，但当我们真正开始用它进行开发时，你的脑海里可能总会有一个疑问：我是通过什么方式，并从哪里取得Session的呢？以下我们就解答这个问题。<br><br>　　<strong>基础配置</strong><br><br>　　现在回顾一下我们先前的内容：我们写出了一个示例程序，并简要地讲解了Hibernate的一些核心类。但要真正使你的项目运行起来，还有一件事必须要做：配置。Hibernate可以配置成可在任何Java环境中运行，一般说来，它通常被用在2－3层的C/S模式的项目中，并被部署在服务端。在这种项目中，Web浏览器、或Java GUI程序充当者客户端。尽管我们的焦点主要是集中在多层web应用，但实际上在一些基于命令行的应用中也可以使用Hibernate。并且，对Hibernate的配置在不同的环境下都会不同，Hibernate运行在两种环境下：可管理环境和不可管理环境<br><br>　　&#183; 可管理环境――这种环境可管理如下资源：池资源管理，诸如数据库连接池和，还有事务管理、安全定义。一些典型的J2EE服务器（JBoss、Weblogic、WebSphere）已经实现了这些。<br><br>　　&#183; 不可管理环境――只是提供了一些基本的功能，诸如像Jetty或Tomcat这样的servlet容器环境。一个普通的Java桌面应用或命令行程序也可以认为是处于这种环境下。这种环境不能提供自动事务处理、资源管理或安全管理，这些都必须由应用程序自己来定义。<br><br>　　Hibernate的设计者们将这两种环境设计了一个统一的抽象界面，因此对于开发者来说只有一种环境：可管理环境。如果实际项目是建立在诸如Tomcat这类不可管理的环境中时，那Hibernate将会使用它自己的事务处理代码和JDBC连接池，使其变为一个可管理环境。<br>对于可管理的环境而言，Hibernate会将自己集成在这种环境中。对于开发者而言，你所要做的工作非常简单：只需从一个Configuration类中创建一个SessionFactory类就可以了。<br><br><br><br><br><br><strong>创建一个SessionFactory对象</strong><br><br>　　为了能创建一个SessionFactory对象，你必须在Hibernate初始化时创建一个Configuration类的实例，并将已写好的映射文件交由它处理。这样，Configuration对象就可以创建一个SessionFactory对象，当SessionFactory对象创建成功后，Configuration对象就没有用了，你可以简单地抛弃它。如下是示例代码：<br><br>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
    <tbody>
        <tr>
            <td>Configuration cfg = new Configuration();<br>cfg.addResource("hello/Message.hbm.xml");<br>cfg.setProperties( System.getProperties() );<br>SessionFactory sessions = cfg.buildSessionFactory(); </td>
        </tr>
    </tbody>
</table>
<br>　　在以上代码中，Message.hb.xml这个映射文件的位置比较特殊，它与当前的classpath相关。例如classpath包含当前目录，那在上述代码中的Message.hbm.xml映射文件就可以保存在当前目录下的hello目录中。<br><br>　　作为一种约定，Hibernate的映射文件默认以.htm.xml作为其扩展名。另一个约定是坚持为每一个持久类写一个配置文件，想一想如果你将所有持久类的映射写入一个单独的配置文件中的话，那这个配置文件肯定非常庞大，不易维护。但这里又出现了一个新问题：如果为每个类写一个配置文件的话，这么多的配置文件应该存放在哪里呢？<br><br>　　Hibernate推荐你将每个映射文件保存在与持久类相同的目录下，并且与持久类同名。例如我们第一个示例程序中的Message持久类放在hello目录下，那你必须在这个目录下存放名为Message.hbm.xml的映射文件。这样一个持久类都有自己的一个映射文件，避免了出现像struts项目中的&#8220;struts-config.xml地狱&#8221;的情况。如果你不遵循这种规定，那你必须手动地用addResource()方法将一个个的映射文件载入；但你如果遵循这种规定，那你可以方便地用addClass()方法同时将持久类和它的映射文件载入，以下是体现这种便利性的示例代码：<br><br>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e6e4dd border=1>
    <tbody>
        <tr>
            <td>SessionFactory sessions = new Configuration()<br>.addClass(org.hibernate.auction.model.Item.class)<br>.addClass(org.hibernate.auction.model.Category.class)<br>.addClass(org.hibernate.auction.model.Bid.class)<br>.setProperties( System.getProperties() )<br>.buildSessionFactory(); </td>
        </tr>
    </tbody>
</table>
<br>　　当然，Hibernate的映射文件还有很多其它的配置选项，比如数据库连接的设定，或是能够改变Hibernate运行时行为的一些设定。所有的设置可能是非常庞杂的，足以让你喘不过气来，但是不必担心，因为Hibernate为绝大多数值都设定了一个合理缺省值，你只需要修改这些配置文件中的极小一部分值。<br><br>　　你可以通过以下几种方式来修改Hibernate的系统配置参数：<br><br>　　&#183; 将一个Java.util.Properties实例作为参数传给Configuration类的setProperties()方法。<br><br>　　&#183; 在Hibernate启动时用Java &#8211;Dproperty=value的方式设置值。<br><br>　　&#183; 在classpath可以找到的路径下创建一个名为hibernate.properties的配置文件。 <br><br>　　&#183; 在classpath可以找到的路径下创建一个名为hibernate.cfg.xml的文件，并在其＜property＞标签中定义属性值。<br><br>　　以上就是对Hibernate的一个大致介绍，如果你想知道得更多，那本文还是远远不够的，我将陆续推出更多关于Hibernate的资料。但有一点是毫无疑问的：它的确是一个非常优秀的持久层解决方案！<br><br><br><br><br>
<img src ="http://www.blogjava.net/xixidabao/aggbug/46596.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-05-17 11:46 <a href="http://www.blogjava.net/xixidabao/archive/2006/05/17/46596.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>