<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>Team12-billgmh    Chacing Dreams</title>
    <description></description>
    <link>http://billgmh.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
          <item>
        <title>Hibernate程序性能优化 [转载]</title>
        <author>billgmh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://billgmh.javaeye.com">billgmh</a>&nbsp;
                    链接：<a href="http://billgmh.javaeye.com/blog/214957" style="color:red;">http://billgmh.javaeye.com/blog/214957</a>&nbsp;
          发表时间: 2008年07月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><span style="color: #ff9900;">本文依照HIBERNATE帮助文档，一些网络书籍及项目经验整理而成，只提供要点和思路，具体做法可以留言探讨，或是找一些更详细更有针对性的资料。</span> </p>
<p>&nbsp;</p>
<p>　　初用HIBERNATE的人也许都遇到过性能问题，实现同一功能，用HIBERNATE与用JDBC性能相差十几倍很正常，如果不及早调整，很可能影响整个项目的进度。</p>
<p>　　大体上，对于HIBERNATE性能调优的主要考虑点如下:</p>
<p>　　&Oslash; 数据库设计调整</p>
<p>　　&Oslash; HQL优化</p>
<p>　　&Oslash; API的正确使用(如根据不同的业务类型选用不同的集合及查询API)</p>
<p>　　&Oslash; 主配置参数(日志，查询缓存，fetch_size, batch_size等)</p>
<p>　　&Oslash; 映射文件优化(ID生成策略，二级缓存，延迟加载，关联优化)</p>
<p>　　&Oslash; 一级缓存的管理</p>
<p>　　&Oslash; 针对二级缓存，还有许多特有的策略</p>
<p>　　&Oslash; 事务控制策略。</p>
<p><strong>　　1、 数据库设计</strong></p>
<p>　　a) 降低关联的复杂性</p>
<p>　　b) 尽量不使用联合主键</p>
<p>　　c) ID的生成机制，不同的数据库所提供的机制并不完全一样</p>
<p>　　d) 适当的冗余数据，不过分追求高范式</p>
<p><strong>　　2、 HQL优化</strong></p>
<p>　　HQL如果抛开它同HIBERNATE本身一些缓存机制的关联，HQL的优化技巧同普通的SQL优化技巧一样，可以很容易在网上找到一些经验之谈。</p>
<p><strong>　　3、 主配置</strong></p>
<p>　　a) 查询缓存，同下面讲的缓存不太一样，它是针对HQL语句的缓存，即完全一样的语句再次执行时可以利用缓存数据。但是，查询缓存在一个交易系统(数据变更频繁，查询条件相同的机率并不大)中可能会起反作用:它会白白耗费大量的系统资源但却难以派上用场。</p>
<p>　　b) fetch_size，同JDBC的相关参数作用类似，参数并不是越大越好，而应根据业务特征去设置</p>
<p>　　c) batch_size同上。</p>
<p>　　d) 生产系统中，切记要关掉SQL语句打印。</p>
<p><strong>　　4、 缓存</strong></p>
<p>　　a) 数据库级缓存:这级缓存是最高效和安全的，但不同的数据库可管理的层次并不一样，比如，在ORACLE中，可以在建表时指定将整个表置于缓存当中。</p>
<p>　　b) SESSION缓存:在一个HIBERNATE SESSION有效，这级缓存的可干预性不强，大多于HIBERNATE自动管理，但它提供清除缓存的方法，这在大批量增加/更新操作是有效的。比如，同时增加十万条记录，按常规方式进行，很可能会发现OutofMemeroy的异常，这时可能需要手动清除这一级缓存:Session.evict以及 Session.clear</p>
<p>　　c) 应用缓存:在一个SESSIONFACTORY中有效，因此也是优化的重中之重，因此，各类策略也考虑的较多，在将数据放入这一级缓存之前，需要考虑一些前提条件:</p>
<p>　　i. 数据不会被第三方修改(比如，是否有另一个应用也在修改这些数据?)</p>
<p>　　ii. 数据不会太大</p>
<p>　　iii. 数据不会频繁更新(否则使用CACHE可能适得其反)</p>
<p>　　iv. 数据会被频繁查询</p>
<p>　　v. 数据不是关键数据(如涉及钱，安全等方面的问题)。</p>
<p>　　缓存有几种形式，可以在映射文件中配置:read-only(只读，适用于很少变更的静态数据/历史数据)，nonstrict-read- write，read-write(比较普遍的形式，效率一般)，transactional(JTA中，且支持的缓存产品较少)</p>
<p>　　d) 分布式缓存:同c)的配置一样，只是缓存产品的选用不同，在目前的HIBERNATE中可供选择的不多，oscache, jboss cache，目前的大多数项目，对它们的用于集群的使用(特别是关键交易系统)都持保守态度。在集群环境中，只利用数据库级的缓存是最安全的。</p>
<p><strong>　　5、 延迟加载</strong></p>
<p>　　a) 实体延迟加载:通过使用动态代理实现</p>
<p>　　b) 集合延迟加载:通过实现自有的SET/LIST，HIBERNATE提供了这方面的支持</p>
<p>　　c) 属性延迟加载:</p>
<p><strong>　　6、 方法选用</strong></p>
<p>　　a) 完成同样一件事，HIBERNATE提供了可供选择的一些方式，但具体使用什么方式，可能用性能/代码都会有影响。显示，一次返回十万条记录 (List/Set/Bag/Map等)进行处理，很可能导致内存不够的问题，而如果用基于游标(ScrollableResults)或 Iterator的结果集，则不存在这样的问题。</p>
<p>　　b) Session的load/get方法，前者会使用二级缓存，而后者则不使用。</p>
<p>　　c) Query和list/iterator，如果去仔细研究一下它们，你可能会发现很多有意思的情况，二者主要区别(如果使用了Spring，在HibernateTemplate中对应find,iterator方法):</p>
<p>　　i. list只能利用查询缓存(但在交易系统中查询缓存作用不大)，无法利用二级缓存中的单个实体，但list查出的对象会写入二级缓存，但它一般只生成较少的执行SQL语句，很多情况就是一条(无关联)。</p>
<p>　　ii. iterator则可以利用二级缓存，对于一条查询语句，它会先从数据库中找出所有符合条件的记录的ID，再通过ID去缓存找，对于缓存中没有的记录，再构造语句从数据库中查出，因此很容易知道，如果缓存中没有任何符合条件的记录，使用iterator会产生N+1条SQL语句(N为符合条件的记录数)</p>
<p>　　iii. 通过iterator，配合缓存管理API，在海量数据查询中可以很好的解决内存问题，如:</p>
<p>　　while(it.hasNext()){</p>
<p>　　YouObject object = (YouObject)it.next();</p>
<p>　　session.evict(youObject);</p>
<p>　　sessionFactory.evice(YouObject.class, youObject.getId());</p>
<p>　　}</p>
<p>　　如果用list方法，很可能就出OutofMemory错误了。</p>
<p>　　iv. 通过上面的说明，我想你应该知道如何去使用这两个方法了。</p>
<p><strong>　　7、 集合的选用</strong></p>
<p>　　在HIBERNATE 3.1文档的&ldquo;19.5. Understanding Collection performance&rdquo;中有详细的说明。</p>
<p><strong>　　8、 事务控制</strong></p>
<p>　　事务方面对性能有影响的主要包括:事务方式的选用，事务隔离级别以及锁的选用</p>
<p>　　a) 事务方式选用:如果不涉及多个事务管理器事务的话，不需要使用JTA，只有JDBC的事务控制就可以。</p>
<p>　　b) 事务隔离级别:参见标准的SQL事务隔离级别</p>
<p>　　c) 锁的选用:悲观锁(一般由具体的事务管理器实现)，对于长事务效率低，但安全。乐观锁(一般在应用级别实现)，如在HIBERNATE中可以定义 VERSION字段，显然，如果有多个应用操作数据，且这些应用不是用同一种乐观锁机制，则乐观锁会失效。因此，针对不同的数据应有不同的策略，同前面许多情况一样，很多时候我们是在效率与安全/准确性上找一个平衡点，无论如何，优化都不是一个纯技术的问题，你应该对你的应用和业务特征有足够的了解。</p>
<p><strong>　　9、 批量操作</strong></p>
<p>　　即使是使用JDBC，在进行大批数据更新时，BATCH与不使用BATCH有效率上也有很大的差别。我们可以通过设置batch_size来让其支持批量操作。</p>
<p>　　举个例子，要批量删除某表中的对象，如&ldquo;delete Account&rdquo;，打出来的语句，会发现HIBERNATE找出了所有ACCOUNT的ID，再进行删除，这主要是为了维护二级缓存，这样效率肯定高不了，在后续的版本中增加了bulk delete/update，但这也无法解决缓存的维护问题。也就是说，由于有了二级缓存的维护问题，HIBERNATE的批量操作效率并不尽如人意!</p>
<p>　　从前面许多要点可以看出，很多时候我们是在效率与安全/准确性上找一个平衡点，无论如何，优化都不是一个纯技术的问题，你应该对你的应用和业务特征有足够的了解，一般的，优化方案应在架构设计期就基本确定，否则可能导致没必要的返工，致使项目延期，而作为架构师和项目经理，还要面对开发人员可能的抱怨，必竟，我们对用户需求更改的控制力不大，但技术/架构风险是应该在初期意识到并制定好相关的对策。</p>
<p>　　还有一点要注意，应用层的缓存只是锦上添花，永远不要把它当救命稻草，应用的根基(数据库设计，算法，高效的操作语句，恰当API的选择等)才是最重要的。</p>
          <br/><br/>
          <span style="color:red;">
            <a href="http://billgmh.javaeye.com/blog/214957#comments" style="color:red;">已有 <strong>0</strong> 人发表留言，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 15 Jul 2008 14:14:50 +0800</pubDate>
        <link>http://billgmh.javaeye.com/blog/214957</link>
        <guid>http://billgmh.javaeye.com/blog/214957</guid>
      </item>
          <item>
        <title>Struts应用转移到Struts2 [转载]</title>
        <author>billgmh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://billgmh.javaeye.com">billgmh</a>&nbsp;
                    链接：<a href="http://billgmh.javaeye.com/blog/213391" style="color:red;">http://billgmh.javaeye.com/blog/213391</a>&nbsp;
          发表时间: 2008年07月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>翻译：<a href="http://www.springside.org.cn/" target="_blank">SpringSide</a>团队&nbsp; &nbsp;转载请注明出处。<br />有很多人都很熟悉 Struts, 无论是从项目中直接获得的实战经验还是从书中了解到的。我们这一系列文章，将通过一个由 Stuts 转移到 Struts2 简单的例子向大家展现Struts2的所有特征。<br />&nbsp; &nbsp; 在我们开始这个例子之前，你需要去知道一点 Struts2的背景知识。 在第一部分的文章中，我们将介绍Struts2与Struts的核心框架的不同点，以助于更好地了解其他方面的整合。第二部分中，我们将深入探讨 actions 的差别, action相关的框架特征，和action配置。在最后一部分中，我们将会讲述 user interface，我们也会讲到其架构，UI构件，themes 和标签。 还有如何为你的应用加上新的外观。<br />&nbsp; &nbsp; 我们并不打算谈及迁移过程的所有细节方面，我们只是从出发点开始介绍Struts2 的概念和现在可用的所有特征。但拥有这些知识，你将在以后Struts2的应用中无往而不利。<br />&nbsp; &nbsp; <br /><strong><span style="font-size: small;">Struts的历史</span></strong><br />&nbsp; &nbsp; Struts的第一个版本 是在 2001年5月份发布。它提供了一个Web应用的解决方案，如何让 JSPs 和 servlets 共存去提供清晰的分离视图和业务和应用逻辑的架构。在Struts之前，最通常的做法是在JSP中加入业务和应用逻辑，或者在servlets中生成视图。<br />&nbsp; &nbsp; 自从第一个版本的发布， Struts 实际上已成为业界公认的Web应用标准。但随着时间的推移，Web应用框架经常变化的需求，产生了几个下一代 Struts的解决方案。其中两个可选方案是Shale 和 Struts Ti。 Shale 是一个基于构建的框架，并在最近成为 Apache 中的重要项目。而 Struts Ti 则是继续坚持 MVC模式的基础上改进，继续Struts的成功经验。<br />&nbsp; &nbsp; WebWork项目是在2002年3月发布的，它对Struts式框架进行了革命性改进，引进了不少新的思想，概念和功能，但和原Struts代码并不兼容。WebWork是一个成熟的框架，经过了好几次重大的改进与发布。在2005年12月，WebWork与Struts Ti决定合拼， 再此同时， Struts Ti 改名为 Struts Action Framework 2.0,成为Struts真正的下一代。<br /><br /><strong><span style="font-size: small;">请求如何运作</span></strong><br />&nbsp; &nbsp; 在我们开始详细探讨如何转移Struts到Struts2之前，让我们来看看整个请求流程在新架构中是如何运作的。你会注意到在整个请求的生命周期，仍是以controller作主体，而且所有的概念还都是你以前所熟悉的， 就如：<br /></p>
<ul>
<li>通过URL请求的参数来调用Actions来把数据传给server. </li>
<li>所有的Servlet objects (request, response, session,之类.) 仍然可以在Action中获取 </li>
</ul>
<ol>&nbsp;</ol>
<p>下图展示了整个请求的概要过程:<br /><img src="http://www.infoq.com/resource/articles/converting-struts-2-part1/en/resources/image1.jpg" border="0" onmouseover="if(this.width&gt;screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://www.infoq.com/resource/articles/converting-struts-2-part1/en/resources/image1.jpg');}" alt="" /><br /><br />整个请求过程可以分为六步骤:</p>
<ol type="1">
<li><em><strong>一个请求产生并经由框架处理</strong></em> - 框架根据请求匹配相应的配置，如使用哪些拦截器，action 类和结果。 </li>
<li><em><strong>请求通过一系列的拦截器</strong></em> - 拦截器，和拦截器组经配置后，能处理不同等级的请求，它们为请求提供了各种预处理，切面处理。这和Struts的使用 Jakarta Commons Chain 构件的 RequestProcessor类很相似。 </li>
<li><em><strong>调用 Action </strong></em>- 产生一个新的action对象实例，并提供请求所调用的处理逻辑的方法。Struts2 可以在配置action时为请求分配其指定的方法。我们在第二部文章中将对这步骤进行进一步讨论; </li>
<li><em><strong>调用产生的结果 </strong></em>- 获取通过action的方法处理后返回来的结果，匹配其result class并调用产生的实例。有种情况是在UI模板去生成HTML时才去处理这些结果。如果在这种情况下，在Struts2 模板中的tags能直接返回到 action 中，取结果来呈现界面。　 </li>
<li><em><strong>请求再次经过一系列的拦截器处理后返回</strong></em> - 请求反顺序通过与原来进入时的拦截器链, 当然，你也可以配置在这个过程中减少或增加拦截器处理． </li>
<li><em><strong>请求返回到用户 </strong></em>- 最后一步是由 control 返回到servlet。通常是生成HTML返回到user, 但你也可以指定返回的HTTP头或HTTP重定向。 
<ul>
&nbsp;
</ul>
</li>
</ol>
<p>你应该已注意到，Struts2与Struts的差别。最明显的就是Struts2是pull-MVC 架构，就是可以直接从Action中获取所需要的数据，而不是像Struts那样必须把 beans 存到page, request,或者session中才能获取。这个我们将在下一章中详细提及。<br /><br /><strong><span style="font-size: small;">配置框架</span></strong><br />首先最重要的是，让框架能通过web.xml在servlet containers里运行。<br />下面这个就是大家都熟悉的 Struts在 web.xml里的配置方法<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=157&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code0'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code0">&nbsp; &nbsp; <br />&lt;servlet&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;servlet-name&gt;action&lt;/servlet-name&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;servlet-class&gt;org.apache.struts.action.ActionServlet&lt;/servlet-class&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;init-param&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;param-name&gt;config&lt;/param-name&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;param-value&gt;/WEB-INF/struts-config.xml&lt;/param-value&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;/init-param&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;load-on-startup&gt;2&lt;/load-on-startup&gt;<br />&nbsp; &nbsp; &lt;/servlet&gt;<br /><br />&nbsp; &nbsp; &lt;servlet-mapping&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;servlet-name&gt;action&lt;/servlet-name&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;url-pattern&gt;*.do&lt;/url-pattern&gt;<br />&nbsp; &nbsp; &lt;/servlet-mapping&gt;</div>
<p><br />在 Struts2 中，这个有少许改变，最明显的是dispatcher 由servlet转为servlet filter, 其配置和servlet一样简单，如下：<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=157&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code1'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code1">&nbsp; &nbsp; <br />　&lt;filter&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;　&lt;filter-name&gt;webwork&lt;/filter-name&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;　　　&lt;filter-class&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;org.apache.struts.action2.dispatcher.FilterDispatcher<br />　　　&lt;/filter-class&gt;<br />&nbsp; &nbsp; &lt;/filter&gt;<br /><br />&nbsp; &nbsp; &lt;filter-mapping&gt;<br />　　　&lt;filter-name&gt;webwork&lt;/filter-name&gt;<br />　　　&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br />&nbsp; &nbsp; &lt;/filter-mapping&gt;</div>
<p><br />和servlet配置一样，filter配置定义了名称(供关联)和filter的类。filter mapping让URI匹配成功的的请求调用该filter。默认情况下，扩展名为".action"。<br />这个是在default.properties文件里的"struts.action.extension" 属性定义的。</p>
<blockquote><strong>工具箱</strong>:&nbsp;&nbsp;"default.properties"是配置选项定义文件。通过在classpath中包含一个叫"struts.properties"的文件，并设置不同的属性值，你可以覆盖这个默认的配置，实现自己的配置。</blockquote>
<p>对于Struts, servlet配置提供了初始化tag的参数和使用的文件，而Struts2没有这样的配置参数，取而代之的是在classpath下的默认配置文件"struts.xml"。</p>
<blockquote><strong>工具箱/提示</strong>:　Struts actions(扩展名".do")，Struts2 actions(扩展名".action"),所以Struts和Struts2可以在一个系统中共存。所以最好是保持原先的系统，在新功能的开发上用Struts2,　如果时间和资源允许的情况下再逐步迁移。另一种方法是只是把Struts2的扩展名改为".do",可重用JSPs.</blockquote>
<p><span style="font-size: small;"><strong>分析Actions</strong><br /></span>在上面介绍的请求运作流程中，我们谈及了一些Struts和Struts2的不同点。现在我们将较深入地探讨这两个框架中action结构的具体差别。<br />让我们来回顾一下 Struts 的 action 结构, 主要的形式如下：<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=157&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code2'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code2">public class MyAction extends Action {<br />&nbsp; &nbsp; public ActionForward execute(ActionMapping mapping,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;ActionForm form,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;HttpServletRequest request,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;HttpServletResponse response)<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;throws Exception {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;// do the work<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return (mapping.findForward("success"));<br />&nbsp; &nbsp; }<br />}</div>
<p><br />当实现一个Struts action时, 你需要注意一下问题：</p>
<ol type="1">
<li>所有的action　都必须继承于base Action 类. </li>
<li>所有的action都必须是线程安全的,因为action是单例的,singleton的. </li>
<li>因为所有的action都必须是线程安全的,所以所有的对象都不能是类属性, 都必须以方法参数的形式传值。 </li>
<li>调用action的方法必须命名为 "execute" ( 在Struts中的&nbsp;&nbsp;DispatchAction 类好像可以用其它方法去执行同一个action ，但实际上在框架中调用的仍然是 "execute" 方法。). </li>
<li>ActionForward 的结果是通过ActionMapping 类中的方法来产生的,通常是"findForward"方法. 
<ul>
&nbsp;
</ul>
</li>
</ol>
<p>相比较之下， Struts2的action　提供了很多简单的实现。下面就是个例子<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=157&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code3'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code3">public class MyAction {<br />&nbsp; &nbsp;public String execute() throws Exception {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;// do the work<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return "success";<br />&nbsp; &nbsp;}<br />}</div>
<p><br />首先你会注意到的是，Struts2中的action不再继承于任何类或需要实现任何接口。实际上，它还远不只这些。按照惯例，只有"execute"方法能调用action,　但在Struts2中并非必要，任何声明为public String methodName() 方法都能通过配置来调用action.<br />另外，你会注意到返回值不再是"ActionForward ",而是String, 如果你需喜欢String的形式，那在Action接口里有个帮助方法可以提供简单的结果常量，如"success", "none", "error", "input" 和 "login"。<br />最后，和Struts最大的革命性的不同是, 调用action不再是带参数的。那你如何在获得你所需要的值呢？答案是"inversion of control" 或 "dependency injection", 反转控制(想了解更多可以看Martin Fowler的文章 <a href="http://www.martinfowler.com/articles/injection.html" target="_blank"><span style="color: #0000ff;">http://www.martinfowler.com/articles/injection.html</span></a>)。<br />为了更好地了解反转控制，让我们来看看一个例子，如何在action处理过程中可以访问到HttpServerRequest 。在我们的例子中，我们用ServletRequestAware 接口，这个接口包含了相应属性的setter,如下<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=157&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code4'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code4">public interface ServletRequestAware {<br />&nbsp; &nbsp; public void setServletRequest(HttpServletRequest request);<br />}</div>
<p><br />当我们继承这个接口时，我们需要通过setter为我们的HttpServerRequest 属性变量赋值：<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=157&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code5'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code5">public class MyAction implements ServletRequestAware {<br />&nbsp; &nbsp;private HttpServletRequest request;<br />&nbsp; &nbsp;public void setServletRequest(HttpServletRequest request) {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;this.request = request;<br />&nbsp; &nbsp;}<br />&nbsp; &nbsp;public String execute() throws Exception {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;// do the work using the request<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return Action.SUCCESS;<br />&nbsp; &nbsp;}<br />}</div>
<p><br />看起来现在这些属性是类级别的，并不是线程安全的，但是在Struts2里并没有问题，因为每个请求过来的时候都会产生一个新的action对象实例，它并没有和其他请求共享一个对象，所以不需要考虑线程安全问题。<br /><br />现在我们还有最后一步，就是把action关联上<strong>ServletConfigInterceptor</strong> 拦截器。这个拦截器继承了<strong>ServletRequestAware</strong> 接口，并提供了把HttpServletRequest 注入到action中的功能。但是你现在不用担心如何配置这些，我们将在下一篇文章中具体讲述。最重要的是我们明白了拦截器和接口共同为action提供了反转控制的功能。<br />这个设计的好处是能让action完全和框架解耦。action仅仅是一个被框架使用的简单的POJO。这对于单元测试但来极大的好处， 你能方便的为Struts action实现 StrutsTestCase 或&nbsp;&nbsp;MockStrutsTestCase　单元测试。<br /><br /><span style="font-size: small;"><strong>总结</strong><br /></span>By到现在为止，你应该已经了解了Struts2的整个请求流程，还有高层的框架概念， 你也应该能自己动手配置Struts2的action，和讲出Struts和Struts2的差别了。<br />在下篇文章中，我们将会介绍一个详细的Struts应用向Struts2迁移的例子，同时我们也会介绍迁移中相关的知识，会讲述如何综合使用JSTL, JSP 和 Struts2，进一步讲述Struts和Struts2的action的差别，Struts2的配置和其他框架元素，和谈到更多的其他相关框架的特征。<br />(自<a href="http://www.infoq.com/" target="_blank">http://www.infoq.com/</a>, cac 翻译)</p>
<p>&nbsp;</p>
<p>&nbsp; <a href="http://forum.springside.org.cn/viewthread.php?tid=157&amp;extra=page%3D1" target="_blank">在上篇文章中</a>，我们已经从较高层解释了整个框架的结构，请求流程的基础，配置方式和Struts2和Struts1的不同之处。了解这些后从Struts 应用 迁移到 Struts 2 不再是难事。<br />&nbsp; &nbsp; 在这篇文章中，我们将会更详细地讲述如何由Struts 的action转为Struts 2的action。<br /><br /><strong><span style="font-size: small;">一个应用的例子</span></strong><br />这个例子选择了大家都熟悉的 - weblog. 简单地介绍下这例子的功能需求:</p>
<ol type="1">
<li>增加一个新的日志 </li>
<li>察看一个日志 </li>
<li>修改一个日志 </li>
<li>删除一个日志 </li>
<li>列出所有日至 
<ul>
&nbsp;
</ul>
</li>
</ol>
<p>&nbsp; &nbsp;&nbsp;&nbsp;增删修改(CRUD)，是项目中最为普遍的应用。<br />&nbsp; &nbsp;&nbsp;&nbsp;业务逻辑类在Struts 和 Struts2 应用都是可共用的。如:<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code0'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code0">public class BlogService {<br />&nbsp; &nbsp;&nbsp;&nbsp;private static List&lt;Blog&gt; blogs = new ArrayList&lt;Blog&gt;();<br />&nbsp; &nbsp;&nbsp;&nbsp;public List&lt;Blog&gt; list() { ... }<br />&nbsp; &nbsp;&nbsp;&nbsp;public Blog create(Blog blog) { ... }<br />&nbsp; &nbsp;&nbsp;&nbsp;public void update(Blog blog) { ... }<br />&nbsp; &nbsp;&nbsp;&nbsp;public void delete(int id) { ... }<br />&nbsp; &nbsp;&nbsp;&nbsp;public Blog findById(int id) { ... }<br />} </div>
<p><br /><strong>BlogService </strong>只是个简单的业务逻辑类，并不是接口，Struts 和 Struts2 的action皆可调用其实例。虽然这样设计在实际项目中会带来不必要的耦合，但我们的例子只是集中在讨论web层上，所以无关重要。</p>
<blockquote><strong>工具箱</strong>: 在第一篇文章中，我们谈论了在Struts2 actions中的依赖注入的接口注入方式。这个是servlet 相关类(<strong>HttpServletRequest</strong>, <strong>HttpServletResponse</strong>, <strong>PrincipalProxy</strong>, 等.)的主要注入方式，但这并不是唯一的方式。<br />Struts2 可以使用Spring框架作为默认的容器时，依赖注入的setter方法就可用了。通过在action中加入setter方法(如下演示)， Struts2 框架将能从Spring框架中获得正确的信息，并通过setter加载在action中。<br /><br />
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code1'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code1">public void setBlogService(BlogService service) {<br />&nbsp; &nbsp;&nbsp;&nbsp;this.blogService = service;<br />} </div>
<br />和接口注入方式类似，我们需要一个拦截器来帮助我们完成任务，这就是 <strong>ActionAutowiringInterceptor</strong> 拦截器。这样我们的业务逻辑类就通过Spring框架管理自动在action被调用之前注入到Struts2得action中。有多种的配置参数(如by name, by type 或 automatically)可供选择，可以让对象和setter匹配的注入的方式根据你的需要而定。<br /></blockquote>
<p><strong><span style="font-size: small;">Struts 应用中的代码</span></strong><br />&nbsp; &nbsp;&nbsp;&nbsp;我们首先从Struts讲起。在Struts中，最普遍的做法是，对于每个需求用例(如save,update,remove,list)来说都会有对应的action类，同时也会有相应的action form类。在我们的应用中的这个方式或许不是最佳的实现方式(其他的解决方案包括使用dynamic form或者使用request来分发action)，但我们例子中的做法是所有Struts开发者最熟悉的一种形式。了解了这种简单的实现方法，你有能力在迁移到Struts2时，使用更为优秀的方法。<br />在第一篇文章中我们谈过Struts 和 Struts2 中action的区别。现在我们从UML中再次看看他们的差别。一般来说form在Struts action中的表现形式是：<br /><br /><img src="http://www.infoq.com/resource/articles/migrating-struts-2-part2/en/resources/image1.jpg" border="0" onmouseover="if(this.width&gt;screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://www.infoq.com/resource/articles/migrating-struts-2-part2/en/resources/image1.jpg');}" alt="" /><br /><br />这action form将会在多个action中使用，让我们来看看它：<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code2'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code2">public class BlogForm extends ActionForm {<br /><br />&nbsp; &nbsp;&nbsp;&nbsp;private String id;<br />&nbsp; &nbsp;&nbsp;&nbsp;private String title;<br />&nbsp; &nbsp;&nbsp;&nbsp;private String entry;<br />&nbsp; &nbsp;&nbsp;&nbsp;private String created;<br /><br />&nbsp; &nbsp;&nbsp;&nbsp;// public setters and getters for all properties<br />}</div>
<p><br />如UML中展示的那样，其中一个限制就是必须继承ActionForm类，另外一个限制就是form中所有属性都必须是String类型，所以所有的getter和setter都必须只能接受String参数和返回String结果。<br /><br />然后我们来看看action。我们这个例子中的action有view, create 和 update action。<br /><em>The View Action:</em><br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code3'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code3">public class ViewBlogAction extends Action {<br /><br />&nbsp; &nbsp;&nbsp;&nbsp;public ActionForward execute(ActionMapping mapping,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ActionForm form,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; HttpServletRequest request,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; HttpServletResponse response)<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; throws Exception {<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;BlogService service = new BlogService();<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;String id = request.getParameter("id");<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;request.setAttribute("blog",service.findById(Integer.parseInt(id)));<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; return (mapping.findForward("success"));<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br />}</div>
<p><br /><em>The Create Action:<br /></em><br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code4'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code4">public class SaveBlogEntryAction extends Action {<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;public ActionForward execute(ActionMapping mapping,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ActionForm form,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; HttpServletRequest request,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; HttpServletResponse response)<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; throws Exception {<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;BlogService service = new BlogService();<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;BlogForm blogForm = (BlogForm) form;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Blog blog = new Blog();<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;BeanUtils.copyProperties( blog, blogForm );<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;service.create( blog );<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return (mapping.findForward("success"));<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br />}</div>
<p><br /><em>The Update Action:<br /></em><br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code5'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code5">public class UpdateBlogEntryAction extends Action {<br /><br />&nbsp; &nbsp;&nbsp;&nbsp;public ActionForward execute(ActionMapping mapping,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ActionForm form,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; HttpServletRequest request,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; HttpServletResponse response)<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; throws Exception {<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;BlogService service = new BlogService();<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;BlogForm blogForm = (BlogForm) form;<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Blog blog = service.findById( Integer.parseInt(blogForm.getId()));<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;BeanUtils.copyProperties( blog, blogForm );<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;service.update( blog );<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;request.setAttribute("blog",blog);<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return (mapping.findForward("success"));<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br />}</div>
<p><br />这三个action都跟随着同一个模式： </p>
<ul>
<li><strong><em>产生一个新的业务逻辑对象实例 </em></strong>- 如前面所提到的，我们使用最直接的方式在action中使用业务逻辑对象，这表示在每个action中都会产生新的业务逻辑对象实例。 </li>
<li><em><strong>从请求中获得数据</strong> </em>- 这是两种形式之一。在view action中，"id"是从<strong>HttpServletRequest</strong> 对象中直接获取的。而在create 和 update action 中，则从<strong>ActionForm</strong> 中取值。ActionForm 与 HttpServletRequest 的调用方式其实很相似，唯一不同的ActionForm 是bean的从field中取值。 </li>
<li><em><strong>调用业务逻辑</strong></em>- 现在开始生成调用业务逻辑所需的参数并调用逻辑。如果参数(在view action中)是一个简单对象类型，则转换值时会自动转为正确的类型(如从String转到Integer)。如果参数是复杂的对象类型，，则<strong>ActionForm</strong> 需要通过<strong>BeanUtil</strong> 来帮忙转成相应的对象。 </li>
<li><em><strong>设定返回的数据</strong></em> - 如果需要把数据返回显示给用户，那则要把这个数据设在<strong>HttpServletRequest</strong> 的attribute 中返回。 </li>
<li><em><strong>返回一个 ActionForward</strong> </em>- 所有 Struts action的最后都需要找到并返回其相应的 <strong>ActionForward</strong> 对象. </li>
</ul>
<ol>&nbsp;</ol>
<p>&nbsp; &nbsp;&nbsp;&nbsp;最后的两个action，remove和list action, 只有很少的差别。remove action如下所示，没有用BlogForm类. 通过从request的attribute中获取"id"(和view action相似)，就能调用业务逻辑完成其需要的工作。在下面我们介绍配置时，你可以看到它并没有返回任何数据，因为它的"success"返回结果其实是执行remove后再执行了list action来返回信息的。<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code6'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code6">public class RemoveBlogEntryAction extends Action {<br /><br />&nbsp; &nbsp;&nbsp;&nbsp;public ActionForward execute(ActionMapping mapping,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ActionForm form,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; HttpServletRequest request,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; HttpServletResponse response)<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; throws Exception {<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;BlogService service = new BlogService();<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;String id = request.getParameter("id");<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;service.delete(Integer.parseInt(id));<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return (mapping.findForward("success"));<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br />}</div>
<p><br />list action并不需要任何的用户输入，它只是简单地调用了业务逻辑的无参方法，同时返回所有的Blog对象。<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code7'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code7">public class ListBlogsAction extends Action {<br /><br />&nbsp; &nbsp;&nbsp;&nbsp;public ActionForward execute(ActionMapping mapping,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ActionForm form,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; HttpServletRequest request,<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; HttpServletResponse response)<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; throws Exception {<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;BlogService service = new BlogService();<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;request.setAttribute("bloglist",service.list());<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return (mapping.findForward("success"));<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br />}</div>
<p><br /><strong><span style="font-size: small;">向 Struts2 迁移</span></strong><br />&nbsp; &nbsp;&nbsp;&nbsp;在Struts2中，可选的实现方式有很多,可以像Struts那样每个需求用例对应一个action,也可以用一个action对应所有需求用例。但在我们的例子中,使用的方法是我认为最佳的解决方案 - 在一个action类中实现整套CRUD功能。<br />&nbsp; &nbsp;&nbsp;&nbsp;也许你人为把list需求用例也同样地整合到同一个action类里会比较好，而我认为把list的功能分到另外一个action中，会减少容易产生的混淆，因为list用例中并不需要<strong>Blog</strong>这个类作为属性，而在其他用例中则需要。<br /><br />对于 Struts2的例子, 它的UML模型展示如下：<br /><img src="http://www.infoq.com/resource/articles/migrating-struts-2-part2/en/resources/image2.jpg" border="0" onmouseover="if(this.width&gt;screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://www.infoq.com/resource/articles/migrating-struts-2-part2/en/resources/image2.jpg');}" alt="" /><br /><br />&nbsp; &nbsp;&nbsp;&nbsp;每个用例在action中都有自己所对应的方法。从上图中我们可以看到，在<strong>BlogAction</strong> 中我们有save, update 和 remove三个方法。而<strong>ListBlogAction</strong>中，没有list这个方法，因为<strong>ListBlogAction</strong>继承了<strong>ActionSupport</strong> 类，实际上就是在默认的execute 方法中实现list功能。<br />&nbsp; &nbsp;&nbsp;&nbsp;为了更容易看，图中的<strong>BlogAction</strong>并没有画出它所实现了的三个接口。它们分别是<strong>ServletRequestAware</strong> 接口,&nbsp;&nbsp;<strong>Prepareable</strong> 接口和 <strong>ModelDriven</strong> 接口。<br />&nbsp; &nbsp;&nbsp;&nbsp;首先回顾一下<strong>ServletRequestAware</strong>, 我们在第一篇文章中已经详细介绍它了。这个<strong>ParametersInterceptor</strong> 拦截器提供了把<strong>HttpServletRequest</strong> 自动set到action中的功能，让我们能通过request, 把所需的值传回到JSPs。<br />&nbsp; &nbsp;&nbsp;&nbsp;接着看看<strong>Preparable</strong> 接口， 它会联合<strong>PrepareInterceptor</strong>拦截器一起工作，让action在执行execute() 方法前， 执行一个prepare()方法，实现在执行前设定，配置或预设一些值在action中。 在我们的例子里，prepare方法会检查<strong>blogId</strong> 属性,如果为零则这是一个新日志，非零则该日志已经存在，根据<strong>blogId</strong>取出日志。<br />&nbsp; &nbsp;&nbsp;&nbsp;最后我们说说<strong>ModelDriven</strong> 接口，在上一篇文章中，我们已经了解到 Struts action的很大的不同在于它是需要线程安全的，而在Struts2中则没有这个限制，因为每次的请求都会有一次action对象的初始化和调用。没有了这个限制，能允许Struts2使用类级别的属性变量(特别是getters和setters)，从而获得更多编码优势。<br /><br />和拦截器的功能结合起来， 把<strong>HttpServletRequest</strong> 中的attribute 注入action中的流程如下所示:</p>
<ul>
<li>循环读取HTTP request中的attribute </li>
<li>查找当前request attribute中是否有和action中的setter中的属性匹配的 </li>
<li>有则根据attribute从<strong>HttpServletRequest</strong> 里取出其值 </li>
<li>把取出来的值由String转成setter中相应的类型 </li>
<li>调用setter把该转换后的值注入action中 </li>
</ul>
<ol>&nbsp;</ol>
<blockquote><strong>提示</strong>: 当调用action时，如果发现不明原因使不能正确地通过setter注入值情况下，第一步最好是先检查下各拦截器，确保它们都已作用于该action。因为这些意外通常有时由拦截器设置不当形成的，检查是否各个拦截器都已起作用，并看看它们作用的顺序，因为有些情况下它们间会相互影响而产生错误。</blockquote>
<p>&nbsp; &nbsp; 现在我们已经有基于String类型的form bean中取值的方法或者是自动把request的attributes 注入到action的方法，那下一步就是如何把值传入 domain object 或 value / transfer object的属性中去。其实这很简单，你只需要实现<strong>ModelDriven</strong> 接口(即实现<strong>getModel()</strong>方法)就可以了，确保<strong>ModelDrivenInterceptor</strong> 拦截器已作用于action。<br />&nbsp; &nbsp; 除了会调用action中的setter外，model 首先检查是否有和setter可以匹配当前的attribute名。如果在model中没有这个attribute相应的setter，则会再在action上找相应的setter来设值。<br />&nbsp; &nbsp; 在<strong>BlogAction</strong> 的例子中我们可以看到如何很灵活地使用这些方法，首先通过prepare() 方法根据Id获取相应的 <strong>Blog</strong> model object 或新建一个instance, 然后再根据把request中相应的属性注入Blog instance中和action中。<br />&nbsp; &nbsp; 以上的两个功能使得现在调用action那么简单 - 调用具体的业务逻辑，和把数据设在<strong>HttpServletRequest</strong>供返回用<strong>。</strong><br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code8'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code8">public class BlogAction extends ActionSupport<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;implements ModelDriven, Preparable, ServletRequestAware {<br /><br />&nbsp; &nbsp;&nbsp;&nbsp;private int blogId;<br />&nbsp; &nbsp;&nbsp;&nbsp;private Blog blog;<br />&nbsp; &nbsp;&nbsp;&nbsp;private BlogService service = new BlogService();<br />&nbsp; &nbsp;&nbsp;&nbsp;private HttpServletRequest request;<br /><br />&nbsp; &nbsp;&nbsp;&nbsp;public void setServletRequest(HttpServletRequest httpServletRequest) {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;this.request = httpServletRequest;<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;public void setId(int blogId) {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;this.blogId = blogId;<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;public void prepare() throws Exception {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if( blogId==0 ) {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; blog = new Blog();<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;} else {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; blog = service.findById(blogId);<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;public Object getModel() {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return blog;<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;public String save() {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;service.create(blog);<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return SUCCESS;<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br />&nbsp; &nbsp;&nbsp; &nbsp;public String update() {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;service.update(blog);<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;request.setAttribute("blog",blog);<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return SUCCESS;<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;public String remove() {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;service.delete(blogId);<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return SUCCESS;<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;public String execute() {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;request.setAttribute("blog",blog);<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return SUCCESS;<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br /><br /><br />} </div>
<p><br />最后就是说说 list这个用例了。它同样需要访问HttpServletRequest对象去返回数据给JSP,所以也需要实现ServletRequestAware 接口。但是，因为它并不需要任何输入值，所以就不需要实现其他的接口了。以下是它的具体实现：<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code9'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code9">public class ListBlogsAction extends ActionSupport implements ServletRequestAware {<br /><br />&nbsp; &nbsp;&nbsp;&nbsp;private BlogService service = new BlogService();<br />&nbsp; &nbsp;&nbsp;&nbsp;private HttpServletRequest request;<br /><br />&nbsp; &nbsp;&nbsp;&nbsp;public void setServletRequest(HttpServletRequest httpServletRequest) {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;this.request = httpServletRequest;<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br /><br />&nbsp; &nbsp;&nbsp;&nbsp;public String execute() {<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;request.setAttribute("bloglist",service.list());<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return SUCCESS;<br />&nbsp; &nbsp;&nbsp;&nbsp;}<br /><br />}</div>
<p><br />这样就完成了我们该实现的action代码了。 在下一篇文章中，当我们新的Struts2用户界面结合时，我们还会进一步简化action的代码。 <br /><br /><br /><strong><span style="font-size: small;">配置Actions</span></strong><br />&nbsp; &nbsp; 在我们调用action之前，我们必须通过XML配置文件去配置它们。<br />&nbsp; &nbsp; 在Struts中, 我们习惯用在WEB-INF 目录的"struts-config.xml"配置文件，在这里我们需要配置action form和action属性。在Struts2中， 用的是在classpath中的"struts.xml"配置文件， 它看起来好象会稍微复杂一些，因为它需要在配置action的同时也配置其拦截器。<br /><br />&nbsp; &nbsp; 在Struts中配置 <strong>form-beans</strong> 节点很容易， 只需要一个唯一的名字，还有就是继承ActionForm类的class作为type。<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code10'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code10">&lt;struts-config&gt;<br /><br />&nbsp; &nbsp;&nbsp;&nbsp;&lt;form-beans&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;form-bean name="blogForm"<br />&nbsp; &nbsp; type="com.fdar.articles.infoq.conversion.struts.BlogForm"/&gt;<br />&nbsp; &nbsp;&nbsp;&nbsp;&lt;/form-beans&gt;<br />&nbsp; &nbsp;&nbsp;&nbsp;...<br /><br />&lt;/struts-config&gt; </div>
<p><br />在我们的例子中，我们的配置文件有3点不相同：<br /><em><strong>1. 重定向配置</strong></em><br />&nbsp; &nbsp; 在Struts的配置中，每个mapping 都需要提供调用action时所需要对应的路径，Struts默认为".do", 例如paht是"/struts/add"对应于URL"/struts/add.do"。同时也需要一个<strong>forward</strong> 属性来提供给URL去转向，如"/struts/add.jsp".<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code11'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code11">&lt;struts-config&gt;<br />&nbsp; &nbsp;&nbsp;&nbsp;...<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&lt;action-mappings&gt;<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;action path="/struts/add" forward="/struts/add.jsp"/&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;...<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&lt;/action-mappings&gt;<br />&lt;/struts-config&gt; </div>
<p><br />而Struts2的需要更多的一些配置，如:<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code12'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code12">&lt;struts&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&lt;include file="struts-default.xml"/&gt;<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&lt;package name="struts2" extends="struts-default" namespace="/struts2"&gt;<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;action name="add" &gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;result&gt;/struts2/add.jsp&lt;/result&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;/action&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;...<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&lt;/package&gt;<br />&lt;/struts&gt; </div>
<p><br />首先你会注意到的是，代替<strong>action-mappings</strong> 节点的是<strong>include</strong> 和<strong>package</strong> 节点。Struts2可以把配置细分到任意数目的配置文件中，来实现配置可模块化管理。每个配置文件的结构其实都是一样的，不同的只是文件名。<br />&nbsp; &nbsp; <strong>include</strong> 节点中，以文件名作为<strong>file</strong> 属性，可把所include的文件内容包含到当前文件中。<br />&nbsp; &nbsp;<strong>package</strong> 节点把actions组成一组，其<strong>name </strong>属性的值必须是唯一的。<br />&nbsp; &nbsp;在 Struts action的配置中， paht属性需要指定完整的URL路径。而在Struts2中，URL是通过<strong>package</strong>节点中的<strong>namespace</strong>属性，还有在<strong>action</strong> 节点中的<strong>name</strong> 属性， 和action扩展(默认是".action")共同起作用的。在上面的例子中，则URL为"/struts2/add.action"时会调用action。<br />&nbsp; &nbsp;package节点除了可以分离命名空间外， package 节点中的 <strong>extends</strong> 属性，还提供了某种可复合的组成结构。通过继承另外一个package节点，你就能继承那个节点的配置,包括其actions, results, interceptors, exception,等值。在我们的例子中，"struts2" package节点继承了 "struts-default" package 节点(在"struts-default.xml" 文件里定义了该节点) ，注意这个是主要的include文件，所以必须在所有配置之前的第一行中写出。 这个功能有助于大大减少你重复性输入默认配置所浪费的时间。<br />&nbsp; &nbsp; 最后是<strong>result</strong> 节点， 它只是存放你这个action所需要转向的URL. 在这里我们没有提及<strong>name</strong> 和 <strong>type</strong> 属性。如果你不想改变它们的默认属性的话，你能忽略不写它们，让你的配置文件看起来更清晰。从action返回的 "success" 的结果将组成这个JSP显示给用户。<br /><br /><br /><em><strong>2. Action 配置</strong></em><br />&nbsp; &nbsp; 在Struts 中<strong>forward</strong> 节点指定了action处理后，结果将重定向到哪个相应的页面。<strong>type</strong>属性指定了action的类，<strong>scope</strong> 属性保证了form beans只在request范围内。<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code13'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code13">&lt;struts-config&gt;<br />&nbsp; &nbsp;&nbsp;&nbsp;...<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&lt;action-mappings&gt;<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;action path="/struts/list" scope="request"<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;type="com.fdar.articles.infoq.conversion.struts.ListBlogsAction" &gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;forward name="success" path="/struts/list.jsp"/&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;/action&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;...<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&lt;/action-mappings&gt;<br />&lt;/struts-config&gt;</div>
<p><br />Struts2 的 XML配置和上面提到的基本相同。唯一不同的就是通过class属性为action节点提供了它所需要调用的类的完整路径<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code14'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code14">&lt;struts&gt;<br />&nbsp; &nbsp;&nbsp;&nbsp;...<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&lt;package name="struts2" extends="struts-default" namespace="/struts2"&gt;<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;default-interceptor-ref name="defaultStack"/&gt;<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;action name="list"<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;class="com.fdar.articles.infoq.conversion.struts2.ListBlogsAction"&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;result&gt;/struts2/list.jsp&lt;/result&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;interceptor-ref name="basicStack"/&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;/action&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;...<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&lt;/package&gt;<br />&lt;/struts&gt;</div>
<p><br />如果是用其他的方法而不是用默认的execute 方法去调用action(在<strong>BlogAction</strong> 类中大多数方法如此)， 则需要在action节点的 <strong>method </strong>属性里加入方法名，下面就是个例子，这时候update方法将会被调用。<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code15'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code15">&lt;action name="update" method="update"<br />&nbsp; &nbsp; class="com.fdar.articles.infoq.conversion.struts2.BlogAction" &gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;...<br />&nbsp; &nbsp; &lt;/action&gt; </div>
<p><br /><strong>default-interceptor-ref</strong> 和 <strong>interceptor-ref </strong>节点有几点不同。在第一篇文章中，我们看到在action被调用之前必须通过一系列的拦截器，而这两个节点就是用来配置拦截器组的。<strong>default-interceptor-ref</strong> 节点为该package提供了默认的拦截器组。当在action节点中提供 <strong>interceptor-ref</strong>节点时 ，它就会覆盖默认的拦截器(<strong>interceptor-ref</strong> 节点能够和单独一个拦截器相关联，或者跟一个拦截器组相关联)，在action节点中可以存在多个<strong>interceptor-ref</strong>节点，处理拦截器组的顺序会和该节点列出的顺序一致。<br /><br /><br /><em><strong>3. 再重定向配置</strong><br /></em>&nbsp; &nbsp; 当我们提交表格的时候，我们需要重定向到更新后的结果页面。这个通常称为 "post-redirect pattern" 或, 最近出现的, "flash scope."<br />&nbsp; &nbsp; 由于这是一个form， 所以在Struts中我们需要为Struts指定一个<strong>ActionForm</strong>。需要在name属性中提供form的名称，同样地，我们也需要在<strong>forward</strong> 节点中举加入redirect属性为true。<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code16'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code16">&lt;struts-config&gt;<br />&nbsp; &nbsp;&nbsp;&nbsp;...<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&lt;action-mappings&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;action path="/struts/save"<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;type="com.fdar.articles.infoq.conversion.struts.SaveBlogEntryAction"<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;name="blogForm" scope="request"&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;forward name="success" redirect="true" path="/struts/list.do"/&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;/action&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;...<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&lt;/action-mappings&gt;<br />&lt;/struts-config&gt; </div>
<p><br />Struts2 在<strong>result </strong>节点里提供了<strong>type</strong> 属性, 默认情况下是"dispatch", 如果需要重定向，则需要设为 "redirect"。<br /></p>
<div class="msgheader">
<div class="right"><a href="http://forum.springside.org.cn/viewthread.php?tid=192&amp;extra=page%3D4#" class="smalltxt" onclick="copycode($('code17'));">[Copy to clipboard]</a></div>
CODE:</div>
<div class="msgborder" id="code17">&lt;struts&gt;<br />&nbsp; &nbsp;&nbsp;&nbsp;...<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&lt;package name="struts2" extends="struts-default" namespace="/struts2"&gt;<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;action name="save" method="save"<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;class="com.fdar.articles.infoq.conversion.struts2.BlogAction" &gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;result type="redirect"&gt;list.action&lt;/result&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;interceptor-ref name="defaultStack"/&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;/action&gt;<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;...<br /><br />&nbsp; &nbsp;&nbsp; &nbsp;&lt;/package&gt;<br />&lt;/struts&gt; </div>
<p><br /><strong><span style="font-size: small;">总结</span></strong><br />&nbsp; &nbsp; 我们并不可能在这篇文章中覆盖所有的内容，如果你需要更好的了解整个框架，还有其他的实现方式和选项，这里有几点可以供你参考：<br /></p>
<ul>
<li><em><strong>配置拦截器和拦截器组</strong></em> - 以Struts2-core JAR 包里的"struts-default.xml" 文件作为例子。"struts-default.xml" 演示了如何配置你自己的拦截器组，包含新的拦截器，你可以尝试实现自己的拦截器。 </li>
<li><em><strong>配置文件中的通配符模式</strong></em> - 你可以选择使用Struts2中的通配符模式来简化你的配置。 </li>
<li><em><strong>通过 ParameterAware 接口把form值传入maps中</strong></em> - 你可以在Struct2中配置，让所有request的form属性都存于action的一个map中，这样就不需要专门再为action指定model / transfer / value object了。这和Struts的dynamic form特点很相似。 </li>
</ul>
<ol>&nbsp;</ol>
<p>&nbsp; &nbsp; 也许到现在为，也许你有个疑问,"迁移后我们的界面是否可以完全重用呢?"，答案是yes。你能从<a href="http://www.infoq.com/resource/articles/migrating-struts-2-part2/en/resources/convertingtostruts2-part-ii.zip" target="_blank">这里</a>, 下载到我这篇文章中的完整源代码，你可以自己尝试把URL的扩展名由".do" 改为 ".action"，使用的页面时一样的。除此之外，其实用JSTL来代替Struts taglib也是很容易的。<br />在下一篇文章中，我们将讲述用户界面，讨论themes 和 tags; 如何做validation;&nbsp;&nbsp;如何重用UI控件。<br />(自 <a href="http://www.infoq.com/" target="_blank">http://www.infoq.com</a> , cac 翻译)</p>
          <br/><br/>
          <span style="color:red;">
            <a href="http://billgmh.javaeye.com/blog/213391#comments" style="color:red;">已有 <strong>0</strong> 人发表留言，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 10 Jul 2008 11:58:43 +0800</pubDate>
        <link>http://billgmh.javaeye.com/blog/213391</link>
        <guid>http://billgmh.javaeye.com/blog/213391</guid>
      </item>
          <item>
        <title>如何提高hibernate性能 [转载]</title>
        <author>billgmh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://billgmh.javaeye.com">billgmh</a>&nbsp;
                    链接：<a href="http://billgmh.javaeye.com/blog/208503" style="color:red;">http://billgmh.javaeye.com/blog/208503</a>&nbsp;
          发表时间: 2008年06月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">在一个拥有单独业务层的应用中，业务层必须在返回之前，为web层&ldquo;准备&rdquo;好其所需的数据集合。这就意味着 业务层应该载入所有表现层/web层所需的数据，并将这些已实例化完毕的数据返回。通常，应用程序应该 为web层所需的每个集合调用<tt><span style="font-family: NSimsun;">Hibernate.initialize()</span></tt>（这个调用必须发生咱session关闭之前）； 或者使用带有<tt><span style="font-family: NSimsun;">FETCH</span></tt>从句，或<tt><span style="font-family: NSimsun;">FetchMode.JOIN</span></tt>的Hibernate查询， 事先取得所有的数据集合。如果你在应用中使用了Command模式，代替Session Facade ， 那么这项任务将会变得简单的多。</p>
</li>
<li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">你也可以通过<tt><span style="font-family: NSimsun;">merge()</span></tt>或<tt><span style="font-family: NSimsun;">lock()</span></tt>方法，在访问未实例化的集合（或代理）之前， 为先前载入的对象绑定一个新的<tt><span style="font-family: NSimsun;">Session</span></tt>。 显然，Hibernate将不会，也不应该自动完成这些任务，因为这将引入一个特殊的事务语义。</p>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">有时候，你并不需要完全实例化整个大的集合，仅需要了解它的部分信息（例如其大小）、或者集合的部分内容。</p>
<p style="TEXT-INDENT: 2em">你可以使用集合过滤器得到其集合的大小，而不必实例化整个集合：</p>
<p style="TEXT-INDENT: 2em">( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()</p>
<p style="TEXT-INDENT: 2em">这里的<tt><span style="font-family: NSimsun;">createFilter()</span></tt>方法也可以被用来有效的抓取集合的部分内容，而无需实例化整个集合：</p>
<p style="TEXT-INDENT: 2em">s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.1.5. 使用批量抓取（Using batch fetching）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">Hibernate可以充分有效的使用批量抓取，也就是说，如果仅一个访问代理（或集合），那么Hibernate将不载入其他未实例化的代理。 批量抓取是延迟查询抓取的优化方案，你可以在两种批量抓取方案之间进行选择：在类级别和集合级别。</p>
<p style="TEXT-INDENT: 2em">类/实体级别的批量抓取很容易理解。假设你在运行时将需要面对下面的问题：你在一个<tt><span style="font-family: NSimsun;">Session</span></tt>中载入了25个 <tt><span style="font-family: NSimsun;">Cat</span></tt>实例，每个<tt><span style="font-family: NSimsun;">Cat</span></tt>实例都拥有一个引用成员<tt><span style="font-family: NSimsun;">owner</span></tt>， 其指向<tt><span style="font-family: NSimsun;">Person</span></tt>，而<tt><span style="font-family: NSimsun;">Person</span></tt>类是代理，同时<tt><span style="font-family: NSimsun;">lazy="true"</span></tt>。 如果你必须遍历整个cats集合，对每个元素调用<tt><span style="font-family: NSimsun;">getOwner()</span></tt>方法，Hibernate将会默认的执行25次<tt><span style="font-family: NSimsun;">SELECT</span></tt>查询， 得到其owner的代理对象。这时，你可以通过在映射文件的<tt><span style="font-family: NSimsun;">Person</span></tt>属性，显式声明<tt><span style="font-family: NSimsun;">batch-size</span></tt>，改变其行为：</p>
<p style="TEXT-INDENT: 2em">&lt;class name="Person" batch-size="10"&gt;...&lt;/class&gt;</p>
<p style="TEXT-INDENT: 2em">随之，Hibernate将只需要执行三次查询，分别为10、10、 5。</p>
<p style="TEXT-INDENT: 2em">你也可以在集合级别定义批量抓取。例如，如果每个<tt><span style="font-family: NSimsun;">Person</span></tt>都拥有一个延迟载入的<tt><span style="font-family: NSimsun;">Cats</span></tt>集合， 现在，<tt><span style="font-family: NSimsun;">Sesssion</span></tt>中载入了10个person对象，遍历person集合将会引起10次<tt><span style="font-family: NSimsun;">SELECT</span></tt>查询， 每次查询都会调用<tt><span style="font-family: NSimsun;">getCats()</span></tt>方法。如果你在<tt><span style="font-family: NSimsun;">Person</span></tt>的映射定义部分，允许对<tt><span style="font-family: NSimsun;">cats</span></tt>批量抓取, 那么，Hibernate将可以预先抓取整个集合。请看例子：</p>
<p style="TEXT-INDENT: 2em">&lt;class name="Person"&gt; &lt;set name="cats" batch-size="3"&gt; ... &lt;/set&gt;&lt;/class&gt;</p>
<p style="TEXT-INDENT: 2em">如果整个的<tt><span style="font-family: NSimsun;">batch-size</span></tt>是3（笔误？），那么Hibernate将会分四次执行<tt><span style="font-family: NSimsun;">SELECT</span></tt>查询， 按照3、3、3、1的大小分别载入数据。这里的每次载入的数据量还具体依赖于当前<tt><span style="font-family: NSimsun;">Session</span></tt>中未实例化集合的个数。</p>
<p style="TEXT-INDENT: 2em">如果你的模型中有嵌套的树状结构，例如典型的帐单－原料结构（bill-of-materials pattern），集合的批量抓取是非常有用的。 （尽管在更多情况下对树进行读取时，嵌套集合（nested set）或原料路径(materialized path)（&times;&times;） 是更好的解决方法。）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.1.6. 使用子查询抓取（Using subselect fetching）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">假若一个延迟集合或单值代理需要抓取，Hibernate会使用一个subselect重新运行原来的查询，一次性读入所有的实例。这和批量抓取的实现方法是一样的，不会有破碎的加载。</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.1.7. 使用延迟属性抓取（Using lazy property fetching）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">Hibernate3对单独的属性支持延迟抓取，这项优化技术也被称为组抓取（fetch groups）。 请注意，该技术更多的属于市场特性。在实际应用中，优化行读取比优化列读取更重要。但是，仅载入类的部分属性在某些特定情况下会有用，例如在原有表中拥有几百列数据、数据模型无法改动的情况下。</p>
<p style="TEXT-INDENT: 2em">可以在映射文件中对特定的属性设置<tt><span style="font-family: NSimsun;">lazy</span></tt>，定义该属性为延迟载入。</p>
<p style="TEXT-INDENT: 2em">&lt;class name="Document"&gt; &lt;id name="id"&gt; &lt;generator class="native"/&gt; &lt;/id&gt; &lt;property name="name" not-null="true" length="50"/&gt; &lt;property name="summary" not-null="true" length="200" lazy="true"/&gt; &lt;property name="text" not-null="true" length="2000" lazy="true"/&gt;&lt;/class&gt;</p>
<p style="TEXT-INDENT: 2em">属性的延迟载入要求在其代码构建时加入二进制指示指令（bytecode instrumentation），如果你的持久类代码中未含有这些指令， Hibernate将会忽略这些属性的延迟设置，仍然将其直接载入。</p>
<p style="TEXT-INDENT: 2em">你可以在Ant的Task中，进行如下定义，对持久类代码加入&ldquo;二进制指令。&rdquo;</p>
<p style="TEXT-INDENT: 2em">&lt;target name="instrument" depends="compile"&gt; &lt;taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask"&gt; &lt;classpath path="${jar.path}"/&gt; &lt;classpath path="${classes.dir}"/&gt; &lt;classpath refid="lib.class.path"/&gt; &lt;/taskdef&gt; &lt;instrument verbose="true"&gt; &lt;fileset dir="${testclasses.dir}/org/hibernate/auction/model"&gt; &lt;include name="*.class"/&gt; &lt;/fileset&gt; &lt;/instrument&gt;&lt;/target&gt;</p>
<p style="TEXT-INDENT: 2em">还有一种可以优化的方法，它使用HQL或条件查询的投影（projection）特性，可以避免读取非必要的列， 这一点至少对只读事务是非常有用的。它无需在代码构建时&ldquo;二进制指令&rdquo;处理，因此是一个更加值得选择的解决方法。</p>
<p style="TEXT-INDENT: 2em">有时你需要在HQL中通过<tt><span style="font-family: NSimsun;">抓取所有属性</span></tt>，强行抓取所有内容。</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.2. 二级缓存（The Second Level Cache）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">Hibernate的<tt><span style="font-family: NSimsun;">Session</span></tt>在事务级别进行持久化数据的缓存操作。 当然，也有可能分别为每个类（或集合)，配置集群、或JVM级别(<tt><span style="font-family: NSimsun;">SessionFactory级别</span></tt>)的缓存。 你甚至可以为之插入一个集群的缓存。注意，缓存永远不知道其他应用程序对持久化仓库（数据库）可能进行的修改 （即使可以将缓存数据设定为定期失效）。</p>
<p style="TEXT-INDENT: 2em">默认情况下，Hibernate使用EHCache进行JVM级别的缓存（目前，Hibernate已经废弃了对JCS的支持，未来版本中将会去掉它）。 你可以通过设置<tt><span style="font-family: NSimsun;">hibernate.cache.provider_class</span></tt>属性，指定其他的缓存策略， 该缓存策略必须实现<tt><span style="font-family: NSimsun;">org.hibernate.cache.CacheProvider</span></tt>接口。</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">表 20.1. 缓存策略提供商（Cache Providers） 
<table border="1" summary="&amp;10;&amp;9;&amp;9;&amp;9;&amp;9;缓存策略提供商(Cache Providers)&amp;10;&amp;9;&amp;9;&amp;9;">
<colgroup span="1"><col align="left" span="1" /><col align="left" span="1" /><col align="left" span="1" /><col align="left" span="1" /><col align="left" span="1" /></colgroup><thead>
<tr>
<th align="left">Cache</th><th align="left">Provider class</th><th align="left">Type</th><th align="left">Cluster Safe</th><th align="left">Query Cache Supported</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">Hashtable (not intended for production use)</td>
<td align="left"><tt><span style="font-family: NSimsun;">org.hibernate.cache.HashtableCacheProvider</span></tt></td>
<td align="left">memory</td>
<td align="left">&nbsp;</td>
<td align="left">yes</td>
</tr>
<tr>
<td align="left">EHCache</td>
<td align="left"><tt><span style="font-family: NSimsun;">org.hibernate.cache.EhCacheProvider</span></tt></td>
<td align="left">memory, disk</td>
<td align="left">&nbsp;</td>
<td align="left">yes</td>
</tr>
<tr>
<td align="left">OSCache</td>
<td align="left"><tt><span style="font-family: NSimsun;">org.hibernate.cache.OSCacheProvider</span></tt></td>
<td align="left">memory, disk</td>
<td align="left">&nbsp;</td>
<td align="left">yes</td>
</tr>
<tr>
<td align="left">SwarmCache</td>
<td align="left"><tt><span style="font-family: NSimsun;">org.hibernate.cache.SwarmCacheProvider</span></tt></td>
<td align="left">clustered (ip multicast)</td>
<td align="left">yes (clustered invalidation)</td>
<td align="left">&nbsp;</td>
</tr>
<tr>
<td align="left">JBoss TreeCache</td>
<td align="left"><tt><span style="font-family: NSimsun;">org.hibernate.cache.TreeCacheProvider</span></tt></td>
<td align="left">clustered (ip multicast), transactional</td>
<td align="left">yes (replication)</td>
<td align="left">yes (clock sync req.)</td>
</tr>
</tbody>
</table>
</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.2.1. 缓存映射（Cache mappings）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">类或者集合映射的&ldquo;<tt><span style="font-family: NSimsun;">&lt;cache&gt;</span></tt>元素&rdquo;可以有下列形式：</p>
<p style="TEXT-INDENT: 2em">&lt;cache usage="transactional|read-write|nonstrict-read-write|read-only" (1)/&gt;</p>
<p style="TEXT-INDENT: 2em">
<table class="FCK__ShowTableBorders" border="0" summary="Callout list">
<tbody>
<tr>
<td align="left" valign="top" width="5%">(1)</td>
<td align="left" valign="top">
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em"><tt><span style="font-family: NSimsun;">usage</span></tt>说明了缓存的策略: <tt><span style="font-family: NSimsun;">transactional</span></tt>、 <tt><span style="font-family: NSimsun;">read-write</span></tt>、 <tt><span style="font-family: NSimsun;">nonstrict-read-write</span></tt>或 <tt><span style="font-family: NSimsun;">read-only</span></tt>。</p>
</td>
</tr>
</tbody>
</table>
</p>
<p style="TEXT-INDENT: 2em">另外(首选?), 你可以在hibernate.cfg.xml中指定<tt><span style="font-family: NSimsun;">&lt;class-cache&gt;</span></tt>和 <tt><span style="font-family: NSimsun;">&lt;collection-cache&gt;</span></tt> 元素。</p>
<p style="TEXT-INDENT: 2em">这里的<tt><span style="font-family: NSimsun;">usage</span></tt> 属性指明了缓存并发策略（cache concurrency strategy）。</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.2.2. 策略：只读缓存（Strategy: read only）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">如果你的应用程序只需读取一个持久化类的实例，而无需对其修改， 那么就可以对其进行<tt><span style="font-family: NSimsun;">只读</span></tt> 缓存。这是最简单，也是实用性最好的方法。甚至在集群中，它也能完美地运作。</p>
<p style="TEXT-INDENT: 2em">&lt;class name="eg.Immutable" mutable="false"&gt; &lt;cache usage="read-only"/&gt; ....&lt;/class&gt;</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.2.3. 策略:读/写缓存（Strategy: read/write）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">如果应用程序需要更新数据，那么使用<tt><span style="font-family: NSimsun;">读/写缓存</span></tt> 比较合适。 如果应用程序要求&ldquo;序列化事务&rdquo;的隔离级别（serializable transaction isolation level），那么就决不能使用这种缓存策略。 如果在JTA环境中使用缓存，你必须指定<tt><span style="font-family: NSimsun;">hibernate.transaction.manager_lookup_class</span></tt>属性的值， 通过它，Hibernate才能知道该应用程序中JTA的<tt><span style="font-family: NSimsun;">TransactionManager</span></tt>的具体策略。 在其它环境中，你必须保证在<tt><span style="font-family: NSimsun;">Session.close()</span></tt>、或<tt><span style="font-family: NSimsun;">Session.disconnect()</span></tt>调用前， 整个事务已经结束。 如果你想在集群环境中使用此策略，你必须保证底层的缓存实现支持锁定(locking)。Hibernate内置的缓存策略并不支持锁定功能。</p>
<p style="TEXT-INDENT: 2em">&lt;class name="eg.Cat" .... &gt; &lt;cache usage="read-write"/&gt; .... &lt;set name="kittens" ... &gt; &lt;cache usage="read-write"/&gt; .... &lt;/set&gt;&lt;/class&gt;</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.2.4. 策略:非严格读/写缓存（Strategy: nonstrict read/write）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">如果应用程序只偶尔需要更新数据（也就是说，两个事务同时更新同一记录的情况很不常见），也不需要十分严格的事务隔离， 那么比较适合使用<tt><span style="font-family: NSimsun;">非严格读/写缓存</span></tt>策略。如果在JTA环境中使用该策略， 你必须为其指定<tt><span style="font-family: NSimsun;">hibernate.transaction.manager_lookup_class</span></tt>属性的值， 在其它环境中，你必须保证在<tt><span style="font-family: NSimsun;">Session.close()</span></tt>、或<tt><span style="font-family: NSimsun;">Session.disconnect()</span></tt>调用前， 整个事务已经结束。</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.2.5. 策略:事务缓存（transactional）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">Hibernate的<tt><span style="font-family: NSimsun;">事务缓存</span></tt>策略提供了全事务的缓存支持， 例如对JBoss TreeCache的支持。这样的缓存只能用于JTA环境中，你必须指定 为其<tt><span style="font-family: NSimsun;">hibernate.transaction.manager_lookup_class</span></tt>属性。</p>
<p style="TEXT-INDENT: 2em">没有一种缓存提供商能够支持上列的所有缓存并发策略。下表中列出了各种提供器、及其各自适用的并发策略。</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">表 20.2. 各种缓存提供商对缓存并发策略的支持情况（Cache Concurrency Strategy Support） 
<table border="1" summary="&amp;10;&amp;9;&amp;9;&amp;9;各种缓存提供商对缓存并发策略的支持情况(Cache Concurrency Strategy Support)&amp;10;&amp;9;&amp;9;&amp;9;">
<colgroup span="1"><col align="left" span="1" /><col align="left" span="1" /><col align="left" span="1" /><col align="left" span="1" /><col align="left" span="1" /></colgroup><thead>
<tr>
<th align="left">Cache</th><th align="left">read-only</th><th align="left">nonstrict-read-write</th><th align="left">read-write</th><th align="left">transactional</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">Hashtable (not intended for production use)</td>
<td align="left">yes</td>
<td align="left">yes</td>
<td align="left">yes</td>
<td align="left">&nbsp;</td>
</tr>
<tr>
<td align="left">EHCache</td>
<td align="left">yes</td>
<td align="left">yes</td>
<td align="left">yes</td>
<td align="left">&nbsp;</td>
</tr>
<tr>
<td align="left">OSCache</td>
<td align="left">yes</td>
<td align="left">yes</td>
<td align="left">yes</td>
<td align="left">&nbsp;</td>
</tr>
<tr>
<td align="left">SwarmCache</td>
<td align="left">yes</td>
<td align="left">yes</td>
<td align="left">&nbsp;</td>
<td align="left">&nbsp;</td>
</tr>
<tr>
<td align="left">JBoss TreeCache</td>
<td align="left">yes</td>
<td align="left">&nbsp;</td>
<td align="left">&nbsp;</td>
<td align="left">yes</td>
</tr>
</tbody>
</table>
</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.3. 管理缓存（Managing the caches）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">无论何时，当你给<tt><span style="font-family: NSimsun;">save()</span></tt>、<tt><span style="font-family: NSimsun;">update()</span></tt>或 <tt><span style="font-family: NSimsun;">saveOrUpdate()</span></tt>方法传递一个对象时，或使用<tt><span style="font-family: NSimsun;">load()</span></tt>、 <tt><span style="font-family: NSimsun;">get()</span></tt>、<tt><span style="font-family: NSimsun;">list()</span></tt>、<tt><span style="font-family: NSimsun;">iterate()</span></tt> 或<tt><span style="font-family: NSimsun;">scroll()</span></tt>方法获得一个对象时, 该对象都将被加入到<tt><span style="font-family: NSimsun;">Session</span></tt>的内部缓存中。</p>
<p style="TEXT-INDENT: 2em">当随后flush()方法被调用时，对象的状态会和数据库取得同步。 如果你不希望此同步操作发生，或者你正处理大量对象、需要对有效管理内存时，你可以调用<tt><span style="font-family: NSimsun;">evict()</span></tt> 方法，从一级缓存中去掉这些对象及其集合。</p>
<p style="TEXT-INDENT: 2em">ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result setwhile ( cats.next() ) { Cat cat = (Cat) cats.get(0); doSomethingWithACat(cat); sess.evict(cat);}</p>
<p style="TEXT-INDENT: 2em">Session还提供了一个<tt><span style="font-family: NSimsun;">contains()</span></tt>方法，用来判断某个实例是否处于当前session的缓存中。</p>
<p style="TEXT-INDENT: 2em">如若要把所有的对象从session缓存中彻底清除，则需要调用<tt><span style="font-family: NSimsun;">Session.clear()</span></tt>。</p>
<p style="TEXT-INDENT: 2em">对于二级缓存来说，在<tt><span style="font-family: NSimsun;">SessionFactory</span></tt>中定义了许多方法， 清除缓存中实例、整个类、集合实例或者整个集合。</p>
<p style="TEXT-INDENT: 2em">sessionFactory.evict(Cat.class, catId); //evict a particular CatsessionFactory.evict(Cat.class); //evict all CatssessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittenssessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections</p>
<p style="TEXT-INDENT: 2em"><tt><span style="font-family: NSimsun;">CacheMode</span></tt>参数用于控制具体的Session如何与二级缓存进行交互。</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<ul type="disc">
<li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em"><tt><span style="font-family: NSimsun;">CacheMode.NORMAL</span></tt> - 从二级缓存中读、写数据。</p>
</li>
<li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em"><tt><span style="font-family: NSimsun;">CacheMode.GET</span></tt> - 从二级缓存中读取数据，仅在数据更新时对二级缓存写数据。</p>
</li>
<li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em"><tt><span style="font-family: NSimsun;">CacheMode.PUT</span></tt> - 仅向二级缓存写数据，但不从二级缓存中读数据。</p>
</li>
<li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em"><tt><span style="font-family: NSimsun;">CacheMode.REFRESH</span></tt> - 仅向二级缓存写数据，但不从二级缓存中读数据。通过 <tt><span style="font-family: NSimsun;">hibernate.cache.use_minimal_puts</span></tt>的设置，强制二级缓存从数据库中读取数据，刷新缓存内容。</p>
</li>
</ul>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">如若需要查看二级缓存或查询缓存区域的内容，你可以使用<tt><span style="font-family: NSimsun;">统计（Statistics）</span></tt> API。</p>
<p style="TEXT-INDENT: 2em">Map cacheEntries = sessionFactory.getStatistics() .getSecondLevelCacheStatistics(regionName) .getEntries();</p>
<p style="TEXT-INDENT: 2em">此时，你必须手工打开统计选项。可选的，你可以让Hibernate更人工可读的方式维护缓存内容。</p>
<p style="TEXT-INDENT: 2em">hibernate.generate_statistics truehibernate.cache.use_structured_entries true</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.4. 查询缓存（The Query Cache）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">查询的结果集也可以被缓存。只有当经常使用同样的参数进行查询时，这才会有些用处。 要使用查询缓存，首先你必须打开它：</p>
<p style="TEXT-INDENT: 2em">hibernate.cache.use_query_cache true</p>
<p style="TEXT-INDENT: 2em">该设置将会创建两个缓存区域 - 一个用于保存查询结果集(<tt><span style="font-family: NSimsun;">org.hibernate.cache.StandardQueryCache</span></tt>)； 另一个则用于保存最近查询的一系列表的时间戳(<tt><span style="font-family: NSimsun;">org.hibernate.cache.UpdateTimestampsCache</span></tt>)。 请注意：在查询缓存中，它并不缓存结果集中所包含的实体的确切状态；它只缓存这些实体的标识符属性的值、以及各值类型的结果。 所以查询缓存通常会和二级缓存一起使用。</p>
<p style="TEXT-INDENT: 2em">绝大多数的查询并不能从查询缓存中受益，所以Hibernate默认是不进行查询缓存的。如若需要进行缓存，请调用 <tt><span style="font-family: NSimsun;">Query.setCacheable(true)</span></tt>方法。这个调用会让查询在执行过程中时先从缓存中查找结果， 并将自己的结果集放到缓存中去。</p>
<p style="TEXT-INDENT: 2em">如果你要对查询缓存的失效政策进行精确的控制，你必须调用<tt><span style="font-family: NSimsun;">Query.setCacheRegion()</span></tt>方法， 为每个查询指定其命名的缓存区域。</p>
<p style="TEXT-INDENT: 2em">List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger") .setEntity("blogger", blogger) .setMaxResults(15) .setCacheable(true) .setCacheRegion("frontpages") .list();</p>
<p style="TEXT-INDENT: 2em">如果查询需要强行刷新其查询缓存区域，那么你应该调用<tt><span style="font-family: NSimsun;">Query.setCacheMode(CacheMode.REFRESH)</span></tt>方法。 这对在其他进程中修改底层数据（例如，不通过Hibernate修改数据），或对那些需要选择性更新特定查询结果集的情况特别有用。 这是对<tt><span style="font-family: NSimsun;">SessionFactory.evictQueries()</span></tt>的更为有效的替代方案，同样可以清除查询缓存区域。</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.5. 理解集合性能（Understanding Collection performance）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">前面我们已经对集合进行了足够的讨论。本段中，我们将着重讲述集合在运行时的事宜。</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.5.1. 分类（Taxonomy）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">Hibernate定义了三种基本类型的集合：</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<ul type="disc">
<li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">值数据集合</p>
</li>
<li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">一对多关联</p>
</li>
<li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">多对多关联</p>
</li>
</ul>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">这个分类是区分了不同的表和外键关系类型，但是它没有告诉我们关系模型的所有内容。 要完全理解他们的关系结构和性能特点，我们必须同时考虑&ldquo;用于Hibernate更新或删除集合行数据的主键的结构&rdquo;。 因此得到了如下的分类：</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<ul type="disc">
<li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">有序集合类</p>
</li>
<li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">集合（sets）</p>
</li>
<li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">包（bags)</p>
</li>
</ul>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">所有的有序集合类（maps, lists, arrays)都拥有一个由<tt><span style="font-family: NSimsun;">&lt;key&gt;</span></tt>和 <tt><span style="font-family: NSimsun;">&lt;index&gt;</span></tt>组成的主键。 这种情况下集合类的更新是非常高效的&mdash;&mdash;主键已经被有效的索引，因此当Hibernate试图更新或删除一行时，可以迅速找到该行数据。</p>
<p style="TEXT-INDENT: 2em">集合(sets)的主键由<tt><span style="font-family: NSimsun;">&lt;key&gt;</span></tt>和其他元素字段构成。 对于有些元素类型来说，这很低效，特别是组合元素或者大文本、大二进制字段； 数据库可能无法有效的对复杂的主键进行索引。 另一方面，对于一对多、多对多关联，特别是合成的标识符来说，集合也可以达到同样的高效性能。（ 附注：如果你希望<tt><span style="font-family: NSimsun;">SchemaExport</span></tt>为你的<tt><span style="font-family: NSimsun;">&lt;set&gt;</span></tt>创建主键， 你必须把所有的字段都声明为<tt><span style="font-family: NSimsun;">not-null="true"</span></tt>。）</p>
<p style="TEXT-INDENT: 2em"><tt><span style="font-family: NSimsun;">&lt;idbag&gt;</span></tt>映射定义了代理键，因此它总是可以很高效的被更新。事实上， <tt><span style="font-family: NSimsun;">&lt;idbag&gt;</span></tt>拥有着最好的性能表现。</p>
<p style="TEXT-INDENT: 2em">Bag是最差的。因为bag允许重复的元素值，也没有索引字段，因此不可能定义主键。 Hibernate无法判断出重复的行。当这种集合被更改时，Hibernate将会先完整地移除 （通过一个(in a single <tt><span style="font-family: NSimsun;">DELETE</span></tt>)）整个集合，然后再重新创建整个集合。 因此Bag是非常低效的。</p>
<p style="TEXT-INDENT: 2em">请注意：对于一对多关联来说，&ldquo;主键&rdquo;很可能并不是数据库表的物理主键。 但就算在此情况下，上面的分类仍然是有用的。（它仍然反映了Hibernate在集合的各数据行中是如何进行&ldquo;定位&rdquo;的。）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.5.2. Lists, maps 和sets用于更新效率最高</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">根据我们上面的讨论，显然有序集合类型和大多数set都可以在增加、删除、修改元素中拥有最好的性能。</p>
<p style="TEXT-INDENT: 2em">可论证的是对于多对多关联、值数据集合而言，有序集合类比集合(set)有一个好处。因为<tt><span style="font-family: NSimsun;">Set</span></tt>的内在结构， 如果&ldquo;改变&rdquo;了一个元素，Hibernate并不会<tt><span style="font-family: NSimsun;">更新（UPDATE）</span></tt>这一行。 对于<tt><span style="font-family: NSimsun;">Set</span></tt>来说，只有在<tt><span style="font-family: NSimsun;">插入（INSERT）</span></tt>和<tt><span style="font-family: NSimsun;">删除（DELETE）</span></tt> 操作时&ldquo;改变&rdquo;才有效。再次强调：这段讨论对&ldquo;一对多关联&rdquo;并不适用。</p>
<p style="TEXT-INDENT: 2em">注意到数组无法延迟载入，我们可以得出结论，list, map和idbags是最高效的（非反向）集合类型，set则紧随其后。 在Hibernate中，set应该时最通用的集合类型，这时因为&ldquo;set&rdquo;的语义在关系模型中是最自然的。</p>
<p style="TEXT-INDENT: 2em">但是，在设计良好的Hibernate领域模型中，我们通常可以看到更多的集合事实上是带有<tt><span style="font-family: NSimsun;">inverse="true"</span></tt> 的一对多的关联。对于这些关联，更新操作将会在多对一的这一端进行处理。因此对于此类情况，无需考虑其集合的更新性能。</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.5.3. Bag和list是反向集合类中效率最高的</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">在把bag扔进水沟之前，你必须了解，在一种情况下，bag的性能(包括list)要比set高得多： 对于指明了<tt><span style="font-family: NSimsun;">inverse="true"</span></tt>的集合类（比如说，标准的双向的一对多关联）， 我们可以在未初始化(fetch)包元素的情况下直接向bag或list添加新元素！ 这是因为<tt><span style="font-family: NSimsun;">Collection.add()</span></tt>)或者<tt><span style="font-family: NSimsun;">Collection.addAll()</span></tt> 方法 对bag或者List总是返回true（这点与与Set不同）。因此对于下面的相同代码来说，速度会快得多。</p>
<p style="TEXT-INDENT: 2em">Parent p = (Parent) sess.load(Parent.class, id); Child c = new Child(); c.setParent(p); p.getChildren().add(c); //no need to fetch the collection! sess.flush();</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.5.4. 一次性删除（One shot delete）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">偶尔的，逐个删除集合类中的元素是相当低效的。Hibernate并没那么笨， 如果你想要把整个集合都删除（比如说调用list.clear()），Hibernate只需要一个DELETE就搞定了。</p>
<p style="TEXT-INDENT: 2em">假设我们在一个长度为20的集合类中新增加了一个元素，然后再删除两个。 Hibernate会安排一条<tt><span style="font-family: NSimsun;">INSERT</span></tt>语句和两条<tt><span style="font-family: NSimsun;">DELETE</span></tt>语句（除非集合类是一个bag)。 这当然是显而易见的。</p>
<p style="TEXT-INDENT: 2em">但是，假设我们删除了18个数据，只剩下2个，然后新增3个。则有两种处理方式：</p>
<ul type="disc">
<li>
<p style="TEXT-INDENT: 2em">逐一的删除这18个数据，再新增三个；</p>
</li>
<li>
<p style="TEXT-INDENT: 2em">删除整个集合类（只用一句DELETE语句），然后增加5个数据。</p>
</li>
</ul>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">Hibernate还没那么聪明，知道第二种选择可能会比较快。 （也许让Hibernate不这么聪明也是好事，否则可能会引发意外的&ldquo;数据库触发器&rdquo;之类的问题。）</p>
<p style="TEXT-INDENT: 2em">幸运的是，你可以强制使用第二种策略。你需要取消原来的整个集合类（解除其引用）， 然后再返回一个新的实例化的集合类，只包含需要的元素。有些时候这是非常有用的。</p>
<p style="TEXT-INDENT: 2em">显然，一次性删除并不适用于被映射为<tt><span style="font-family: NSimsun;">inverse="true"</span></tt>的集合。</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.6. 监测性能（Monitoring performance）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">没有监测和性能参数而进行优化是毫无意义的。Hibernate为其内部操作提供了一系列的示意图，因此可以从 每个<tt><span style="font-family: NSimsun;">SessionFactory</span></tt>抓取其统计数据。</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.6.1. 监测SessionFactory</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">你可以有两种方式访问<tt><span style="font-family: NSimsun;">SessionFactory</span></tt>的数据记录，第一种就是自己直接调用 <tt><span style="font-family: NSimsun;">sessionFactory.getStatistics()</span></tt>方法读取、显示<tt><span style="font-family: NSimsun;">统计</span></tt>数据。</p>
<p style="TEXT-INDENT: 2em">此外，如果你打开<tt><span style="font-family: NSimsun;">StatisticsService</span></tt> MBean选项，那么Hibernate则可以使用JMX技术 发布其数据记录。你可以让应用中所有的<tt><span style="font-family: NSimsun;">SessionFactory</span></tt>同时共享一个MBean，也可以每个 SessionFactory分配一个MBean。下面的代码即是其演示代码：</p>
<p style="TEXT-INDENT: 2em">// MBean service registration for a specific SessionFactoryHashtable tb = new Hashtable();tb.put("type", "statistics");tb.put("sessionFactory", "myFinancialApp");ObjectName on = new ObjectName("hibernate", tb); // MBean object nameStatisticsService stats = new StatisticsService(); // MBean implementationstats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactoryserver.registerMBean(stats, on); // Register the Mbean on the server</p>
<p style="TEXT-INDENT: 2em">// MBean service registration for all SessionFactory'sHashtable tb = new Hashtable();tb.put("type", "statistics");tb.put("sessionFactory", "all");ObjectName on = new ObjectName("hibernate", tb); // MBean object nameStatisticsService stats = new StatisticsService(); // MBean implementationserver.registerMBean(stats, on); // Register the MBean on the server</p>
<p style="TEXT-INDENT: 2em">TODO：仍需要说明的是：在第一个例子中，我们直接得到和使用MBean；而在第二个例子中，在使用MBean之前 我们则需要给出SessionFactory的JNDI名，使用<tt><span style="font-family: NSimsun;">hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name")</span></tt> 得到SessionFactory，然后将MBean保存于其中。</p>
<p style="TEXT-INDENT: 2em">你可以通过以下方法打开或关闭<tt><span style="font-family: NSimsun;">SessionFactory</span></tt>的监测功能：</p>
<p style="TEXT-INDENT: 2em">&nbsp;在配置期间，将<tt><span style="font-family: NSimsun;">hibernate.generate_statistics</span></tt>设置为<tt><span style="font-family: NSimsun;">true</span></tt>或<tt><span style="font-family: NSimsun;">false</span></tt>；在运行期间，则可以可以通过<tt><span style="font-family: NSimsun;">sf.getStatistics().setStatisticsEnabled(true)</span></tt> 或<tt><span style="font-family: NSimsun;">hibernateStatsBean.setStatisticsEnabled(true)</span></tt></p>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">你也可以在程序中调用<tt><span style="font-family: NSimsun;">clear()</span></tt>方法重置统计数据，调用<tt><span style="font-family: NSimsun;">logSummary()</span></tt> 在日志中记录（info级别）其总结。</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">20.6.2. 数据记录（Metrics）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">Hibernate提供了一系列数据记录，其记录的内容包括从最基本的信息到与具体场景的特殊信息。所有的测量值都可以由 <tt><span style="font-family: NSimsun;">Statistics</span></tt>接口进行访问，主要分为三类：</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<ul type="disc">
<li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">使用<tt><span style="font-family: NSimsun;">Session</span></tt>的普通数据记录，例如打开的Session的个数、取得的JDBC的连接数等；</p>
</li>
<li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">实体、集合、查询、缓存等内容的统一数据记录</p>
</li>
<li>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">和具体实体、集合、查询、缓存相关的详细数据记录</p>
</li>
</ul>
<p>&nbsp;</p>
<p style="TEXT-INDENT: 2em">例如：你可以检查缓存的命中成功次数，缓存的命中失败次数，实体、集合和查询的使用概率，查询的平均时间等。请注意 Java中时间的近似精度是毫秒。Hibernate的数据精度和具体的JVM有关，在有些平台上其精度甚至只能精确到10秒。</p>
<p style="TEXT-INDENT: 2em">你可以直接使用getter方法得到全局数据记录（例如，和具体的实体、集合、缓存区无关的数据），你也可以在具体查询中通过标记实体名、 或HQL、SQL语句得到某实体的数据记录。请参考<tt><span style="font-family: NSimsun;">Statistics</span></tt>、<tt><span style="font-family: NSimsun;">EntityStatistics</span></tt>、 <tt><span style="font-family: NSimsun;">CollectionStatistics</span></tt>、<tt><span style="font-family: NSimsun;">SecondLevelCacheStatistics</span></tt>、 和<tt><span style="font-family: NSimsun;">QueryStatistics</span></tt>的API文档以抓取更多信息。下面的代码则是个简单的例子：</p>
<p style="TEXT-INDENT: 2em">Statistics stats = HibernateUtil.sessionFactory.getStatistics();double queryCacheHitCount = stats.getQueryCacheHitCount();double queryCacheMissCount = stats.getQueryCacheMissCount();double queryCacheHitRatio = queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);log.info("Query Hit ratio:" + queryCacheHitRatio);EntityStatistics entityStats = stats.getEntityStatistics( Cat.class.getName() );long changes = entityStats.getInsertCount() + entityStats.getUpdateCount() + entityStats.getDeleteCount();log.info(Cat.class.getName() + " changed " + changes + "times" );</p>
<p style="TEXT-INDENT: 2em">如果你想得到所有实体、集合、查询和缓存区的数据，你可以通过以下方法获得实体、集合、查询和缓存区列表： <tt><span style="font-family: NSimsun;">getQueries()</span></tt>、<tt><span style="font-family: NSimsun;">getEntityNames()</span></tt>、 <tt><span style="font-family: NSimsun;">getCollectionRoleNames()</span></tt>和 <tt><span style="font-family: NSimsun;">getSecondLevelCacheRegionNames()</span></tt>。</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
</li>
          <br/><br/>
          <span style="color:red;">
            <a href="http://billgmh.javaeye.com/blog/208503#comments" style="color:red;">已有 <strong>0</strong> 人发表留言，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 27 Jun 2008 10:02:23 +0800</pubDate>
        <link>http://billgmh.javaeye.com/blog/208503</link>
        <guid>http://billgmh.javaeye.com/blog/208503</guid>
      </item>
          <item>
        <title>初试Hibernate Search</title>
        <author>billgmh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://billgmh.javaeye.com">billgmh</a>&nbsp;
                    链接：<a href="http://billgmh.javaeye.com/blog/128957" style="color:red;">http://billgmh.javaeye.com/blog/128957</a>&nbsp;
          发表时间: 2007年10月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>不久前Hibernate推出了Hibernate Search 3.0 GA，由它的名字大家也可以大概猜到它的作用是对数据库中的数据进行检索的。它是hibernate对著名的全文检索系统Lucene的一个集成方案，作用在于对数据表中某些内容庞大的字段（如声明为text的字段）建立全文索引，这样通过hibernate search就可以对这些字段进行全文检索后获得相应的POJO，从而加快了对内容庞大字段进行模糊搜索的速度（sql语句中like匹配）。</p>
<p>Hibernate Search运行的环境如下：</p>
<p>1、JDK或JRE 5.0以上</p>
<p>2、Hibernate-Search以及相应的依赖包</p>
<p>3、Hibernate Core 3.2.X</p>
<p>4、Hibernate Annotations 3.3.X</p>
<p>一、配置</p>
<p>使用过Lucene的人都知道，Lucene是使用Directory这个概念来存储索引文件的，所以在Hibernate Search中提供了一个初始化、配置化的工厂类DirectoryProvider来生成相应的Directory。而在这里，我使用了FSDirectoryProvider这个工厂类，其中FS代表文件系统，意思是索引文件保存在文件系统中。因此，我们在<font face="Arial">hibernate.cfg.xml文件中加入了一下内容：</font></p>
<div class="code_title">xml 代码</div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-xml">
    <li class="alt"><span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;hibernate.search.default.directory_provider&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org.hibernate.search.store.FSDirectoryProvider &nbsp;&nbsp;</span></li>
    <li class="alt"><span></span><span class="tag">&lt;/</span><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span></span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;hibernate.search.default.indexBase&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;E:/temp/index &nbsp;&nbsp;</span></li>
    <li class=""><span></span><span class="tag">&lt;/</span><span class="tag-name">property</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
</ol>
</div>
<p>其中属性<font face="Arial">hibernate.search.default.indexBase代表索引文件默认的保存位置。</font></p>
<p>这些属性设置完成后，接下来就是使用Annotation对指定POJO的指定属性进行配置了。如下：</p>
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-j">
    <li class="alt"><span><span class="annotation">@Indexed</span><span>(index&nbsp;=&nbsp;</span><span class="string">&quot;text&quot;</span><span>) &nbsp;&nbsp;</span></span></li>
    <li class=""><span></span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;Text&nbsp;</span><span class="keyword">implements</span><span>&nbsp;java.io.Serializable &nbsp;&nbsp;</span></li>
    <li class="alt"><span>{ &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="annotation">@DocumentId</span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;Integer&nbsp;id; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;String&nbsp;fileName; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;String&nbsp;filePath; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="annotation">@Field</span><span>(name&nbsp;=&nbsp;</span><span class="string">&quot;content&quot;</span><span>,&nbsp;store&nbsp;=&nbsp;Store.NO,&nbsp;index&nbsp;=&nbsp;Index.TOKENIZED,&nbsp;analyzer&nbsp;=&nbsp;</span><span class="annotation">@Analyzer</span><span>(impl&nbsp;=&nbsp;ChineseAnalyzer.</span><span class="keyword">class</span><span>)) &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">private</span><span>&nbsp;String&nbsp;content; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;...... &nbsp;&nbsp;</span></li>
    <li class="alt"><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<p>其中@Indexed用于标示需要建立全文索引的实体类，它包含一个属性index用于标示这个全文索引的名字</p>
<p>@DocumentId用于标示实体类中的唯一的属性保存在索引文件中，是当进行全文检索时可以这个唯一的属性来区分索引中其他实体对象，一般使用实体类中的主键属性</p>
<p>@Field就是用来标示Lucene的Field字段，其中name属性用于标示Field的名称，store属性用于标示这个属性的内容是否需要保存在索引中，index属性标示该字段属性是否进行分词（<font face="Arial">Index.TOKENIZED</font>），analyzer用于标示建立索引时所使用的分析器是什么类，这里使用Lucene自带的ChineseAnalyzer</p>
          <br/><br/>
          <span style="color:red;">
            <a href="http://billgmh.javaeye.com/blog/128957#comments" style="color:red;">已有 <strong>20</strong> 人发表留言，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">Windows7在微软WinHEC 2008上揭开神秘面纱</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 03 Oct 2007 16:16:10 +0800</pubDate>
        <link>http://billgmh.javaeye.com/blog/128957</link>
        <guid>http://billgmh.javaeye.com/blog/128957</guid>
      </item>
          <item>
        <title>带局部统计识别高频词汇及人名识别的Lucene中文分词</title>
        <author>billgmh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://billgmh.javaeye.com">billgmh</a>&nbsp;
                    链接：<a href="http://billgmh.javaeye.com/blog/82519" style="color:red;">http://billgmh.javaeye.com/blog/82519</a>&nbsp;
          发表时间: 2007年05月24日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>经过努力终于在 <a href="http://www.javaeye.com/post/253773">引入局部统计识别高频词汇的Lucene中文分词程序</a> 以及 <a href="http://www.javaeye.com/topic/59121">基于词典的最大匹配的Lucene中文分词程序</a> 把中文人名识别功能加入，能把大部分的中文姓名识别出来，结果喜人。</p>
<p>原理：</p>
<p>根据贝叶斯定理，分别建立中文形式库FirstName.stu，从网上搜集得到的真实姓名库Name.stu，以及以姓氏为开头但非中文姓名的词库（如&ldquo;曾经是&rdquo;，&ldquo;关门&rdquo;，&ldquo;陈述&rdquo;等）UnName.stu。然后先使用之前完成的基于词典的最大匹配分词算法对文本进行初步分词，然后利用Spring的AOP把人名识别器SimpleChineseNameAnalyzer编织到初步分词方法执行之后，利用姓氏驱动的方式遍历分词碎片，找到所有以姓氏开头后两个分词结果为单字或后一个分词结果为双字词的碎片（成为候选词），分别从Name.stu以及UnName.stu中获取姓氏以及各个名字成为姓名的概率，最后判断候选词前后是否存在边界词汇，若有则对其进行概率调整，最终通过与阀值的比较判断是否是一个中文姓名。</p>
<p>用法：</p>
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-j">
    <li class="alt"><span><span>ApplicationContext&nbsp;context&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;FileSystemXmlApplicationContext(</span><span class="string">&quot;STUSegmentConfig.xml&quot;</span><span>); &nbsp;&nbsp;</span></span> </li>
    <li class=""><span>STUChineseAnalyzer&nbsp;analyzer&nbsp;=&nbsp;(STUChineseAnalyzer)&nbsp;context&nbsp;.getBean(</span><span class="string">&quot;stuChineseAnalyzer&quot;</span><span>);&nbsp;&nbsp;</span> </li>
</ol>
</div>
<p>测试数据：</p>
<p><font face="Arial">东方网记者靳慧、张海盈、实习生霍世杰5月20日报道：今天上午10点，同济大学建校100周年庆典大会在同济大学大礼堂隆重举行。中共中央总书记、国家主席胡锦涛发来贺信。全国人大常委会副委员长李铁映，全国人大常委会副委员长顾秀莲，国务委员陈至立，中共上海市委书记习近平，德国前总理施罗德，中央纪委副书记张惠新，教育部部长周济，建设部部长汪光焘，中共上海市委副书记、市长韩正，科技部党组书记、副部长李学勇等出席庆典大会。</font></p>
<p><font face="Arial">　　庆典大会上首先宣读胡锦涛总书记贺信，之后科技部部长、同济大学校长万钢从&ldquo;大学对社会的承诺&rdquo;的角度讲述百年同济的辉煌历史和未来发展。来自德国柏林工业大学的校长库茨勒、中国科学院院士吴孟超、同济大学交通运输工程学院孙立军等也将作为友好学校代表、校友代表、师生代表先后致辞。</font></p>
<p><font face="Arial">　　今天早晨8点开始，同济大学的师生、老校友们就陆续进入庆典大会会场，会场大屏幕上滚动播放着建校100周年的宣传片。庆典大会开始前举行了同济老校友互动活动，现场十分感人。</font></p>
<p><font face="Arial">　　同济大学是教育部直属重点大学，创建于1907年，早期为德国医生在上海创办的德文医学堂，取名&quot;同济&quot;意蕴合作共济。1912年增设工学堂，1923年被批准改名为大学，1927年正式定为国立同济大学。抗战期间曾内迁经浙、赣、滇入川，1946年回迁上海并发展为以拥有理、工、医、文、法五大学院著称海内外的综合性大学。1952年院系调整后，同济大学成为国内土木建筑领域最大、专业最全的工科大学。</font></p>
<p><font face="Arial">　　1978年经中央批准恢复对德交流，在中科院学部委员李国豪校长领导下实行&ldquo;两个转变&rdquo;，即由土木为主的理工科大学向理工为主的多科性大学转变，由国内普通高校向作为中外文化交流&ldquo;窗口&rdquo;之一的国际性大学转变，从而迅速恢复和发展成为一所以工为主、理工结合，经、管、文、法各具特色的多科性大学。 </font></p>
<p>测试结果：</p>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-j">
    <li class="alt"><span><span>(东方,</span><span class="number">0</span><span>,</span><span class="number">2</span><span>) &nbsp;&nbsp;</span></span></li>
    <li class=""><span>(网,</span><span class="number">2</span><span>,</span><span class="number">3</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(记者,</span><span class="number">3</span><span>,</span><span class="number">5</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(靳慧,</span><span class="number">5</span><span>,</span><span class="number">7</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(张海盈,</span><span class="number">8</span><span>,</span><span class="number">11</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(实习生,</span><span class="number">12</span><span>,</span><span class="number">15</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(霍世杰,</span><span class="number">15</span><span>,</span><span class="number">18</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(</span><span class="number">5</span><span>,</span><span class="number">18</span><span>,</span><span class="number">19</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(月,</span><span class="number">19</span><span>,</span><span class="number">20</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(</span><span class="number">20</span><span>,</span><span class="number">20</span><span>,</span><span class="number">22</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(日报,</span><span class="number">22</span><span>,</span><span class="number">24</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(道,</span><span class="number">24</span><span>,</span><span class="number">25</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(今天,</span><span class="number">26</span><span>,</span><span class="number">28</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(上午,</span><span class="number">28</span><span>,</span><span class="number">30</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(</span><span class="number">10</span><span>,</span><span class="number">30</span><span>,</span><span class="number">32</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(点,</span><span class="number">32</span><span>,</span><span class="number">33</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(同济大学,</span><span class="number">34</span><span>,</span><span class="number">38</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(建校,</span><span class="number">38</span><span>,</span><span class="number">40</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(</span><span class="number">100</span><span>,</span><span class="number">40</span><span>,</span><span class="number">43</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(周年,</span><span class="number">43</span><span>,</span><span class="number">45</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(庆典,</span><span class="number">45</span><span>,</span><span class="number">47</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(大会,</span><span class="number">47</span><span>,</span><span class="number">49</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(在,</span><span class="number">49</span><span>,</span><span class="number">50</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(同济大学,</span><span class="number">50</span><span>,</span><span class="number">54</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(大礼堂,</span><span class="number">54</span><span>,</span><span class="number">57</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(隆重举行,</span><span class="number">57</span><span>,</span><span class="number">61</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(中共中央总书记,</span><span class="number">62</span><span>,</span><span class="number">69</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(国家主席,</span><span class="number">70</span><span>,</span><span class="number">74</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(胡锦涛,</span><span class="number">74</span><span>,</span><span class="number">77</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(发来,</span><span class="number">77</span><span>,</span><span class="number">79</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(贺信,</span><span class="number">79</span><span>,</span><span class="number">81</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(全国人大常委会,</span><span class="number">82</span><span>,</span><span class="number">89</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(副委员长,</span><span class="number">89</span><span>,</span><span class="number">93</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(李铁映,</span><span class="number">93</span><span>,</span><span class="number">96</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(全国人大常委会,</span><span class="number">97</span><span>,</span><span class="number">104</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(副委员长,</span><span class="number">104</span><span>,</span><span class="number">108</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(顾秀莲,</span><span class="number">108</span><span>,</span><span class="number">111</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(国务委员,</span><span class="number">112</span><span>,</span><span class="number">116</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(陈至立,</span><span class="number">116</span><span>,</span><span class="number">119</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(中共,</span><span class="number">120</span><span>,</span><span class="number">122</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(上海市,</span><span class="number">122</span><span>,</span><span class="number">125</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(委,</span><span class="number">125</span><span>,</span><span class="number">126</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(书记,</span><span class="number">126</span><span>,</span><span class="number">128</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(习近平,</span><span class="number">128</span><span>,</span><span class="number">131</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(德国,</span><span class="number">132</span><span>,</span><span class="number">134</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(前总理,</span><span class="number">134</span><span>,</span><span class="number">137</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(施,</span><span class="number">137</span><span>,</span><span class="number">138</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(罗德,</span><span class="number">138</span><span>,</span><span class="number">140</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(中央纪委,</span><span class="number">141</span><span>,</span><span class="number">145</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(副书记,</span><span class="number">145</span><span>,</span><span class="number">148</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(张惠新,</span><span class="number">148</span><span>,</span><span class="number">151</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(教育部,</span><span class="number">152</span><span>,</span><span class="number">155</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(部长,</span><span class="number">155</span><span>,</span><span class="number">157</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(周济,</span><span class="number">157</span><span>,</span><span class="number">159</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(建设部,</span><span class="number">160</span><span>,</span><span class="number">163</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(部长,</span><span class="number">163</span><span>,</span><span class="number">165</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(汪光焘,</span><span class="number">165</span><span>,</span><span class="number">168</span><span>) &nbsp;&nbsp;</span></li>
    <li class=""><span>(中共,</span><span class="number">169</span><span>,</span><span class="number">171</span><span>) &nbsp;&nbsp;</span></li>
    <li class="alt"><span>(上海市,</