<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>Jamsa</title>
    <description></description>
    <link>http://jamsa.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>JBPM 与 Spring 结合</title>
        <author>Jamsa</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jamsa.javaeye.com">Jamsa</a>&nbsp;
          链接：<a href="http://jamsa.javaeye.com/blog/192996" style="color:red;">http://jamsa.javaeye.com/blog/192996</a>&nbsp;
          发表时间: 2008年05月14日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>今天尝试了将jbpm和spring进行结合，主要参考http://betafox.javaeye.com/blog/177649来进行。</p>
<p>版本：</p>
<p>jbpm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.1.4</p>
<p>struts2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.0.11</p>
<p>spring&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.5.1</p>
<p>hibernate&nbsp;&nbsp; 3.2.5.ga</p>
<p>配置文件如下：</p>
<pre name="code" class="xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
	xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
	xmlns:util=&quot;http://www.springframework.org/schema/util&quot;
	xmlns:jee=&quot;http://www.springframework.org/schema/jee&quot;
	xmlns:tx=&quot;http://www.springframework.org/schema/tx&quot;
	xmlns:aop=&quot;http://www.springframework.org/schema/aop&quot;
	xmlns:lang=&quot;http://www.springframework.org/schema/lang&quot;
	xsi:schemaLocation=&quot;
<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a> http://www.springframework.org/schema/beans/spring-beans.xsd
<a href="http://www.springframework.org/schema/util" target="_blank">http://www.springframework.org/schema/util</a> http://www.springframework.org/schema/util/spring-util.xsd
<a href="http://www.springframework.org/schema/jee" target="_blank">http://www.springframework.org/schema/jee</a> http://www.springframework.org/schema/jee/spring-jee.xsd
<a href="http://www.springframework.org/schema/tx" target="_blank">http://www.springframework.org/schema/tx</a> http://www.springframework.org/schema/tx/spring-tx.xsd
<a href="http://www.springframework.org/schema/aop" target="_blank">http://www.springframework.org/schema/aop</a> http://www.springframework.org/schema/aop/spring-aop.xsd
<a href="http://www.springframework.org/schema/lang" target="_blank">http://www.springframework.org/schema/lang</a> http://www.springframework.org/schema/lang/spring-lang.xsd&quot;
	default-autowire=&quot;byName&quot;&gt;

	&lt;bean id=&quot;workflow1&quot;
		class=&quot;org.springmodules.workflow.jbpm31.definition.ProcessDefinitionFactoryBean&quot;&gt;
		&lt;property name=&quot;definitionLocation&quot;
			value=&quot;classpath:jbpm/processes/processdefinition.xml&quot; /&gt;
	&lt;/bean&gt;

	&lt;!-- jBPM configuration--&gt;
	&lt;bean id=&quot;jbpmConfiguration&quot;
		class=&quot;org.springmodules.workflow.jbpm31.LocalJbpmConfigurationFactoryBean&quot;&gt;
		&lt;property name=&quot;sessionFactory&quot; ref=&quot;sessionFactory&quot; /&gt;
		&lt;property name=&quot;configuration&quot; value=&quot;classpath:jbpm.cfg.xml&quot; /&gt;

		&lt;property name=&quot;processDefinitions&quot;&gt;
			&lt;list&gt;
				&lt;ref local=&quot;workflow1&quot; /&gt;
			&lt;/list&gt;
		&lt;/property&gt;
		&lt;!-- 
			&lt;property name=&quot;createSchema&quot; value=&quot;true&quot; /&gt;
			&lt;property name=&quot;dropSchema&quot; value=&quot;true&quot; /&gt;
		--&gt;
	&lt;/bean&gt;

	&lt;!-- jBPM template --&gt;
	&lt;bean id=&quot;jbpmTemplate&quot;
		class=&quot;org.springmodules.workflow.jbpm31.JbpmTemplate&quot;&gt;
		&lt;constructor-arg index=&quot;0&quot; ref=&quot;jbpmConfiguration&quot; /&gt;
		&lt;constructor-arg index=&quot;1&quot; ref=&quot;workflow1&quot; /&gt;
	&lt;/bean&gt;

&lt;/beans&gt;</pre>
<p>&nbsp;在casspath下添加了jbpm.cfg.xml，内容是从default.jbpm.cfg.xml中复制过来的：</p>
<pre name="code" class="xml">&lt;jbpm-configuration&gt;

	&lt;jbpm-context name=&quot;default.jbpm.context&quot;&gt;

		&lt;service name=&quot;persistence&quot;
			factory=&quot;org.jbpm.persistence.db.DbPersistenceServiceFactory&quot; /&gt;
		&lt;service name=&quot;message&quot;
			factory=&quot;org.jbpm.msg.db.DbMessageServiceFactory&quot; /&gt;
		&lt;service name=&quot;scheduler&quot;
			factory=&quot;org.jbpm.scheduler.db.DbSchedulerServiceFactory&quot; /&gt;
		&lt;service name=&quot;logging&quot;
			factory=&quot;org.jbpm.logging.db.DbLoggingServiceFactory&quot; /&gt;
		&lt;service name=&quot;authentication&quot;
			factory=&quot;org.jbpm.security.authentication.DefaultAuthenticationServiceFactory&quot; /&gt;
	&lt;/jbpm-context&gt;


	&lt;!-- 不再使用&lt;string name=&quot;resource.hibernate.cfg.xml&quot; value=&quot;hibernate.cfg.xml&quot; /&gt;--&gt;
	&lt;string name=&quot;resource.business.calendar&quot;
		value=&quot;org/jbpm/calendar/jbpm.business.calendar.properties&quot; /&gt;
	&lt;string name=&quot;resource.default.modules&quot;
		value=&quot;org/jbpm/graph/def/jbpm.default.modules.properties&quot; /&gt;
	&lt;string name=&quot;resource.converter&quot;
		value=&quot;org/jbpm/db/hibernate/jbpm.converter.properties&quot; /&gt;
	&lt;string name=&quot;resource.action.types&quot;
		value=&quot;org/jbpm/graph/action/action.types.xml&quot; /&gt;
	&lt;string name=&quot;resource.node.types&quot;
		value=&quot;org/jbpm/graph/node/node.types.xml&quot; /&gt;
	&lt;string name=&quot;resource.parsers&quot;
		value=&quot;org/jbpm/jpdl/par/jbpm.parsers.xml&quot; /&gt;
	&lt;string name=&quot;resource.varmapping&quot;
		value=&quot;org/jbpm/context/exe/jbpm.varmapping.xml&quot; /&gt;

&lt;/jbpm-configuration&gt;</pre>
&nbsp;
<p>在配置过程中也遇到了一些问题。</p>
<p>问题一：系统报找不到 default.jbpm.context。</p>
<p>解决方法：在自己的jbpm.cfg.xml中设置</p>
<pre name="code" class="xml">jbpm-context name=&quot;default.jbpm.context&quot;</pre>
<p>问题二：系统报找不到hibernate.cfg.xml。</p>
<p>解决方法：我的系统里hibernate配置放在spring的配置文件里，没有使用hibernate.cfg.xml。跟踪DbPersistenceServiceFactory和LocalJbpmConfigurationFactoryBean.afterPropertiesSet发现问题出在jbpmContext构建时需要获取持久层，如果sessionFactory存在则不会去读取hibernate.cfg.xml，除了在配置jbpmConfiguration时要设置sessionFactory外，如果jbpm的数据库表已经存在，则不要设置createSchema和dropSchema否则会报错。按上面的配置文件设置后，不会再去读取hibernate.cfg.xml，可以注释掉jbpm.cfg.xml中的resource.hibernate.cfg.xml。</p>
<p><span style="color: #0000ff;">注意：如果使用这种配置方式，省略了hibernate.cfg.xml文件，但如果调用jbpmConfiguration.createSchema();jbpmConfiguration.dropSchema();等方法还是会出错，跟踪源码可以发现这些方法都会调用DbPersistenceServiceFactory.getConfiguration()方法，这个方法会去读取hibernate.cfg.xml。我们可以使用jbpm-db\build\mysql\scripts\下的脚本手工创建或删除数据库，这在做单元测试时稍显麻烦。</span>
</p>
<p>问题三：jbpm-identity中有User.hbm.xml，这个名称与我系统中原有的User.hbml.xml冲突，尽管他们不在同一个包中，但还是产生了冲突。只能调整一下自己的代码了。</p>
<p>spring配置文件中配置流程定义的xml有些多余，反而使系统在启动的时候更新数据库里的流程定义。完全可以将流程部署到数据库。JbpmTemplate有无参数的构造器，可以不使用构造器注入，只需要在spring配置文件中注它的jbpmConfiguration属性就可以了。</p>
<p>&nbsp;</p>
<p>另，没有添加参考文章里说的过滤器，暂时没发现问题。</p>
          <br/>
          <span style="color:red;">
            <a href="http://jamsa.javaeye.com/blog/192996#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><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;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 14 May 2008 16:56:13 +0800</pubDate>
        <link>http://jamsa.javaeye.com/blog/192996</link>
        <guid>http://jamsa.javaeye.com/blog/192996</guid>
      </item>
      <item>
        <title>ExtJS中FormPanel实现数据加载和提交</title>
        <author>Jamsa</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jamsa.javaeye.com">Jamsa</a>&nbsp;
          链接：<a href="http://jamsa.javaeye.com/blog/173897" style="color:red;">http://jamsa.javaeye.com/blog/173897</a>&nbsp;
          发表时间: 2008年03月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p class="first">在使用使用FormPanel时我们通常需要使用它的form对象来加载数据或提交数据。FormPanel中的
form对象为Ext.form.BasicForm类型的对象，它有load和submit方法分别用于加载数据和提交数据。而这两个方法都是通过调用
Ext.form.BasicForm中的doAction方法来操作的。doAction方法带有两个参数，其中第二个参数为从load或submit
方法传递过来的Ext.form.Action对象的配置数据（Config
Options）。其中的success和failure属性是用于处理请求成功或失败的函数。但需要注意的是，文档中的说明是这个success或
failure取决于Http请求过程是否出错。实际情况却并非这样，我在开发过程中发现HTTP响应代码一直都是200，但被调用的函数却一直都是
failure属性对应的函数。通过查看Action.js可以发现响应过来的数据是需要符合一定格式的，Ext.form.Action.Load的
API文档开头就说明了响应数据包必须类似下面的格式：</p>

<pre class="src">{
    success: <span style="color: #5f9ea0">true</span>,
    data: {
        clientName: <span style="color: #bc8f8f">&quot;Fred. Olsen Lines&quot;</span>,
        portOfLoading: <span style="color: #bc8f8f">&quot;FXT&quot;</span>,
        portOfDischarge: <span style="color: #bc8f8f">&quot;OSL&quot;</span>
    }
}
</pre>
这个说明在我使用ExtJS的时候再次误导了我。我认为只需要响应的数据为类似的格式就可以了。结果仍然出错，查看Action.js中的
handleResponse方法可以看出，返回的数据为上面代码的格式，但并不是说从服务端发送过来的数据就是这样的格式，而是需要将
Ext.form.Action.Load的result属性设置成上面的格式的数据。从handleResponse也可以看出，Action.js使
用了form.reader属性来处理服务端数据。这个属性也可以在初始化FormPanel的时候传递给FormPanel，FormPanel将会把
这个属性传递给它内置的BasicForm对象。怎样使用JsonReader来使提取响应数据来使它满足PanelForm的要求呢？再看
handleResponse中的代码，在reader存在的情况下，它返回的是所需要的格式的数据，一个包含success属性和data属性的对象，
而data属性来自于JsonReader的read方法处理后的结果。再查看JsonReader.js中的read方法，它调用的是
readRecords来读取数据，而返回的值是由reader的root属性决定的，从JsonReader.js中还可以看出，root属性对应的
JSON对象必须是集合类型的，因此我们在后台发送过来的数据必须也是集合类型，我在这里也出了错。一直认为加载数据到Form里，一次只加载一条，所以
从服务端传递过来的数据都是单个的对象，而将JsonReader对象的root设置为单个对象的名称，结果Form中一直都加载不上数据。后来将服务端
传递过来的数据修改为集合数型问题解决了。
<p>小结：</p>

<ol><li>ExtJS中JsonReader对于数据的处理总是一致的，不管你需要的是单条记录还是多条记录，它总是通过total属性判断记录数，通过root属性对应的名称来取记录集合。</li><li>FormPanel中处理数据的为内置的BasicForm类型的对象，它通过load和submit方法来加载或提交数据。而这两个方法是通过
Ext.form.Action的两个子类Ext.form.Action.Load、Ext.form.Action.Submit来提交请求和调用用
户的success和failure方法。决定调用success和failure的并非Http请求是否出错，而是决定于Action.js中
handleResponse的处理结果。我们可以通过设置FormPanel的reader配置对象来干预handleResponse对数据的处理。
而这个reader也可设置响应数据与FormPanel中字段的对应关系。</li><li>多看源码，可以获取更多。</li></ol>

<p>附：</p>

<p>表格中双击进行编辑的JS源码，这个代码比官方文档中的处理方式简单一些，觉得官方文档中的edit中加载数据的处理是一种hack的方式，并不太适合实际应用。</p>

<p><a href="http://extjs.com/learn/Tutorial:Using_Ext_grid_form_dialog_to_achieve_paging_list,_create,_edit,_delete_function">ExtJS网站上的CRUD的文章，其中包含有加载数据进行编辑的例子</a></p>

<pre class="src">Ext.onReady(<span style="color: #a020f0">function</span>(){
<span style="color: #a020f0">var</span> <span style="color: #b8860b">newFormWin</span>;
<span style="color: #a020f0">var</span> <span style="color: #b8860b">form1</span>;

    <span style="color: #b22222">//表格处理
</span>    <span style="color: #b22222">//Ext.BLANK_IMAGE_URL = 'extjs/resources/images/default/s.gif';
</span>    Ext.QuickTips.init();
    <span style="color: #a020f0">var</span> <span style="color: #b8860b">sm</span> = <span style="color: #a020f0">new</span> Ext.grid.CheckboxSelectionModel(); <span style="color: #b22222">//CheckBox选择列
</span>
    <span style="color: #a020f0">var</span> <span style="color: #b8860b">cm</span> = <span style="color: #a020f0">new</span> Ext.grid.ColumnModel([
        <span style="color: #a020f0">new</span> Ext.grid.RowNumberer(), <span style="color: #b22222">//行号列
</span>        sm,
        {header:<span style="color: #bc8f8f">'编号'</span>,dataIndex:<span style="color: #bc8f8f">'id'</span>},
        {header:<span style="color: #bc8f8f">'性别'</span>,dataIndex:<span style="color: #bc8f8f">'sex'</span>,renderer:<span style="color: #a020f0">function</span>(<span style="color: #b8860b">value</span>){
            <span style="color: #a020f0">if</span>(value==<span style="color: #bc8f8f">'1'</span>){
                <span style="color: #a020f0">return</span> <span style="color: #bc8f8f">&quot;男&quot;</span>;
            }<span style="color: #a020f0">else</span>{
                <span style="color: #a020f0">return</span> <span style="color: #bc8f8f">&quot;女&quot;</span>;
            }
        }}, <span style="color: #b22222">//增加性别，自定义renderer，即显示的样式，可以加html代码，来显示图片等。
</span>        {header:<span style="color: #bc8f8f">'名称'</span>,dataIndex:<span style="color: #bc8f8f">'name'</span>},
        {header:<span style="color: #bc8f8f">'描述'</span>,dataIndex:<span style="color: #bc8f8f">'memo'</span>}
    ]);

    <span style="color: #a020f0">var</span> <span style="color: #b8860b">ds</span> = <span style="color: #a020f0">new</span> Ext.data.Store({
        proxy: <span style="color: #a020f0">new</span> Ext.data.HttpProxy({url:ACTION_URL}),<span style="color: #b22222">//调用的动作
</span>        reader: <span style="color: #a020f0">new</span> Ext.data.JsonReader({
            totalProperty: <span style="color: #bc8f8f">'total'</span>,
            root: <span style="color: #bc8f8f">'list'</span>,
            successProperty :<span style="color: #bc8f8f">'success'</span>
        },
                                        [{name: <span style="color: #bc8f8f">'id'</span>,mapping:<span style="color: #bc8f8f">'id'</span>,type:<span style="color: #bc8f8f">'string'</span>},
                                         {name: <span style="color: #bc8f8f">'sex'</span>,mapping:<span style="color: #bc8f8f">'sex'</span>,type:<span style="color: #bc8f8f">'string'</span>},
                                         {name: <span style="color: #bc8f8f">'name'</span>,mapping:<span style="color: #bc8f8f">'name'</span>,type:<span style="color: #bc8f8f">'string'</span>},
                                         {name: <span style="color: #bc8f8f">'memo'</span>,mapping:<span style="color: #bc8f8f">'memo'</span>,type:<span style="color: #bc8f8f">'string'</span>} <span style="color: #b22222">//列的映射
</span>                                        ])
    });


    <span style="color: #a020f0">var</span> <span style="color: #b8860b">grid</span> = <span style="color: #a020f0">new</span> Ext.grid.GridPanel({
        id: <span style="color: #bc8f8f">'grid'</span>,
        el: <span style="color: #bc8f8f">'center'</span>,
        region:<span style="color: #bc8f8f">'center'</span>,
        title:<span style="color: #bc8f8f">'用户'</span>,
        ds: ds,
        sm: sm,
        cm: cm,
        bbar: <span style="color: #a020f0">new</span> Ext.PagingToolbar({
            pageSize: 10,
            store: ds,
            displayInfo: <span style="color: #5f9ea0">true</span>,
            displayMsg: <span style="color: #bc8f8f">'显示第 {0} 条到 {1} 条记录，一共 {2} 条'</span>,
            emptyMsg: <span style="color: #bc8f8f">&quot;没有记录&quot;</span>
        }) <span style="color: #b22222">//页脚显示分页
</span>    });


    <span style="color: #b22222">//布局处理
</span>    Ext.state.Manager.setProvider(<span style="color: #a020f0">new</span> Ext.state.CookieProvider());
    <span style="color: #a020f0">new</span> Ext.Viewport({
        layout:<span style="color: #bc8f8f">'border'</span>,
        items:[
            {
                xtype:<span style="color: #bc8f8f">'box'</span>,
                region:<span style="color: #bc8f8f">'north'</span>,
                el: <span style="color: #bc8f8f">'north'</span>,
                height:32,
                title:<span style="color: #bc8f8f">'north'</span>
            },{
                region:<span style="color: #bc8f8f">'south'</span>,
                contentEl: <span style="color: #bc8f8f">'south'</span>,
                split:<span style="color: #5f9ea0">true</span>,
                height: 100,
                minSize: 100,
                maxSize: 200,
                collapsible: <span style="color: #5f9ea0">true</span>,
                title:<span style="color: #bc8f8f">'South'</span>,
                margins:<span style="color: #bc8f8f">'0 0 0 0'</span>
            },
            grid
        ]
    });

    <span style="color: #b22222">//el:指定html元素用于显示grid
</span>    grid.render();<span style="color: #b22222">//Ext.getCmp('grid').render();//渲染表格
</span>    ds.load({params:{start:0, limit:10}}); <span style="color: #b22222">//加载数据
</span>
    grid.on(<span style="color: #bc8f8f">&quot;rowdblclick&quot;</span>, <span style="color: #a020f0">function</span>(<span style="color: #b8860b">grid</span>) {
        loadFormData(grid);
        <span style="color: #b22222">//alert(form1.reader);
</span>    });

    <span style="color: #b22222">// 载入被选择的数据行的表单数据
</span>    <span style="color: #a020f0">var</span> <span style="color: #b8860b">loadFormData</span> = <span style="color: #a020f0">function</span>(<span style="color: #b8860b">grid</span>) {
        <span style="color: #a020f0">var</span> <span style="color: #b8860b">_record</span> = grid.getSelectionModel().getSelected();
        <span style="color: #a020f0">if</span> (!_record) {
            Ext.example.msg(<span style="color: #bc8f8f">'修改操作'</span>, <span style="color: #bc8f8f">'请选择要修改的一项！'</span>);
        } <span style="color: #a020f0">else</span> {
            myFormWin();
            form1.form.load( {
                url : EDIT_ACTION_URL+<span style="color: #bc8f8f">'?sid='</span>+ _record.get(<span style="color: #bc8f8f">'id'</span>),
                waitMsg : <span style="color: #bc8f8f">'正在载入数据...'</span>,
                <span style="color: #0000ff">success</span> : <span style="color: #a020f0">function</span>(<span style="color: #b8860b">form</span>,<span style="color: #b8860b">action</span>) {
                    Ext.example.msg(<span style="color: #bc8f8f">'编辑'</span>, <span style="color: #bc8f8f">'载入成功！'</span>);
                },
                <span style="color: #0000ff">failure</span> : <span style="color: #a020f0">function</span>(<span style="color: #b8860b">form</span>,<span style="color: #b8860b">action</span>) {
                    Ext.example.msg(<span style="color: #bc8f8f">'编辑'</span>, <span style="color: #bc8f8f">'载入失败'</span>);
                }
            });
        }
    };




    <span style="color: #a020f0">var</span> <span style="color: #b8860b">myFormWin</span> = <span style="color: #a020f0">function</span>() {
        <span style="color: #b22222">// create the window on the first click and reuse on subsequent
</span>        <span style="color: #b22222">// clicks
</span>
        <span style="color: #a020f0">if</span> (!newFormWin) {
            newFormWin = <span style="color: #a020f0">new</span> Ext.Window( {
                el : <span style="color: #bc8f8f">'topic-win'</span>,
                layout : <span style="color: #bc8f8f">'fit'</span>,
                width : 400,
                height : 300,
                closeAction : <span style="color: #bc8f8f">'hide'</span>,
                plain : <span style="color: #5f9ea0">true</span>,
                title : <span style="color: #bc8f8f">'窗口'</span>,
                items : form1
            });
        }
        newFormWin.show(<span style="color: #bc8f8f">'New1'</span>);
    };



    form1 = <span style="color: #a020f0">new</span> Ext.FormPanel( {
        <span style="color: #b22222">// collapsible : true,// 是否可以展开
</span>        labelWidth : 75, <span style="color: #b22222">// label settings here cascade unless overridden
</span>        url : <span style="color: #bc8f8f">'AddLevel.action'</span>,
        frame : <span style="color: #5f9ea0">true</span>,
        title : <span style="color: #bc8f8f">'修改'</span>,
        bodyStyle : <span style="color: #bc8f8f">'padding:5px 5px 0'</span>,
        width : 350,
        waitMsgTarget : <span style="color: #5f9ea0">true</span>,
        <span style="color: #b22222">//这个属性决定了load和submit中对数据的处理，list必须是一个集合类型，json格式应该是[]包含的一个数组
</span>        reader: <span style="color: #a020f0">new</span> Ext.data.JsonReader({root:<span style="color: #bc8f8f">'list'</span>},
                                        [{name: <span style="color: #bc8f8f">'id'</span>,mapping:<span style="color: #bc8f8f">'id'</span>,type:<span style="color: #bc8f8f">'string'</span>},
                                         {name: <span style="color: #bc8f8f">'sex'</span>,mapping:<span style="color: #bc8f8f">'sex'</span>,type:<span style="color: #bc8f8f">'string'</span>},
                                         {name: <span style="color: #bc8f8f">'memo'</span>,mapping:<span style="color: #bc8f8f">'memo'</span>,type:<span style="color: #bc8f8f">'string'</span>}
                                        ]),
        defaults : {
            width : 230
        },
        defaultType : <span style="color: #bc8f8f">'textfield'</span>,
        items : [ {
            fieldLabel : <span style="color: #bc8f8f">'编号'</span>,
            name : <span style="color: #bc8f8f">'id'</span>,
            allowBlank : <span style="color: #5f9ea0">false</span>
        }, {
            fieldLabel : <span style="color: #bc8f8f">'性别'</span>,
            name : <span style="color: #bc8f8f">'sex'</span>,
            allowBlank : <span style="color: #5f9ea0">false</span>
        }, <span style="color: #a020f0">new</span> Ext.form.TextArea( {
            fieldLabel : <span style="color: #bc8f8f">'备注'</span>,
            name : <span style="color: #bc8f8f">'memo'</span>,
            growMin : 234
        })],

        buttons : [ {
            text : <span style="color: #bc8f8f">'保存'</span>,
            disabled : <span style="color: #5f9ea0">false</span>,
            <span style="color: #0000ff">handler</span> : <span style="color: #a020f0">function</span>() {
                <span style="color: #a020f0">if</span> (form1.form.isValid()) {
                    form1.form.submit( {
                        url : <span style="color: #bc8f8f">'AddLevel.action'</span>,
                        <span style="color: #0000ff">success</span> : <span style="color: #a020f0">function</span>(<span style="color: #b8860b">from</span>, <span style="color: #b8860b">action</span>) {
                            Ext.example.msg(<span style="color: #bc8f8f">'保存成功'</span>, <span style="color: #bc8f8f">'添加级别成功！'</span>);
                            ds.load( {
                                params : {
                                    start : 0,
                                    limit : 30,
                                    forumId : 4
                                }
                            });
                        },
                        <span style="color: #0000ff">failure</span> : <span style="color: #a020f0">function</span>(<span style="color: #b8860b">form</span>, <span style="color: #b8860b">action</span>) {
                            Ext.example.msg(<span style="color: #bc8f8f">'保存失败'</span>, <span style="color: #bc8f8f">'添加级别失败！'</span>);
                        },
                        waitMsg : <span style="color: #bc8f8f">'正在保存数据，稍后...'</span>
                    });
                    dialog.hide();
                } <span style="color: #a020f0">else</span> {
                    Ext.Msg.alert(<span style="color: #bc8f8f">'信息'</span>, <span style="color: #bc8f8f">'请填写完成再提交!'</span>);
                }
            }
        }, {
            text : <span style="color: #bc8f8f">'取消'</span>,
            <span style="color: #0000ff">handler</span> : <span style="color: #a020f0">function</span>() {
                newFormWin.hide();
            }
        }]
    });



});

</pre>
          <br/>
          <span style="color:red;">
            <a href="http://jamsa.javaeye.com/blog/173897#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 19 Mar 2008 21:33:58 +0800</pubDate>
        <link>http://jamsa.javaeye.com/blog/173897</link>
        <guid>http://jamsa.javaeye.com/blog/173897</guid>
      </item>
      <item>
        <title>Emacs Lisp中的hash table</title>
        <author>Jamsa</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jamsa.javaeye.com">Jamsa</a>&nbsp;
          链接：<a href="http://jamsa.javaeye.com/blog/169893" style="color:red;">http://jamsa.javaeye.com/blog/169893</a>&nbsp;
          发表时间: 2008年03月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          (defun zj-hash-test ()<br />&nbsp; &quot;hash table 测试&quot;<br />&nbsp; (interactive)<br />&nbsp; (let (myhash val)<br />&nbsp;&nbsp;&nbsp; ;; 创建hash table并告诉elips用equal来测试key是否存在<br />&nbsp;&nbsp;&nbsp; (setq myhash (make-hash-table :test 'equal))<br /><br />&nbsp;&nbsp;&nbsp; ;; 添加数据<br />&nbsp;&nbsp;&nbsp; (puthash &quot;key1&quot; &quot;19&quot; myhash)<br />&nbsp;&nbsp;&nbsp; (puthash &quot;key2&quot; &quot;1ddd&quot; myhash)<br />&nbsp;&nbsp;&nbsp; (puthash &quot;key3&quot; &quot;1te&quot; myhash)<br />&nbsp;&nbsp;&nbsp; (puthash &quot;key4&quot; &quot;19&quot; myhash)<br /><br /><br />&nbsp;&nbsp;&nbsp; ;; 修改数据<br />&nbsp;&nbsp;&nbsp; (puthash &quot;key1&quot; &quot;2334dd&quot; myhash)<br /><br />&nbsp;&nbsp;&nbsp; ;; 删除数据<br />&nbsp;&nbsp;&nbsp; (remhash &quot;key3&quot; myhash)<br /><br />&nbsp;&nbsp;&nbsp; ;; 获取数据<br />&nbsp;&nbsp;&nbsp; (setq val (gethash &quot;key2&quot; myhash))<br />&nbsp;&nbsp;&nbsp; (message val)<br />&nbsp;&nbsp;&nbsp; )<br />&nbsp; )
          <br/>
          <span style="color:red;">
            <a href="http://jamsa.javaeye.com/blog/169893#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><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;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 10 Mar 2008 16:30:24 +0800</pubDate>
        <link>http://jamsa.javaeye.com/blog/169893</link>
        <guid>http://jamsa.javaeye.com/blog/169893</guid>
      </item>
      <item>
        <title>Emacs Lisp与Shell的交互</title>
        <author>Jamsa</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jamsa.javaeye.com">Jamsa</a>&nbsp;
          链接：<a href="http://jamsa.javaeye.com/blog/169891" style="color:red;">http://jamsa.javaeye.com/blog/169891</a>&nbsp;
          发表时间: 2008年03月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p> 一直以来对于w3m、tramp、dired等与shell关系密切的mode不是很了解，没有仔细读过代码。但总觉得应该是调用shell命令，再将shell命令的输出重定向到emacs中进行处理。今天在网上看到了相关的方法：</p><ol><li>调用shell命令<pre name="code" class="java">(defun zj-open-directory-with-explorer ()
  &quot;在windows中用explorer浏览当前目录&quot;
  (interactive)
  (shell-command &quot;explorer.exe .&quot;)
  (browse-url &quot;www.google.cn&quot;)
  )
</pre>&nbsp;</li><li>处理shell命令输出<pre name="code" class="java">(defun zj-display-directory-files ()
  &quot;执行shell命令并处理它的输出。这里为显示当前目录下的文件&quot;
  (interactive)
  (message (shell-command-to-string &quot;ls -l&quot;))
  )</pre>&nbsp;</li></ol><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://jamsa.javaeye.com/blog/169891#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></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;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 10 Mar 2008 16:27:41 +0800</pubDate>
        <link>http://jamsa.javaeye.com/blog/169891</link>
        <guid>http://jamsa.javaeye.com/blog/169891</guid>
      </item>
      <item>
        <title>重新整理后的Oracle OAF学习笔记——离线版本</title>
        <author>Jamsa</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jamsa.javaeye.com">Jamsa</a>&nbsp;
          链接：<a href="http://jamsa.javaeye.com/blog/167874" style="color:red;">http://jamsa.javaeye.com/blog/167874</a>&nbsp;
          发表时间: 2008年03月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          离线版本中包含了前段学习OAF开发的完整笔记。其中包含了OAF开发手册中的一些截图以便于理解。<br /><br />由于博客上传图片的限制，前面的连载中没有上传这些图片。<br /><br />转载请注明出处，谢谢！
          <br/>
          <span style="color:red;">
            <a href="http://jamsa.javaeye.com/blog/167874#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></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;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 04 Mar 2008 22:07:46 +0800</pubDate>
        <link>http://jamsa.javaeye.com/blog/167874</link>
        <guid>http://jamsa.javaeye.com/blog/167874</guid>
      </item>
      <item>
        <title>重新整理后的Oracle OAF学习笔记——7.实体对象</title>
        <author>Jamsa</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jamsa.javaeye.com">Jamsa</a>&nbsp;
          链接：<a href="http://jamsa.javaeye.com/blog/167872" style="color:red;">http://jamsa.javaeye.com/blog/167872</a>&nbsp;
          发表时间: 2008年03月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <h2>
关于实体对象</h2>

<p class="first">实体对象包含了业务逻辑和对表的DML操作。</p>

<h3><a name="sec2" id="sec2"></a>
对象模型和关键类</h3>

<ul><li>oracle.apps.fnd.framework.server.OAEntityCache：这个缓冲用于存储特殊实体的查询过的行。映射到同样的实体的多个视图对象共享相同的实体缓存。</li><li>&lt;YourEntityName&gt;EOImpl继承oracle.apps.fnd.framework.server.OAEntityImpl：这是实体对象本身。当实例化后，它代表数据中的一行。</li><li>oracle.apps.fnd.framework.server.OAEntityDefImpl：表示描述实体对象的元数据，包括属性
（attributes）、事件、校验器、关联和属性（properties）。当实例化后，它描述了实体对象类的所有实例。实体定义类是一个单例类。</li><li>&lt;YourEntityName&gt;Expert继承oracle.apps.fnd.framework.server.OAEntityExpert：这是一个特殊的单例辅助类，它用于注册一个实体。</li><li>oracle.jbo.Key：这是一个不可变的主、外键或复合主键。</li></ul>



<h2><a name="sec3" id="sec3"></a>
创建</h2>

<p class="first">为了创建实体对象，必须调用对应的上层视图对象中的createRow方法然后再调用insertRow方法。</p>

<pre class="src"><span style="color: #ff7f24">// </span><span style="color: #ff7f24">In the application module; this example from the OA Framework
</span><span style="color: #ff7f24">// </span><span style="color: #ff7f24">ToolBox Tutorial will instantiate a SupplierEOImpl.
</span>
<span style="color: #00ffff">public</span> <span style="color: #98fb98">void</span> <span style="color: #87cefa">create</span>()
{
  <span style="color: #98fb98">OAViewObject</span> <span style="color: #eedd82">vo</span> = getSuppliersVO();
  vo.insertRow(vo.createRow());

  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Always call this after you perform a row insert. See the Entity Object
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">New / Initial section below for additional information.
</span>  vo.setNewRowState(<span style="color: #7fffd4">Row</span>.STATUS_INITIALIZED);
}
</pre>
视图对象的createRow方法调用下层实体对象的create()方法。可以在实体的create()方法中添加初始化代码，可以参考
oracle.apps.fnd.framework.toolbox.tutorial.server.SupplierEOImpl类。
<p><strong>警告：</strong> 不要将初始化逻辑放到实体对象的构造器中；总是应该将这些代码添加到create()方法中super.create(attributeList)方法调用的后面。</p>

<p><strong>技巧：</strong>
如果默认值可以在设计时决定，并且是为特定的UI而决定的，也可以通过在设计器中设置item的Initial
Value属性来指定默认值。这些值可以被用户个性化；而不需要创建实体你的对象的子类并覆盖create()方法来设置默认值。查看
Defaulting章节查看细节信息。</p>

<pre class="src"><span style="color: #ffa07a">/**
 * In the SupplierEOImpl class; initialize a new supplier.
 */</span>
Public <span style="color: #98fb98">void</span> <span style="color: #87cefa">create</span>(<span style="color: #98fb98">AttributeList</span> <span style="color: #eedd82">attributeList</span>)
{
  <span style="color: #00ffff">super</span>.create(attributeList);
  <span style="color: #98fb98">OADBTransaction</span> <span style="color: #eedd82">transaction</span> = getOADBTransaction();

  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Supplier id is obtained from the table's sequence
</span>  <span style="color: #98fb98">Number</span> <span style="color: #eedd82">supplierId</span> = transaction.getSequenceValue(<span style="color: #ffa07a">&quot;FWK_TBX_SUPPLIERS_S&quot;</span>);
  setSupplierId(supplierId);

  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Start date should be set to sysdate
</span>  setStartDate(transaction.getCurrentDBDate());
} <span style="color: #ff7f24">// </span><span style="color: #ff7f24">end create()
</span></pre>
<strong>技巧：</strong>
当在实体对象中设置值时，总是使用set&lt;AttributeName&gt;(val)代替setAttribute(&quot;&lt;
AttributeName&gt;&quot;,
val)方法可以提高性能，因为前者跳过了查找字段的步骤。如果需要忽略编程方式实现的属性校验而仍需要执行设计时定义的校验时，可以直接调用
setAttributeInternal()。查看Entity Object and View Object Attribute
Setters以获取更多信息。
<h3><a name="sec4" id="sec4"></a>
复合实体关联</h3>

<p class="first">在复合关联中BC4J将在设置父对象主键属性值时自动设置子实体对象。父对象主键值是在调用create()方法时通过attributeList参数传递进去的，并且在super.create(attributeList)被执行时被设置值。</p>

<p>不要尝试自己来处理主键值。</p>


<h3><a name="sec5" id="sec5"></a>
实体对象的Initial/New状态</h3>

<p class="first">缺省情况下，实体对象被创建时row状态为STATUS_NEW，并且BC4J将它们添加到它们的校验器中并且
post监听。这时，任何事件触发校验或数据库提交sequence包括这些实体对象。（By default, entity objects
are created with the row state of STATUS_NEW, and BC4J adds them to its
validation and post listener lists. In this case, any event that
triggers a validation or database post sequence includes these entity
objects.）</p>

<p>如OA Framework Model Coding Standards中的规定，应该将通过显式的在视图对象中调用创建新行的方法后立即调用ViewRowImpl对象的setNewRowState(STATE_INITIALIZED)方法。</p>

<p>当执行这个后，BC4J将从事务中和校验监听列表中移除对应的实体对象，这样它们将不会被校验或提交到数据库。当用户做出修改（属性的
&ldquo;setter&rdquo;被调用后），实体对象的状态修改为STATUS_NEW，并且BC4J返回它到validation/post
lists。你也可以在ViewRowImpl上调用setNewRowState(STATUS_NEW)在任何时候手工改变状态。</p>


<h3><a name="sec6" id="sec6"></a>
特殊&ldquo;Create&rdquo;的情况</h3>

<p><strong>&ldquo;Flattened&rdquo;主／从处于单一行中</strong></p>

<p>在OA Framework ToolBox教程中，我们有主／从实体显示于同一行中，&ldquo;flattened&rdquo;行中。比如采购单包含了很多行，它们依次包含了多个供货商，在我们的UI中，我们将采购单的行和供货单实现为1:1的关系。</p>

<p>尽管BC4J可以很容易的为单个视图对象行创建多个不同类型的实体对象&mdash;&mdash;这些实体对象是不相关的或是平等的&mdash;&mdash;在一个对象是另一个对象子对象时需
要你介入。在这种情况下，必须在你的视图对象行实现的create()方法中添加下面的代码，以确保正确的父对象的主键被设置到低层次的子对象的实体中：</p>

<pre class="src"><span style="color: #ff7f24">// </span><span style="color: #ff7f24">The following is required to support the creating the master/detail line
</span><span style="color: #ff7f24">// </span><span style="color: #ff7f24">and shipment entities that have been &quot;flattened&quot; into a single row in
</span><span style="color: #ff7f24">// </span><span style="color: #ff7f24">POLineShipFullVO with a 1:1 join.
</span><span style="color: #ff7f24">//</span><span style="color: #ff7f24">
</span><span style="color: #ff7f24">// </span><span style="color: #ff7f24">If you don't do this, BC4J will automatically treat them like peers and
</span><span style="color: #ff7f24">// </span><span style="color: #ff7f24">try to set the po header id as the parent key for both entities.
</span>
<span style="color: #00ffff">protected</span> <span style="color: #98fb98">void</span> <span style="color: #87cefa">create</span>(<span style="color: #7fffd4">oracle</span>.<span style="color: #7fffd4">jbo</span>.<span style="color: #98fb98">AttributeList</span> <span style="color: #eedd82">nvp</span>)
{
  <span style="color: #98fb98">PurchaseOrderLineEOImpl</span> <span style="color: #eedd82">lineEO</span> = (<span style="color: #98fb98">PurchaseOrderLineEOImpl</span>)getEntity(0);
  <span style="color: #98fb98">PurchaseOrderShipmentEOImpl</span> <span style="color: #eedd82">shipmentEO</span> = (<span style="color: #98fb98">PurchaseOrderShipmentEOImpl</span>)getEntity(1);

  <span style="color: #00ffff">try</span>
  {
    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Create Lines EO
</span>    lineEO.create(nvp);

    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Create Shipments EO
</span>    shipmentEO.create(lineEO);

    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Calling this ensures that any personalization default values are
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">properly set since the OAF normally sets this in the super.create(), but
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">since this is not called in this workaround, we need another method
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">to ensure customer defaults are applied.
</span>    setDefaultValue();
  }
  <span style="color: #00ffff">catch</span> (<span style="color: #98fb98">Exception</span> <span style="color: #eedd82">ex</span>)
  {
    lineEO.revert();
    shipmentEO.revert();

    <span style="color: #00ffff">if</span> (ex <span style="color: #00ffff">instanceof</span> <span style="color: #7fffd4">oracle</span>.<span style="color: #7fffd4">jbo</span>.JboException)
    {
      <span style="color: #7fffd4">oracle</span>.<span style="color: #7fffd4">jbo</span>.<span style="color: #98fb98">JboException</span> <span style="color: #eedd82">jboEx</span> = (<span style="color: #7fffd4">oracle</span>.<span style="color: #7fffd4">jbo</span>.<span style="color: #98fb98">JboException</span>)ex;
      <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Developers have to do the mapping on their own because of the override.
</span>      jboEx.doEntityToVOMapping(getApplicationModule(), <span style="color: #00ffff">new</span> <span style="color: #7fffd4">oracle</span>.<span style="color: #7fffd4">jbo</span>.<span style="color: #98fb98">ViewObject</span>[]{getViewObject()});
      <span style="color: #00ffff">throw</span> jboEx;
    }
    <span style="color: #00ffff">throw</span> OAException.wrapperException(ex);
  }
} <span style="color: #ff7f24">// </span><span style="color: #ff7f24">end create()
</span></pre>


<h3><a name="sec7" id="sec7"></a>
实体对象缓存</h3>

<p class="first">一旦被创建后，BC4J将实体对象因为各种原因被存储于特殊的事务缓冲区中，在JDeveloper BC4J文档中有完整的描述。两个重要的好处在于：</p>

<ul><li>处理同一个根应用模块中的多个视图对象可以共享同样的下层实体对象。这意味着在一个视图对象中修改实体后将立即反映到其它引用该实体对象的视图对象中。</li><li>数据修改将被保留在缓冲区中即使视图对象的行集被刷新。比如，在主－从关系中，在 <strong>从</strong> 视图对象中由实体对象派生的属性值被保存在缓存中，即使用户从一个 <strong>主</strong> 视图对象转换到另一个。所有数据修改将原封不动的保存在事务生命中。</li></ul>

<p>懂得这个缓冲区的存在是很重要的，因为你必须明确的执行某些验证，比如，当执行唯一性验证时你必须同时检查实体缓冲区和数据库。</p>

<p>有三种主要方式同时检查缓冲区和数据库：</p>

<ol><li>使用findByPrimaryKey()方法</li><li>手工迭代缓冲区</li><li>使用关联对象迭代缓冲区</li></ol>

<p><strong>findByPrimaryKey()方法</strong></p>

<p>findByPrimaryKey()方法确保先查询缓冲区中与给定的主键相匹配的实体对象，然后查找数据库。这是一个非常用有的方法，但它并不是一个轻量的方法，因为它为从数据库中找到的记录实例化实体对象。它将 <strong>整个</strong> 实体对象放入内存，而不只是主键。这个方法可以&mdash;&mdash;也应该&mdash;&mdash;被用于当你不需要查找一个匹配的情况&mdash;&mdash;比如，当验证一个基于序列的主键。它也适合用于需要在查找到的目标上调用方法以便中间层访问的情况。</p>

<p>下面的代码来自于oracle.apps.fnd.framework.toolbox.schema.server.SupplierEOImpl类，描述了这个方法的使用：</p>

<pre class="src"><span style="color: #00ffff">public</span> <span style="color: #98fb98">void</span> <span style="color: #87cefa">setSupplierId</span>(<span style="color: #98fb98">Number</span> <span style="color: #eedd82">value</span>)
{
  <span style="color: #00ffff">if</span> (value != null)
  {
    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Supplier id must be unique. To verify this, you must check both the
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">entity cache and the database. In this case, it's appropriate
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">to use findByPrimaryKey( ) because you're unlikely to get a match, and
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">and are therefore unlikely to pull a bunch of large objects into memory.
</span>
    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Note that findByPrimaryKey() is guaranteed to check all suppliers.
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">First it checks the entity cache, then it checks the database.
</span>
    <span style="color: #98fb98">OADBTransaction</span> <span style="color: #eedd82">transaction</span> = getOADBTransaction();
    <span style="color: #98fb98">Object</span>[] <span style="color: #eedd82">supplierKey</span> = {value};
    <span style="color: #98fb98">EntityDefImpl</span> <span style="color: #eedd82">supplierDefinition</span> = SupplierEOImpl.getDefinitionObject();
    <span style="color: #98fb98">SupplierEOImpl</span> <span style="color: #eedd82">supplier</span> =
      (<span style="color: #98fb98">SupplierEOImpl</span>)supplierDefinition.findByPrimaryKey(transaction, <span style="color: #00ffff">new</span> <span style="color: #98fb98">Key</span>(supplierKey));
    <span style="color: #00ffff">if</span> (supplier != null)
    {
      <span style="color: #00ffff">throw</span> <span style="color: #00ffff">new</span> <span style="color: #98fb98">OAAttrValException</span>(<span style="color: #7fffd4">OAException</span>.TYP_ENTITY_OBJECT,
             getEntityDef().getFullName(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO name
</span>             getPrimaryKey(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO PK
</span>             <span style="color: #ffa07a">&quot;SupplierId&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute Name
</span>             value, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Bad attribute value
</span>             <span style="color: #ffa07a">&quot;ICX&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message application short name
</span>             <span style="color: #ffa07a">&quot;FWK_TBX_T_SUP_ID_UNIQUE&quot;</span>); <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message name
</span>    }
  }

  setAttributeInternal(SUPPLIERID, value);
} <span style="color: #ff7f24">// </span><span style="color: #ff7f24">end setSupplierId()
</span></pre>

<p><strong>手工迭代缓冲区</strong></p>

<p>可以通过手工的方式检查实体缓冲区，以执行与findByPrimaryKey()相同的检查。然后再在另一步中执行对数据库的检查。这种方式的好处是可以避免不必要的实例对象（译：实体）。</p>

<p>下面的例子也是来自于ToolBox教程中SupplierEOImpl类中：</p>

<pre class="src"><span style="color: #00ffff">public</span> <span style="color: #98fb98">void</span> <span style="color: #87cefa">setName</span>(<span style="color: #98fb98">String</span> <span style="color: #eedd82">value</span>)
{
  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Since this value is marked as &quot;mandatory,&quot; the BC4J Framework will take
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">care of ensuring that it's a non-null value. However, if it is null, we
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">don't want to proceed with any validation that could result in a NPE.
</span>
  <span style="color: #00ffff">if</span> ((value != null) || (!(<span style="color: #ffa07a">&quot;&quot;</span>.equals(value.trim()))))
  {
    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Verify that the name is unique. To do this, we must check both the entity
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">cache and the database. We begin with the entity cache.
</span>    <span style="color: #7fffd4">com</span>.<span style="color: #7fffd4">sun</span>.<span style="color: #7fffd4">java</span>.<span style="color: #7fffd4">util</span>.<span style="color: #7fffd4">collections</span>.<span style="color: #98fb98">Iterator</span> <span style="color: #eedd82">supplierIterator</span> =
      getEntityDef().getAllEntityInstancesIterator(getDBTransaction());

    <span style="color: #98fb98">Number</span> <span style="color: #eedd82">currentId</span> = getSupplierId();

    <span style="color: #00ffff">while</span> ( supplierIterator.hasNext() )
    {
      <span style="color: #98fb98">SupplierEOImpl</span> <span style="color: #eedd82">cachedSupplier</span> = (<span style="color: #98fb98">SupplierEOImpl</span>)supplierIterator.next();

      <span style="color: #98fb98">String</span> <span style="color: #eedd82">cachedName</span> = cachedSupplier.getName();
      <span style="color: #98fb98">Number</span> <span style="color: #eedd82">cachedId</span> = cachedSupplier.getSupplierId();

      <span style="color: #ff7f24">// </span><span style="color: #ff7f24">We found a match for the name we're trying to set, so throw an
</span>      <span style="color: #ff7f24">// </span><span style="color: #ff7f24">exception. Note that we need to exclude this EO from our test.
</span>
      If (cachedName != null &amp;&amp; value.equalsIgnoreCase(cachedName) &amp;&amp;
        cachedId.compareTo(currentId) != 0 )
      {
        <span style="color: #00ffff">throw</span> <span style="color: #00ffff">new</span> <span style="color: #98fb98">OAAttrValException</span>(<span style="color: #7fffd4">OAException</span>.TYP_ENTITY_OBJECT,
                               getEntityDef().getFullName(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO name
</span>                               getPrimaryKey(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO PK
</span>                               <span style="color: #ffa07a">&quot;Name&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute Name
</span>                               value, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute value
</span>                               <span style="color: #ffa07a">&quot;ICX&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message product short name
</span>                               <span style="color: #ffa07a">&quot;FWK_TBX_T_SUP_DUP_NAME&quot;</span>); <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message name
</span>      }
    }

    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Now we want to check the database for any occurrences of the supplier
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">name. The most efficient way to check this is with a validation view
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">object which we add to a special &quot;Validation&quot; application module.
</span>    <span style="color: #98fb98">OADBTransaction</span> <span style="color: #eedd82">transaction</span> = getOADBTransaction();
    <span style="color: #98fb98">OAApplicationModule</span> <span style="color: #eedd82">vam</span>;
    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Look to see if the VAM has already been created in this transaction. If not,
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">create it.
</span>    vam = (<span style="color: #98fb98">OAApplicationModule</span>)transaction.findApplicationModule(<span style="color: #ffa07a">&quot;supplierVAM&quot;</span>);
    <span style="color: #00ffff">if</span> (vam == null)
    {
      vam =
      (<span style="color: #98fb98">OAApplicationModule</span>)transaction.createApplicationModule(<span style="color: #ffa07a">&quot;supplierVAM&quot;</span>,
         <span style="color: #ffa07a">&quot;oracle.apps.fnd.framework.toolbox.schema.server.SupplierVAM&quot;</span>);
    }

    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Now, we use a lightweight &quot;validation&quot; view object to see if a supplier exists
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">with the given name.
</span>    <span style="color: #98fb98">SupplierNameVVOImpl</span> <span style="color: #eedd82">valNameVo</span> = (<span style="color: #98fb98">SupplierNameVVOImpl</span>)vam.findViewObject(<span style="color: #ffa07a">&quot;SupplierNameVVO&quot;</span>);
    valNameVo.initQuery(value);
    <span style="color: #00ffff">if</span> (valNameVo.hasNext())
    {
      <span style="color: #00ffff">throw</span> <span style="color: #00ffff">new</span> <span style="color: #98fb98">OAAttrValException</span>(<span style="color: #7fffd4">OAException</span>.TYP_ENTITY_OBJECT,
            getEntityDef().getFullName(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO name
</span>            getPrimaryKey(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO PK
</span>            <span style="color: #ffa07a">&quot;Name&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute Name
</span>            value, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute value
</span>            <span style="color: #ffa07a">&quot;ICX&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message application short name
</span>            <span style="color: #ffa07a">&quot;FWK_TBX_T_SUP_DUP_NAME&quot;</span>); <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message name
</span>    }
  }

  setAttributeInternal(NAME, value);
} <span style="color: #ff7f24">// </span><span style="color: #ff7f24">end setName()
</span></pre>

<p><strong>关联对象迭代</strong></p>

<p>这与findByPrimaryKey()类似，它保证同时检查实体缓冲区和数据库。它也将会将找到的实体对象加载到内存，这用于需要调用实体中方
法时。与findByPrimaryKey()方法不同，它可以通过任何key查找任何类型的实体对象，这只用于与当前对象间通过关联对象相关联的实体对
象。</p>

<p>下面的代码描述了根复合实体对象使用关联对象查找它的所有子对象。</p>

<pre class="src"><span style="color: #00ffff">private</span> <span style="color: #98fb98">void</span> <span style="color: #87cefa">checkLineExists</span>()
{
  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">A purchase order header must have at least 1 associated line.
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">To check this, we first do a manual check of the entity cache
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">If we find a line for this header, we're done (note that the entity cache won't
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">include EOs that are DELETED or DEAD).
</span>
  <span style="color: #7fffd4">com</span>.<span style="color: #7fffd4">sun</span>.<span style="color: #7fffd4">java</span>.<span style="color: #7fffd4">util</span>.<span style="color: #7fffd4">collections</span>.<span style="color: #98fb98">Iterator</span> <span style="color: #eedd82">fastIterator</span> =
    PurchaseOrderLineEOImpl.getDefinitionObject().getAllEntityInstancesIterator(getDBTransaction());

  <span style="color: #98fb98">Number</span> <span style="color: #eedd82">currentHeaderId</span> = getHeaderId();
  <span style="color: #00ffff">while</span> ( fastIterator.hasNext() )
  {
    <span style="color: #98fb98">PurchaseOrderLineEOImpl</span> <span style="color: #eedd82">cachedLine</span> = (<span style="color: #98fb98">PurchaseOrderLineEOImpl</span>)fastIterator.next();

    <span style="color: #98fb98">Number</span> <span style="color: #eedd82">cachedHeaderId</span> = cachedLine.getHeaderId();

    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">If we find a match, we're done. Don't forget to look ONLY for lines
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">for this header... The entity cache can include lines for other headers
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">also.
</span>
    If ((cachedHeaderId != null) &amp;&amp; (cachedHeaderId.compareTo(currentHeaderId) == 0 ))
    {
      <span style="color: #00ffff">return</span>;
    }
  }

  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">We haven't found any matches in the cache yet, so now we need to check
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">the database...
</span>
  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">In this example, we're illustrating the use of the association between the
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">header and its lines to iterate through all the shipments.  This will
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">check both the cache and the database, and will bring all the rows
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">into the middle tier.
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Note that this looks only at lines for this
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">header so we don't need to filter our results, so it is convenient.
</span>  <span style="color: #98fb98">RowIterator</span> <span style="color: #eedd82">linesIterator</span> = getPurchaseOrderLineEO();

  <span style="color: #00ffff">if</span> (!(linesIterator.hasNext()))
  {
    <span style="color: #00ffff">throw</span> <span style="color: #00ffff">new</span> <span style="color: #98fb98">OARowValException</span>(<span style="color: #7fffd4">OARowValException</span>.TYP_ENTITY_OBJECT,
                getEntityDef().getFullName(),
                getPrimaryKey(),
                <span style="color: #ffa07a">&quot;ICX&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message product short name
</span>                <span style="color: #ffa07a">&quot;FWK_TBX_T_PO_NO_LINES&quot;</span>); <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message name
</span>  }
} <span style="color: #ff7f24">// </span><span style="color: #ff7f24">end checkLineExists()
</span></pre>


<h3><a name="sec8" id="sec8"></a>
实体状态</h3>

<p class="first">每个实体对象都有一个相关的&ldquo;实体状态&rdquo;它描述了实体的状态与下层数据库和事务关联。可以调用getEntityState()检查实体的状态。</p>

<p><strong>技巧：</strong> BC4J从实体缓冲区中自动移除任何状态为STATUS_DEAD的实体对象，因此你不需要在查找&ldquo;好&rdquo;的实体对象时关心手工排除这些对象的问题。</p>

<ul><li>STATUS_NEW 实体对象在当前事务中是新的</li><li>STATUS_DELETED 实体对象来自于数据库并且在当前事务中已经被删除</li><li>STATUS_MODIFIED 实体对象来自于数据库并且已经被改变了</li><li>STATUS_UNMODIFIED 实体对象来源于数据库并且没有被改变为，或者已经被改变过并且改变已经被提交</li><li>STATUS_DEAD 实体对象在当前事务中是新的并且已经被删除</li><li>STATUS_INITIALIZED 实体对象牌&ldquo;临时（temporary）&rdquo;状态并且将不会被提交或校验</li></ul>



<h2><a name="sec9" id="sec9"></a>
修改／校验</h2>

<p class="first">这节描述如何正确执行属性级和实体级的校验。</p>

<h3><a name="sec10" id="sec10"></a>
属性级校验</h3>

<p class="first">如<a href="file:///e:/devel/docs/publish_html/ebs_build_view.html">实现视图</a>一章中描述的，当向页面发起HTTP POST请求时修改的值时，OA Framework将这些值回写到下层的视图对象，再通过调用实体对象的setter方法将这些值写入下层的实体对象。</p>

<p>因此每个属性的校验应该被添加到它的setter方法中（查看ToolBox的PurchaseOrderHeaderEOImpl的setHeaderId()方法，如下），调用实体对象的setter方法执行的是属性级的校验。</p>

<p>如果显示的指定了校验（比如，你在JDeveloper Entity Object
Wizard中指定一个属性在它被保存后不能被更新），这个校验是在setAttributeInternal()方法中执行的，你应该将它放在你自己的
校验逻辑的后面执行。它也将在validateEntity()中执行。</p>

<pre class="src"><span style="color: #ffa07a">/**
 * Sets the PO Header Id.
 *
 * Business Rules:
 * Required; cannot be null.
 * Cannot be updated on a committed row.
 */</span>
<span style="color: #00ffff">public</span> <span style="color: #98fb98">void</span> <span style="color: #87cefa">setHeaderId</span>(<span style="color: #98fb98">Number</span> <span style="color: #eedd82">value</span>)
{
  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">BC4J validates that this can be updated only on a new line. This
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">adds the additional check of only allowing an update if the value
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">is null to prevent changes while the object is in memory.
</span>
  If (getHeaderId() != null)
  {
    <span style="color: #00ffff">throw</span> <span style="color: #00ffff">new</span> <span style="color: #98fb98">OAAttrValException</span>(<span style="color: #7fffd4">OAException</span>.TYP_ENTITY_OBJECT,
                                 getEntityDef().getFullName(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO name
</span>                                 getPrimaryKey(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO PK
</span>                                 <span style="color: #ffa07a">&quot;HeaderId&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute Name
</span>                                 value, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute value
</span>                                 <span style="color: #ffa07a">&quot;ICX&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message product short name
</span>                                 <span style="color: #ffa07a">&quot;DEBUG -- need message name&quot;</span>); <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message name
</span>  }
  <span style="color: #00ffff">if</span> (value != null)
  {
    <span style="color: #98fb98">OADBTransaction</span> <span style="color: #eedd82">transaction</span> = (<span style="color: #98fb98">OADBTransaction</span>)getOADBTransaction();

    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">findByPrimaryKey() is guaranteed to first check the entity cache, then check
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">the database. This is an appropriate use of this method because finding a
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">match would be the exception rather than the rule so we're not worried
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">about pulling entities into the middle tier.
</span>
    <span style="color: #98fb98">Object</span>[] <span style="color: #eedd82">headerKey</span> = {value};
    <span style="color: #98fb98">EntityDefImpl</span> <span style="color: #eedd82">hdrDef</span> = PurchaseOrderHeaderEOImpl.getDefinitionObject();
    <span style="color: #98fb98">PurchaseOrderHeaderEOImpl</span> <span style="color: #eedd82">hdrEO</span> =
      (<span style="color: #98fb98">PurchaseOrderHeaderEOImpl</span>)hdrDef.findByPrimaryKey(transaction, <span style="color: #00ffff">new</span> <span style="color: #98fb98">Key</span>(headerKey));

    <span style="color: #00ffff">if</span> (hdrEO != null)
    {
      <span style="color: #00ffff">throw</span> <span style="color: #00ffff">new</span> <span style="color: #98fb98">OAAttrValException</span>(<span style="color: #7fffd4">OAException</span>.TYP_ENTITY_OBJECT,
                                   getEntityDef().getFullName(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO name
</span>                                   getPrimaryKey(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO PK
</span>                                   <span style="color: #ffa07a">&quot;HeaderId&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute Name
</span>                                   value, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute value
</span>                                   <span style="color: #ffa07a">&quot;ICX&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message product short name
</span>                                   <span style="color: #ffa07a">&quot;FWK_TBX_T_PO_ID_UNIQUE&quot;</span>); <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message name
</span>    }
  }

  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Executes declarative validation, and finally sets the new value.
</span>  setAttributeInternal(HEADERID, value);
} <span style="color: #ff7f24">// </span><span style="color: #ff7f24">end setHeaderId()
</span></pre>

<p><strong>不同的&ldquo;Set&rdquo;方法</strong></p>

<p>有多种方法可以设置实体变量的值。在编码中，通常调用set&lt;AttributeName&gt;()和
setAttributeInternal()。查看Entity Object and View Object Attribute
Setters获取更多的信息。</p>


<h3><a name="sec11" id="sec11"></a>
交叉属性校验</h3>

<p class="first">任何与两个个或更多属性值相关的校验应该被包含在validateEntity()方法中；不要将交叉属性校验放在单个属性的setter方法中，因为属性值的设置可能是无序的。</p>


<h3><a name="sec12" id="sec12"></a>
实体校验</h3>

<p class="first">当OA Framework在HTTP
POST处理周期中设置实体对象值时，它总会校验它接触到的视图对象的行，它依次在下层的实体对象（一个或多个）上调用validateEntity()
方法。而且，entities are validated again prior to posting (up to 10 times in
a composition).</p>

<p>任何操作于行级的逻辑&mdash;&mdash;且对被重复调用不是非常敏感的校验&mdash;&mdash;应该被包含在validateEntity()方法中。</p>

<p>下面的PurchaseOrderHeaderEOImpl代码描述了实体级的校验：</p>

<pre class="src"><span style="color: #ffa07a">/**
 * Performs entity-level validation including cross-attribute validation that
 * is not appropriately performed in a single attribute setter.
 */</span>
<span style="color: #00ffff">protected</span> <span style="color: #98fb98">void</span> <span style="color: #87cefa">validateEntity</span>()
{
  <span style="color: #00ffff">super</span>.validateEntity();

  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">If our supplier value has changed, verify that the order is in an &quot;IN_PROCESS&quot;
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">or &quot;REJECTED&quot; state. Changes to the supplier in any other state are disallowed.
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Note that these checks for supplier and site are both performed here
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">because they are doing cross-attribute validation.
</span>
  <span style="color: #98fb98">String</span> <span style="color: #eedd82">status</span> = getStatusCode();

  <span style="color: #00ffff">if</span> (((<span style="color: #ffa07a">&quot;APPROVED&quot;</span>)Equals(status)) || (<span style="color: #ffa07a">&quot;COMPLETED&quot;</span>Equals(status)))
  {
    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Start by getting the original value and comparing it to the current
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">value. Changes at this point are invalid.
</span>
    <span style="color: #98fb98">Number</span> <span style="color: #eedd82">oldSupplierId</span> = (<span style="color: #98fb98">Number</span>)getPostedAttribute(SUPPLIERID);
    <span style="color: #98fb98">Number</span> <span style="color: #eedd82">currentSupplierId</span> = getSupplierId();

    <span style="color: #00ffff">if</span> (oldSupplierId.compareTo(currentSupplierId) != 0)
    {
      <span style="color: #00ffff">throw</span> <span style="color: #00ffff">new</span> <span style="color: #98fb98">OAAttrValException</span>(<span style="color: #7fffd4">OAException</span>.TYP_ENTITY_OBJECT,
                                   getEntityDef().getFullName(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO name
</span>                                   getPrimaryKey(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO PK
</span>                                   <span style="color: #ffa07a">&quot;SupplierId&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute Name
</span>                                   currentSupplierId, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute value
</span>                                   <span style="color: #ffa07a">&quot;ICX&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message product short name
</span>                                   <span style="color: #ffa07a">&quot;FWK_TBX_T_PO_SUPPLIER_NOUPDATE&quot;</span>); <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message name
</span>    }

    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">If our supplier site has changed, verify that the order is in an &quot;IN_PROCESS&quot;
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">state. Changes to the supplier site in any other state are disallowed.
</span>
    <span style="color: #98fb98">Number</span> <span style="color: #eedd82">oldSiteId</span> = (<span style="color: #98fb98">Number</span>)getPostedAttribute(SUPPLIERSITEID);
    <span style="color: #98fb98">Number</span> <span style="color: #eedd82">currentSiteId</span> = getSupplierSiteId();

    <span style="color: #00ffff">if</span> (oldSiteId.compareTo(currentSiteId) != 0)
    {
      <span style="color: #00ffff">throw</span> <span style="color: #00ffff">new</span> <span style="color: #98fb98">OAAttrValException</span>(<span style="color: #7fffd4">OAException</span>.TYP_ENTITY_OBJECT,
                                  getEntityDef().getFullName(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO name
</span>                                  getPrimaryKey(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO PK
</span>                                  <span style="color: #ffa07a">&quot;SupplierId&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute Name
</span>                                  currentSiteId, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute value
</span>                                  <span style="color: #ffa07a">&quot;ICX&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message product short name
</span>                                  <span style="color: #ffa07a">&quot;FWK_TBX_T_PO_SUPSITE_NOUPDATE&quot;</span>); <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message name
</span>    }
  }

  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Verify that our supplier site is valid for the supplier and make sure it is
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">an active &quot;Purchasing&quot; site.
</span>
  <span style="color: #98fb98">SupplierEntityExpert</span> <span style="color: #eedd82">supplierExpert</span> =
    SupplierEOImpl.getSupplierEntityExpert(getOADBTransaction());

  <span style="color: #00ffff">if</span> (!(supplierExpert.isSiteValidForPurchasing(getSupplierId(), getSupplierSiteId())))
  {
    <span style="color: #00ffff">throw</span> <span style="color: #00ffff">new</span> <span style="color: #98fb98">OAAttrValException</span>(<span style="color: #7fffd4">OAException</span>.TYP_ENTITY_OBJECT,
                                 getEntityDef().getFullName(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO name
</span>                                 getPrimaryKey(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">EO PK
</span>                                 <span style="color: #ffa07a">&quot;SupplierSiteId&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute Name
</span>                                 getSupplierSiteId(), <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Attribute value
</span>                                 <span style="color: #ffa07a">&quot;ICX&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message product short name
</span>                                 <span style="color: #ffa07a">&quot;FWK_TBX_T_PO_SUPSITE_INVALID&quot;</span>); <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message name
</span>  }
} <span style="color: #ff7f24">// </span><span style="color: #ff7f24">end validateEntity();
</span></pre>


<h3><a name="sec13" id="sec13"></a>
交叉实体校验</h3>

<p class="first">开发者经常认为他们需要实现&ldquo;交叉实体（cross-entity）&rdquo;校验，一个实体对象在校验中调用另一个的方法。在OA Framework中，&ldquo;交叉实体校验&rdquo;意味着某些非常特殊的东西：</p>

<ul><li>实体A和实体B在执行validateEntity()方法时各自己引用对方（因为实体A需要从实体B获得一些属性，实体B需要从实体A获得一些属性）and...</li><li>期望两个对象都是&ldquo;脏（dirty）&rdquo;对象（需要校验）在同一个事务中and...</li><li>另一个实体对象必须是有效的，以便引用它的对象获取属性值用于自己的校验。这样问题就来了：哪个实体应该先校验？</li></ul>

<p><strong>技巧：</strong> 对于复合关联的主／从实体对象这不是个问题，因为子对象将会先于父对象被校验，且BC4J Framework将校验复合层级结构向上10次的校验，从底部到顶部直到所有实体都是有效的。</p>

<p>需要&ldquo;交叉实体校验&rdquo;的环境是非常少见的。如果你觉得需要，解决的办法是创建一个特殊的&ldquo;调停者&rdquo;对象实现BC4J的ValidationListener接口。简单来说，这个对象交叉实体中的哪个对象的校验先执行。</p>


<h3><a name="sec14" id="sec14"></a>
不妥当的校验失败处理</h3>

<p class="first">在实体级的校验方法（validateEntity()，set&lt;AttributeName&gt;()或其
它）中调用Transaction.rollback()，Transaction.clearEntityCache()执行回滚或清除BC4J缓冲的
操作。如果因为某些原因需要执行这些操作，你必须按下面的方法在 <strong>应用模块／事务级（application module/transaction level）</strong> 捕获校验异常，并执行你需要的方法。比如，在应用模块级执行回滚是安全的；在实体级执行回滚或清理实体缓冲区却不是，并且可能导致不可预知的行为。</p>

<pre class="src"><span style="color: #7fffd4">Bad</span> <span style="color: #7fffd4">Code</span>:
---------
<span style="color: #00ffff">protected</span> <span style="color: #98fb98">void</span> validateEntity()
{
  ...
  DBTransaction txn = getDBTransaction();

  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Do not issue a rollback from within the EO.
</span>  txn.rollback();
  <span style="color: #00ffff">throw</span> OAException(...);
}

<span style="color: #7fffd4">Good</span> <span style="color: #7fffd4">Code</span>:
----------
<span style="color: #00ffff">protected</span> <span style="color: #98fb98">void</span> validateEntity()
{
  ...
  <span style="color: #00ffff">throw</span> OAException(...);
}

<span style="color: #ff7f24">// </span><span style="color: #ff7f24">The following logic is written at the application-module level.
</span>
<span style="color: #00ffff">try</span>
{
  txn.commit();
}
<span style="color: #00ffff">catch</span> (<span style="color: #98fb98">OAException</span> <span style="color: #eedd82">e</span>)
{
  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Cache the exception thrown by the validation logic in the EO,
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">and perform the rollback.
</span>  txn.rollback();
}
</pre>



<h2><a name="sec15" id="sec15"></a>
删除</h2>

<p class="first">为了删除实体对象，可以在对应的视图对象上调用remove()方法，如下面的应用模块中的代码所示。</p>

<pre class="src"><span style="color: #ffa07a">/**
 * Deletes a purchase order from the PoSimpleSummaryVO using the
 * poHeaderId parameter.
 */</span>
<span style="color: #00ffff">public</span> <span style="color: #98fb98">Boolean</span> <span style="color: #87cefa">delete</span>(<span style="color: #98fb98">String</span> <span style="color: #eedd82">poHeaderId</span>)
{
  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">First, we need to find the selected purchase order in our VO.
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">When we find it, we call remove( ) on the row which in turn
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">calls remove on the associated PurchaseOrderHeaderEOImpl object.
</span>  <span style="color: #98fb98">int</span> <span style="color: #eedd82">poToDelete</span> = Integer.parseInt(poHeaderId);

  <span style="color: #98fb98">OAViewObject</span> <span style="color: #eedd82">vo</span> = getPoSimpleSummaryVO();
  <span style="color: #98fb98">PoSimpleSummaryVORowImpl</span> <span style="color: #eedd82">row</span> = null;

  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">This tells us the number of rows that have been fetched in the
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">row set, and will not pull additional rows in like some of the
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">other &quot;get count&quot; methods.
</span>  <span style="color: #98fb98">int</span> <span style="color: #eedd82">fetchedRowCount</span> = vo.getFetchedRowCount();
  <span style="color: #98fb98">boolean</span> <span style="color: #eedd82">rowFound</span> = false;

  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">We use a separate iterator -- even though we could step through the
</span>  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">rows without it -- because we don't want to affect row currency.
</span>  <span style="color: #98fb98">RowSetIterator</span> <span style="color: #eedd82">deleteIter</span> = vo.createRowSetIterator(<span style="color: #ffa07a">&quot;deleteIter&quot;</span>);

  <span style="color: #00ffff">if</span> (fetchedRowCount &gt; 0)
  {
    deleteIter.setRangeStart(0);
    deleteIter.setRangeSize(fetchedRowCount);
    <span style="color: #00ffff">for</span> (<span style="color: #98fb98">int</span> <span style="color: #eedd82">i</span> = 0; i &lt; fetchedRowCount; i++)
    {
      row = (<span style="color: #98fb98">PoSimpleSummaryVORowImpl</span>)deleteIter.getRowAtRangeIndex(i);

      <span style="color: #ff7f24">// </span><span style="color: #ff7f24">For performance reasons, we generate ViewRowImpls for all
</span>      <span style="color: #ff7f24">// </span><span style="color: #ff7f24">View Objects. When we need to obtain an attribute value,
</span>      <span style="color: #ff7f24">// </span><span style="color: #ff7f24">we use the named accessors instead of a generic String lookup.
</span>
      <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Number primaryKey = (Number)row.getAttribute(&quot;HeaderId&quot;);
</span>      <span style="color: #98fb98">Number</span> <span style="color: #eedd82">primaryKey</span> = row.getHeaderId();

      <span style="color: #00ffff">if</span> (primaryKey.compareTo(poToDelete) == 0)
      {
        row.remove();
        rowFound = true;
        getTransaction().commit();
        <span style="color: #00ffff">break</span>; <span style="color: #ff7f24">// </span><span style="color: #ff7f24">only one possible selected row in this case
</span>      }
    }
  }

  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Always close iterators.
</span>  deleteIter.closeRowSetIterator();
  <span style="color: #00ffff">return</span> <span style="color: #00ffff">new</span> <span style="color: #98fb98">Boolean</span>(rowFound);
} <span style="color: #ff7f24">// </span><span style="color: #ff7f24">end delete()
</span></pre>

<p><strong>校验和级联删除</strong></p>

<p>row.remove()方法依次调用下层实体对象的remove()方法。为实现特殊的删除行为，比如，检查删除操作是否被允许，或实现级联删
除，应该在实体的remove()方法中添加代码，如下所描述的TooBox中的PurchaseOrderHeaderEOImpl。</p>

<p><strong>注意：</strong> 由于Oracle Applications编码规范禁止使用数据库的级联删除功能。BC4J
Framework需要我们手工为多层的purchase
order业务对象实现自己的级联删除，每个实体对象在在执行super.remove()之前，先删除它自己的直接子对象。如下所示：</p>

<pre class="src"><span style="color: #ffa07a">/**
 * Marks all the lines for deletion, then mark the header for deletion.
 * You can delete a purchase order only if it is &quot;In Process&quot; or &quot;Rejected.&quot;
 */</span>
<span style="color: #00ffff">public</span> <span style="color: #98fb98">void</span> <span style="color: #87cefa">remove</span>()
{
  <span style="color: #98fb98">String</span> <span style="color: #eedd82">status</span> = getStatusCode();

  <span style="color: #00ffff">if</span> ((<span style="color: #ffa07a">&quot;IN_PROCESS&quot;</span>Equals(status)) || (<span style="color: #ffa07a">&quot;REJECTED&quot;</span>Equals(status)))
  {

    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Note this is a good use of the header -&gt; lines association since we
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">want to call remove( ) on each line.
</span>    <span style="color: #98fb98">RowIterator</span> <span style="color: #eedd82">linesIterator</span> = getPurchaseOrderLineEO();

    <span style="color: #00ffff">if</span> (linesIterator != null)
    {
      <span style="color: #98fb98">PurchaseOrderLineEOImpl</span> <span style="color: #eedd82">line</span> = null;

      <span style="color: #00ffff">while</span> (linesIterator.hasNext())
      {
        line = (<span style="color: #98fb98">PurchaseOrderLineEOImpl</span>)linesIterator.next();
        line.remove();
      }
    }
    <span style="color: #00ffff">super</span>.remove(); <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Must be called last in this case.
</span>  }
  <span style="color: #00ffff">else</span>
  {
    <span style="color: #00ffff">throw</span> <span style="color: #00ffff">new</span> <span style="color: #98fb98">OARowValException</span>(<span style="color: #7fffd4">OARowValException</span>.TYP_ENTITY_OBJECT,
                                getEntityDef().getFullName(),
                                getPrimaryKey(),
                                <span style="color: #ffa07a">&quot;ICX&quot;</span>, <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message product short name
</span>                                <span style="color: #ffa07a">&quot;FWK_TBX_T_PO_NO_DELETE&quot;</span>); <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Message name
</span>  }
} <span style="color: #ff7f24">// </span><span style="color: #ff7f24">end remove()
</span></pre>


<h2><a name="sec16" id="sec16"></a>
锁</h2>

<p class="first">BC4J支持下面的锁技术：</p>

<ul><li>悲观锁 BC4J在执行setAttribute()方法时锁定实体对象对应的数据库行（在做出任何修改之前）。如果行已经被锁，BC4J将招聘一个AlreadyLockedException异常。这也是BC4J缺省的锁模式。</li><li>乐观锁 BC4J在执行数据库post处理逻辑时锁定实体对象对应的数据库行。如果行已经被锁，BC4J将抛出AlreadyLockedException异常。</li></ul>

<p><strong>注意：</strong> OA Framework默认使用乐观锁并且推荐使用，由于连接池使传统的悲观锁不能实行。但是对于基于Form的应用是使用悲观锁的。</p>

<p>如果你需要悲观锁，你必须改变事务的行为：</p>

<pre class="src"><span style="color: #ff7f24">// </span><span style="color: #ff7f24">In the application module...
</span>
<span style="color: #98fb98">OADBTransaction</span> <span style="color: #eedd82">txn</span> = getOADBTransaction();
txn.setLockingMode(<span style="color: #7fffd4">Transaction</span>.LOCK_PESSIMISTIC);
</pre>

<h3><a name="sec17" id="sec17"></a>
过期数据侦测</h3>

<p class="first">当BC4J锁定一行时，它试着决定行对象是否被其它用户删除或修改，因为它是为当前用户而查询的。</p>

<ul><li>如果行已经被删除了，BC4J抛出RowAlreadyDeletedException。</li><li>如果行已经被修改，BC4J抛出RowInconsistentException。</li></ul>

<p>为覆盖缺省的逐行比较的检测行为，可以在实体对象属性定义向导中使用属性级的Chage
Indicator标志。如果某个属性的这个标志被选中，BC4J限制对这个属性的比较。Oracle Application PL/SQL
API通常使用OBJECT_VERSION_NUMBER表列检查数据的改变，这列也可以影响实体对象。查看下面的Object Version
Number Column。</p>



<h2><a name="sec18" id="sec18"></a>
提交</h2>

<p class="first">当准备提交实体对象的修改时，只要简单的从应用模块中调用getTransaction()Commit()。当调用这个方法时，你的对象被校验（如果需要），posted和committed。</p>

<ol><li>commit()方法调用oracle.apps.fnd.framework.OADBTransaction.validate()方法。</li><li>validate()方法检查所有需要校验的根实体对象的&ldquo;Validation
Listener&ldquo;。（在多个实体组成的复合对象，只有根实体对象被添加到Validation
list）。校验完成后在commit前，它将不会存在于list中，因为当对象校验成功后，BC4J将会从validation list中移除它。</li></ol>

<p><strong>技巧：</strong> 也可以调用OADBTransaction.validate()方法在任何地方强制进行校验。它执行相同的功能。</p>

<ol><li>对象位于validation list中，OADBTransaction validate()方法将调用实体的final viladate()方法，现依次调用validateEntity()执行你的校验逻辑。</li></ol>

<p><strong>注意：</strong>
在BC4J中对于list中各个实体的校验顺序是随机的。但是，在一个复合对象中，比如一个采购单有多个供货商。BC4J总是在校验父对象前校验子对象。
BC4J只会将复合对象的根实体放入validation
list中（子对象不会被包含进来）。当根实体对象调用super.validateEntity时，BC4J调用它的子对象的validate，直到遍
历整个层级结构。由于这个原因，你应该在你的校验逻辑之前调用super.validateEntity以保证父对象在校验子对象后才校验自己。</p>

<ol><li>commit方法调用OADBTransaction postChanges方法。</li><li>postChanges方法检查&ldquo;Post Listener&rdquo;获得实体对象中哪些数据需要被提交（posted）到数据库。</li><li>对于post list中的任何对象，OADBTransaction postChanges方法调用实体的postChanges方法。当对象被提交（posted），BC4J将它从post list中移除。</li><li>如果没有错误发生，数据库commit被发出，任何数据库锁被释放。</li></ol>


<h2><a name="sec19" id="sec19"></a>
回滚</h2>

<p class="first">OA Framework为post和commit动作实现了一个&ldquo;all or nothing&ldquo;的事务处理方式。不管错误是否严重，如果数据库post或commit失败，OA Framework：</p>

<ul><li>发起JDBC rollback释放数据库锁。</li></ul>

<p><strong>注意：</strong> 这不会影响中间层的状态。</p>

<ul><li>恢复视图行对象的状态以便于事务发起第二次尝试。</li></ul>

<p><strong>注意：</strong> 这意味着你不需要显式的rollback失败的实体对象事务；OA Framework将在post或commit失败后自动显示出用户友好的错误信息。下面的例子描述了用户按下Apply按钮后commit和后来显示&ldquo;Confirmation&ldquo;对话框的情况。</p>

<pre class="src"><span style="color: #ff7f24">// </span><span style="color: #ff7f24">In the root application module
</span>
<span style="color: #00ffff">public</span> <span style="color: #98fb98">void</span> <span style="color: #87cefa">apply</span>()
{
  getTransaction()Commit();
}

<span style="color: #ff7f24">// </span><span style="color: #ff7f24">In the controller
</span><span style="color: #00ffff">public</span> <span style="color: #98fb98">void</span> <span style="color: #87cefa">processFormData</span>(<span style="color: #98fb98">OAPageContext</span> <span style="color: #eedd82">pageContext</span>, <span style="color: #98fb98">OAWebBean</span> <span style="color: #eedd82">webBean</span>)
{
  <span style="color: #00ffff">super</span>.processFormRequest(webBean);

  <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Handle the user pressing the &quot;Apply&quot; button
</span>  <span style="color: #00ffff">if</span> (pageContext.getParameter(<span style="color: #ffa07a">&quot;Apply&quot;</span>) != null)
  {
    <span style="color: #98fb98">OAApplicationModule</span> <span style="color: #eedd82">am</span> = pageContext.getRootApplicationModule();

    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">No need for any special exception handling.  You can just display the
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">confirmation message because the OAF won't reach this code if the post/commit
</span>    <span style="color: #ff7f24">// </span><span style="color: #ff7f24">fails.
</span>    am.invokeMethod(<span style="color: #ffa07a">&quot;apply&quot;</span>);
    <span style="color: #98fb98">OAException</span> <span style="color: #eedd82">confirmMessage</span> =
      <span style="color: #00ffff">new</span> <span style="color: #98fb98">OAException</span>(<span style="color: #ffa07a">&quot;ICX&quot;</span>, <span style="color: #ffa07a">&quot;FWK_TBX_T_SUPPLIER_CREATE_CONF&quot;</span>, null,
                       <span style="color: #7fffd4">OAException</span>.CONFIRMATION, null);
    pageContext.putDialogMessage(confirmMessage);

  }
}
</pre>

<h3><a name="sec20" id="sec20"></a>
回滚方法</h3>

<p class="first">手工清除中间层视图对象和实体对象缓冲，可以从应用模块中调用getTransaction().rollback
()。这也将roll back任何数据库修改并清除任何缓存于事务中的值。查找Support the Browser Back
Button了解这对于创建实体对象的作用。</p>

<p>如果执行PL/SQL过程需要显式的roll back数据库而不影响中间层，可以在应用模块中调用getTransaction().executeCommand(&quot;rollback&quot;)。</p>

<p><strong>注意：</strong> 如BC4J Native JDBC Statement Management中说过的。Transaction.rollback()会调用vo.clearCache()关闭相关的视图对象的JDBC结果集（游标）。比如，如果按下面的顺序执行：</p>

<pre class="src">vo.executeQuery();
Transaction.rollback();
vo.next();
</pre>
SQL异常&ldquo;ORA-01002: fetch out of
sequence&rdquo;（这通常是由于在数据库中执行rollback使打开的cursors失效）将不会发生，因为
Transaction.rollback()关闭了游标，强制vo.next()重新执行视图对象的查询并打开一个新的有效游标。
<p>当Transaction.rollback() roll back 数据库状态和中间层业务对象状态，下层直接JDBC调用并不会有意识的rollback任何中间层业务对象的状态，因此不要关闭JDBC游标。</p>

<blockquote>
<p class="quoted">i. Transaction.executeCommand(&quot;rollback&quot;)调用或</p>
</blockquote>

<blockquote>
<p class="quoted">ii. BC4J的&ldquo;rollback to
savepoint&rdquo;数据库调用是在Transaction.postChanges()或Transaction.commit()方法调用时
validation或post出错时，由内部发出的的。尽管实体对象或视图对象中用户修改的数据仍然存在，实体的post
state已经变回modified state以便用户可以再次发起post/commit。</p>
</blockquote>

<p>BC4J
Framework不会补偿JDBC或数据库的rollback，无效的JDBC和数据库游标中的结果集（当数据库执行rollback调用后，游标被打
开）。因此，如果你需要使用Transaction.executeCommand(&quot;rollback&quot;)，请先查看M52 model
coding standards。</p>

<p>如果需要覆盖post处理或EntityImpl中的beforeCommid，请先参考下节不当的Post处理。</p>


<h3><a name="sec21" id="sec21"></a>
不当的Post处理</h3>

<p class="first">在下面的情况下避免调用executeQuery()：</p>

<ul><li>在视图对象的EntityImpl的post处理器方法（postChanges，beforePost，afterPost）中。</li><li>在beforeCommit中并且随后试图在相同的视图对象中使用vo.next()，vo.first()等方法获取行。</li></ul>
          <br/>
          <span style="color:red;">
            <a href="http://jamsa.javaeye.com/blog/167872#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></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/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 04 Mar 2008 22:03:06 +0800</pubDate>
        <link>http://jamsa.javaeye.com/blog/167872</link>
        <guid>http://jamsa.javaeye.com/blog/167872</guid>
      </item>
      <item>
        <title>重新整理后的Oracle OAF学习笔记——6.错误处理</title>
        <author>Jamsa</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jamsa.javaeye.com">Jamsa</a>&nbsp;
          链接：<a href="http://jamsa.javaeye.com/blog/167871" style="color:red;">http://jamsa.javaeye.com/blog/167871</a>&nbsp;
          发表时间: 2008年03月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <h2>
异常类型</h2>

<p class="first">OA Framework处理三种基本类型的异常：通用、校验和严重。这些类型在这节中简单的描述；特殊异常的使用在下面介绍。</p>

<h3><a name="sec2" id="sec2"></a>
通用异常</h3>

<p class="first">BC4J框架中的错误是通过抛出类型为oracle.jbo.JBOException的隐式（runtime）异
常。OA
Framework中有自己的对应的版本为oracle.apps.fnd.framework.OAException。这个特殊化的版本提供了一种机
制，可以将多个异常捆绑在一起，并使用Oracle应用消息字典（Oracle Applications Message
Dictionary）翻译这些异常信息，以便显示出有用的信息。在任何代码中，通常可以抛出一个OAException类型的页面级别异常。</p>


<h3><a name="sec3" id="sec3"></a>
校验异常</h3>

<p class="first">校验异常是从实体对象和视图对象中抛出的，可以是由于属性级或行级的校验失败引起。</p>

<ul><li>oracle.apps.fnd.framework.OAAttrValException 特殊版本的OAException，用于属性级校验失败。</li><li>oracle.apps.fnd.framework.OARowValException 特殊版本的OAException，用于行（row）（entity）级校验失败。</li></ul>

<p>OA Framework使用下面的方式显示错误信息：</p>

<ul><li>属性级异常将在错误项目（item）和页面顶部标示出来</li><li>行级异常将在错误行（row）和页面顶部标示出来</li><li>页面级异常通常在页面顶部标示出来</li></ul>


<h3><a name="sec4" id="sec4"></a>
严重异常</h3>

<p class="first">严重（severe）（或称为&ldquo;毁灭（fatal）&rdquo;）性的异常包括不希望出现的系统级的错误（比如NullPointerException）和所选的JBOException如：NoDefExcpetion。可以直接在代码中抛出严重异常。</p>

<p>如果严重异常发生，用户将被定向到OAErrorPage（异常被渲染在页面的中间，页面是区域渲染的，页面显示了用户友好的错误信息，并包含了一个指向堆栈跟踪细节的链接）。</p>

<p><strong>注意：</strong> 这是一个未翻译过的信息客户可以在站点中修改。</p>


<h3><a name="sec5" id="sec5"></a>
Oracle工作流通知</h3>
          <br/>
          <span style="color:red;">
            <a href="http://jamsa.javaeye.com/blog/167871#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></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;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 04 Mar 2008 22:01:43 +0800</pubDate>
        <link>http://jamsa.javaeye.com/blog/167871</link>
        <guid>http://jamsa.javaeye.com/blog/167871</guid>
      </item>
      <item>
        <title>重新整理后的Oracle OAF学习笔记——5.应用构建基础之实现控制器</title>
        <author>Jamsa</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jamsa.javaeye.com">Jamsa</a>&nbsp;
          链接：<a href="http://jamsa.javaeye.com/blog/160974" style="color:red;">http://jamsa.javaeye.com/blog/160974</a>&nbsp;
          发表时间: 2008年02月01日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <h2>
设计一个OA Controller</h2>

<p class="first">如<a href="file:///E:/tmp/ebs_page.html">OA Framework Page解析</a>中所描述的，OA Controller定义了web beans的行为。具体来说，编写控制器代码的目的是：</p>

<ul><li>在运行时处理／初始化UI（包含那些通过编程方式添加的layout）</li><li>拦截或响应按钮按下之类的用户事件</li></ul>

<p>控制器不应该包含任何业务逻辑；这应该属于模型类。</p>

<h3><a name="sec2" id="sec2"></a>
必备知识</h3>

<p class="first">通常来说，在提出如何设计控制器之前，应该思考一下是否需要创建控制器。</p>

<p>作为一条规则，应该只在绝对必要的情况下才编写控制器。如果可以通过设计的方式创建页面，就不要通过编程的方式实现region和item。编程方式创建的web beans不能被个性化，重用或继承。而且，一些硬编码的layouts可能会丢失BLAF UI样式。</p>

<p>在<a href="file:///E:/tmp/ebs_build_view.html">实现视图</a>中说过，所有位于共同组件中的顶级regions必须与一个控制器关联。</p>


<h3><a name="sec3" id="sec3"></a>
粒度</h3>

<p class="first">OA Controllers可以与任何region关联（任何实现oracle.apps.fnd.framework.webui.beans.OAWebBeanContainer接口的web beans）；不能将控制器与items关联。</p>

<p>许多OA Framework新手都想知道控制器应该是&rdquo;多大&ldquo;。应该一个页面一个，或一个功能region一个（比如&ldquo;Search&rdquo; region），或一个复合web bean（比如一个table）一个，或者？答案是要看情况。</p>

<p>最初，在一个非常简单的页面，你可能不需要任何控制器（如果没有工作要作就不需要创建控制器）。如果需要编写代码，你需要根据下面的条件决定创建什么样的控制器：</p>

<ul><li>利于封装，一个web bean实现了它自己的行为</li><li>组件重用，如果组件被设置为重用，它必须是自包含，自已自足。</li><li>代码实用性，尽管页面包含了8个regions时可以很容易的添加8个控制器（每个包含少量的代码），这种&ldquo;纯&rdquo;OO的观念可以导致代码维护困难，可能导致产品代码文件膨胀。</li></ul>

<p>有一些方法可以帮助决定如何处理：</p>

<ul><li>永远不要从child bean中设置parent/grandparent web bean的属性。</li><li>为相关联的region定义控制器来设置region和它的子孙region的属性。如果需要主控制器管理多个子／孙web bean，控制器应该与适当的父／祖父bean相关联。</li><li>对于复杂的beans（比如OATableBean）应该将控制器关联到bean自身，或关联到一个简单的容器bean中（如果它实现功能逻辑单元）。</li></ul>

<p>通常，应该为页面创建少于满足上面规则和考虑数量的控制器。对于非常简单的页面，通常是为pageLayout区域关联单个的控制器。对于更复杂的
页面，应该为各个功能组件（比如，查询页面中典型的&ldquo;Search&rdquo;区域控制器和&ldquo;Results&rdquo;区域控制器）创建少量不同的控制器。共享区域应该拥有
自己的适当的控制器。</p>


<h3><a name="sec4" id="sec4"></a>
模型性／重用</h3>

<p class="first">在同一组相关联的页面中，你有时将找到可以重用代码的机会。下面是创建模块性更强的控制器代码的方法：</p>

<ul><li>在控制器中添加私自己的私有方法。</li><li>创建一个公用的控制器类（它是oracle.apps.fnd.framework.webui.OAControllerImpl），然后为有需要的页面／区域继承这个类。</li><li>创建辅助的实用工具类，控制器中可以根据需要代理。这些类不需要实现任何OA Framework类或接口，应该被包含在与它们所辅助的控制器类所在的包中。注意，静态方法适合于在这些类中使用，当使用静态方法时，应该考虑下面的问题：

<ul><li>不能影响子类中的静态方法。</li><li>封装相关常量和静态方法。（There are packaging implications related to the use of constants and static methods ）</li></ul></li></ul>


<h3><a name="sec5" id="sec5"></a>
线程安全</h3>

<p class="first">OA Framework被设计为支持多线程web bean访问（尽管还没有实现）。大部分对于你的代码来说是透明的，只有少量规则必须在控制器代码中遵守：</p>

<ul><li>如果在控制器或辅助类中使用静态方法，则永远不要包含状态。</li><li>总是将页面的OAPageContext传递给任何web
bean存储器（如果需要可以带OAPageContext）。比如，使用setText(OAPageContext pageContext,
String text)代替setText(String text)。</li></ul>


<h3><a name="sec6" id="sec6"></a>
状态管理</h3>

<p class="first">不要在控制器中或你实例化的辅助类中添加非易失性成员变量。OA Framework不会钝化成员变量，因此一旦虚拟机失效被支持后将不能恢复这些值。可以添加static final成员变量。</p>


<h3><a name="sec7" id="sec7"></a>
编码规则</h3>



<h2><a name="sec8" id="sec8"></a>
创建一个控制器</h2>

<p class="first">为一个区域创建控制器：</p>

<ol><li>在JDeveloper Structure页面中选择区域</li><li>右键选择Set New Controller...</li><li>在New
Controller对话框中，输入包和类名。选择OK创建与选择区域关联的控制器。注意Inspector中的Controller
Class属性值是类的全名，如：
oracle.apps.fnd.framework.toolbox.tutorial.webui.HomeSearchCO。</li></ol>

<p>JDeveloper将创建控制器模板。</p>

<pre class="src"><span style="color: #ff7f24">/*</span><span style="color: #ff7f24">===========================================================================+
 | Copyright (c) 2001, 2003 Oracle Corporation, Redwood Shores, CA, USA      |
 | All rights reserved.                                                      |
 +===========================================================================+
 | HISTORY                                                                   |
 +===========================================================================*/</span>
<span style="color: #00ffff">package</span> oracle.apps.fnd.framework.toolbox.tutorial.<span style="color: #7fffd4">webui</span>;

<span style="color: #00ffff">import</span> <span style="color: #7fffd4">oracle</span>.<span style="color: #7fffd4">apps</span>.<span style="color: #7fffd4">fnd</span>.<span style="color: #7fffd4">common</span>.<span style="color: #98fb98">VersionInfo</span>;
<span style="color: #00ffff">import</span> <span style="color: #7fffd4">oracle</span>.<span style="color: #7fffd4">apps</span>.<span style="color: #7fffd4">fnd</span>.<span style="color: #7fffd4">framework</span>.<span style="color: #7fffd4">webui</span>.<span style="color: #98fb98">OAControllerImpl</span>;
<span style="color: #00ffff">import</span> <span style="color: #7fffd4">oracle</span>.<span style="color: #7fffd4">apps</span>.<span style="color: #7fffd4">fnd</span>.<span style="color: #7fffd4">framework</span>.<span style="color: #7fffd4">webui</span>.<span style="color: #98fb98">OAPageContext</span>;
<span style="color: #00ffff">import</span> <span style="color: #7fffd4">oracle</span>.<span style="color: #7fffd4">apps</span>.<span style="color: #7fffd4">fnd</span>.<span style="color: #7fffd4">framework</span>.<span style="color: #7fffd4">webui</span>.<span style="color: #7fffd4">beans</span>.<span style="color: #98fb98">OAWebBean</span>;

<span style="color: #ffa07a">/**
  * Controller for ...
  */</span>
<span style="color: #00ffff">public</span> <span style="color: #00ffff">class</span> <span style="color: #98fb98">OrderSummaryCO</span> <span style="color: #00ffff">extends</span> <span style="color: #98fb98">OAControllerImpl</span>
{
   <span style="color: #00ffff">public</span> <span style="color: #00ffff">static</span> <span style="color: #00ffff">final</span> <span style="color: #98fb98">String</span> <span style="color: #eedd82">RCS_ID</span>=<span style="color: #ffa07a">&quot;$Header$&quot;</span>;
   <span style="color: #00ffff">public</span> <span style="color: #00ffff">static</span> <span style="color: #00ffff">final</span> <span style="color: #98fb98">boolean</span> <span style="color: #eedd82">RCS_ID_RECORDED</span> =
   VersionInfo.recordClassVersion(RCS_ID, <span style="color: #ffa07a">&quot;%packagename%&quot;</span>);

 <span style="color: #ffa07a">/**
   * Layout and page setup logic for a region.
   * </span><span style="color: #7fffd4">@param</span><span style="color: #ffa07a"> pageContext the current OA page context
   * </span><span style="color: #7fffd4">@param</span><span style="color: #ffa07a"> webBean the web bean corresponding to the region
   */</span>
   <span style="color: #00ffff">public</span> <span style="color: #98fb98">void</span> <span style="color: #87cefa">processRequest</span>(<span style="color: #98fb98">OAPageContext</span> <span style="color: #eedd82">pageContext</span>, <span style="color: #98fb98">OAWebBean</span> <span style="color: #eedd82">webBean</span>)
   {
     <span style="color: #00ffff">super</span>.processRequest(pageContext, webBean);
   }

 <span style="color: #ffa07a">/**
   * Procedure to handle form submissions for form elements in
   * a region.
   * </span><span style="color: #7fffd4">@param</span><span style="color: #ffa07a"> pageContext the current OA page context
   * </span><span style="color: #7fffd4">@param</span><span style="color: #ffa07a"> webBean the web bean corresponding to the region
   */</span>
   <span style="color: #00ffff">public</span> <span style="color: #98fb98">void</span> <span style="color: #87cefa">processFormRequest</span>(<span style="color: #98fb98">OAPageContext</span> <span style="color: #eedd82">pageContext</span>, <span style="color: #98fb98">OAWebBean</span> <span style="color: #eedd82">webBean</span>)
   {
     <span style="color: #00ffff">super</span>.processFormRequest(pageContext, webBean);
   }

}
</pre>

<p><strong>注意：</strong> 缺省的模板内容不包含processFormData(OAPageContext pageContext, OAWebBean webBean)方法，这个方法在POST处理的第一个阶段被调用。如果需要（非常少见），可以将它加到控制器中。</p>

<p><strong>注意：</strong> 也可以通过编程的方式将控制器与区域关联。查看OAWebBeanContainer中的setControllerClass(String javaClass)方法。</p>


<h2><a name="sec9" id="sec9"></a>
处理HTTP GET</h2>

<p class="first">在GET处理过程中，每个控制器的processRequest(OAPageContext
pageContext, OAWebBean webBean)方法被按照它们被实例化时的层级结构而依次被调用。处理从pageLayout
bean开始，然后递归处理整个层级结构。初始化页面&mdash;&mdash;或影响层级结构中的web bean（通过设置属性，创建web
bean等等）&mdash;&mdash;属于processRequest()方法。</p>

<p><strong>注意：</strong> 传递到processRequest()方法中的oracle.apps.fnd.framework.webui.OAWebBean参数是与当前控制器关联的区域。</p>

<p>下面是一个典型的processRequet的代码。它描绘的是根据从&ldquo;search&rdquo;页面传递过来的参数，初始化用于查看的&ldquo;detail&rdquo;页面。</p>

<pre class="src"><span style="color: #ffa07a">/**
 * Layout and page setup logic for region.
 * </span><span style="color: #7fffd4">@param</span><span style="color: #ffa07a"> pageContext the current OA page context
 * </span><span style="color: #7fffd4">@param</span><span style="color: #ffa07a"> webBean the web bean corresponding to the region
 */</span>
<span style="color: #00ffff">public</span> <span style="color: #98fb98">void</span> <span style="color: #87cefa">processRequest</span>(<span style="color: #98fb98">OAPageContext</span> <span style="color: #eedd82">pageContext</span>, <span style="color: #98fb98">OAWebBean</span> <span style="color: #eedd82">webBean</span>)
{
   <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Always call this before adding your own code.
</span>   <span style="color: #00ffff">super</span>.processRequest(pageContext, webBean);

   <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Get the purchase order number from the request.
</span>   <span style="color: #98fb98">String</span> <span style="color: #eedd82">orderNumber</span> = pageContext.getParameter(<span style="color: #ffa07a">&quot;headerId&quot;</span>);

   <span style="color: #ff7f24">// </span><span style="color: #ff7f24">We need to set the page header text to include the PO order number for reference.
</span>   <span style="color: #98fb98">MessageToken</span>[] <span style="color: #eedd82">tokens</span> = { <span style="color: #00ffff">new</span> <span style="color: #98fb98">MessageToken</span>(<span style="color: #ffa07a">&quot;PO_NUMBER&quot;</span>, orderNumber) };

   <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Always use a translated value from Message Dictionary when setting strings in
</span>   <span style="color: #ff7f24">// </span><span style="color: #ff7f24">your controllers.
</span>   <span style="color: #98fb98">String</span> <span style="color: #eedd82">pageHeaderText</span> = pageContext.getMessage(<span style="color: #ffa07a">&quot;ICX&quot;</span>, <span style="color: #ffa07a">&quot;FWK_TBX_T_PO_HEADER_TEXT&quot;</span>, tokens);

   <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Set the po-specific page title (which also appears in the breadcrumbs. Since this
</span>   <span style="color: #ff7f24">// </span><span style="color: #ff7f24">controller is associated with the page layout region, simply cast the webBean
</span>   <span style="color: #ff7f24">// </span><span style="color: #ff7f24">parameter to the right type and set the title.
</span>
   ((<span style="color: #98fb98">OAPageLayoutBean</span>)webBean).setTitle(pageHeaderText);

   <span style="color: #ff7f24">// </span><span style="color: #ff7f24">Now we want to initialize the query for our single purchase order with all of its
</span>   <span style="color: #ff7f24">// </span><span style="color: #ff7f24">details.
</span>   <span style="color: #98fb98">OAApplicationModule</span> <span style="color: #eedd82">am</span> = pageContext.getApplicationModule(webBean);
   <span style="color: #98fb98">Serializable</span>[] <span style="color: #eedd82">parameters</span> = { orderNumber };
   am.invokeMethod(<span style="color: #ffa07a">&quot;initDetails&quot;</span>, parameters);

} <span style="color: #ff7f24">// </span><span style="color: #ff7f24">end processRequest()
</span></pre>
在调用super.processRequest(pageContxt,
webBean)后，范例中的代码从request参数中获取名为&ldquo;headerId（从Search页面传递过来的参数）&rdquo;。这个值被显示在页面标题和
breadcrumbs上，并且它被传递给模型以便查询。
<p>使用页面的title值定义页面标题和breadcrumbs：</p>

<p class="image"><img src="file:///E:/tmp/ebs_build_control/page_title.gif" alt="" /></p>

<p>由于显示于页面中的值必须被翻译，我们在Oracle应用消息字典（Oracle Application Message
Dictionary）中创建了一个名为FWK_TBX_T_PO_HEADER_TEXT的消息，消息内容为&ldquo;Purchase Order:
&amp;PO_NUMBER&rdquo;。这个代码定义了以令牌PO_NUMBER作为Purchase Order
Number的占位符，然后从oracle.apps.fnd.framework.webui.OAPageContext（它将操作委派给
AOL/J）中提取翻译后的版本。然后将翻译后的字符串作为页面标题。</p>

<p><strong>警告：</strong> 不要在用户界面中使用硬编码的文本值。所有以编程方式显示的文本值必须来源于消息字典（Message Dictionary）。也可以在设计时在web bean中使用这种方式（所有显示的bean属性都是被翻译的），或者也可以从多国语言表中查询出值来显示。</p>

<p>最后，这个只读的&rdquo;details&ldquo;页面自动按给定的编号进行查询而不管它是否会被渲染。它通过将编号传递给页面根应用模块的initDetails()方法。然后应用模块将参数传递给适当的视图对象，在那里将参数与WHERE子句绑定并执行查询。</p>

<h3><a name="sec10" id="sec10"></a>
修改Bean属性</h3>

<p><strong>注意：</strong> 作为规则来说，更好的修改web bean属性的方法是使用局部页面渲染（partial page
rendering （PPR））和SPEL，在Dynamic User
Interface中有描述。在不能通过PPR和SPEL的环境下，也必须在processRequest()方法中修改web
bean层级结构（这节被包含在GET处理一章中，也是由于只能在processRequest()方法中才允许修改web bean层级结构）。</p>

<p>如果需要以编程方式在响应表单提交的事件中修改层级结构，必须forward到同一个页面的processRequest()方法（见下面POST事件处理）。作出这个限制的原因有：</p>

<ul><li>确保web bean层级结构能在需要的时候被正确的重建。</li><li>Beans被适当的初始化。主要是Rendered属性，或影响复杂组件渲染的prepareForRendering()方法。</li><li>Bean层级结构被放在同一个方法中维护。</li></ul>

<p>修改web bean的属性时，只需要简单的根据它的名称（在JDeveloper中赋给它的ID）查找到正确的bean，然后按下面的方法调用适当的方法。</p>

<p><strong>警告：</strong> 当获得web bean时，在调用它的任何方法前都需要检查对象是否为空。即使你认为bean被包含于web bean层级结构中，但也有可能在用户使用个性化定制时半它隐藏了。</p>

<pre class="src">processRequest(OAPageContext pageContext, OAWebBean webBean)
{
  // Always call this before adding your own code.
  super.processRequest(pageContext, webBean);

  OATableBean table = (OATableBean)webBean.findIndexedChildRecursive(<span style="color: #ffa07a">&quot;OrdersTable&quot;</span>);

  if (table == null)
  {
     MessageToken[] tokens = { new MessageToken(<span style="color: #ffa07a">&quot;OBJECT_NAME&quot;</span>, <span style="color: #ffa07a">&quot;OrdersTable&quot;</span>)};
     throw new OAException(<span style="color: #ffa07a">&quot;ICX&quot;</span>, <span style="color: #ffa07a">&quot;FWK_TBX_OBJECT_NOT_FOUND&quot;</span>, tokens);
  }

  // Set the purchase-order specific <span style="color: #ffa07a">&quot;control bar&quot;</span> select text:
  // <span style="color: #ffa07a">&quot;Select Purchase Order(s) and...&quot;</span>

  String selectPOText = pageContext.getMessage(<span style="color: #ffa07a">&quot;ICX&quot;</span>, <span style="color: #ffa07a">&quot;FWK_TBX_T_SELECT_PO&quot;</span>, null);
  table.setTableSelectionText(selectPOText);

}
</pre>
使用findIndexedChildRecursive(String name)方法可以在整个web
bean层级结构中查找到第一个与名称匹配的被索引的子对象。如果如果要修改的web
bean是一个被命名的UIX子对象（或，如果你不确定它是否&ldquo;被命名（named）&ldquo;或&rdquo;被索引（indexed）&ldquo;），则使用
findChildRecursive(String name)方法。
<p>如果需要修改控制器区域的属性，只需要将processRequest()的OAWebBean参数转换为正确的类型并调用需要的方法。</p>


<h3><a name="sec11" id="sec11"></a>
编程的方式创建Bean</h3>

<p><strong>注意：</strong> 本节包含于GET处理部分，因为只允许在processRequest()方法中修改web bean层级结构。</p>

<p>如果需要在响应表单提交事件中添加web bean到层级结构中，必须forward到同一个页面的processRequest()代码中执行。</p>

<p>作为规则，如果你可以通过设计的方式产生web bean就不应该通过编程的方式产生web beans。另外，如果你的页面与局部页面渲染相关，则也不能在运行时修改web bean层级结构。</p>

<p>对于那些极少见的必须手工实例化web
bean的情况，则使用OAControllerImpl类中的createWebBean()工厂方法。不要直接使用web
bean的构造器，不必担心要直接创建oracle.apps.fnd.framework.webui.OAWebBeanFactory，因为控制器
的createWebBean()方法代理了OAWebBeanFactory。</p>

<p><strong>注意：</strong>
对于这些手工创建的beans，使用工厂方法时可以指定bean的&ldquo;name&rdquo;（JDeveloper中的ID属性）。避免使用deprecated的方
法，它允许你在创建web bean时不指定name。web
bean的名称（name）在同一个页面中必须是一个唯一标识。另外，bean的名称可能被OA
Framework用于BC4J对象实例名（比如应用模块实例），因此不应该包含Java命名中规定的无效字符。</p>

<p>比如，下面的代码描述了如何创建两个web bean并将它们添加到父区域中。</p>

<pre class="src"><span style="color: #98fb98">OATableLayoutBean</span> <span style="color: #eedd82">tableLayout</span> = (<span style="color: #98fb98">OATableLayoutBean</span>)findIndexedChildRecursive(<span style="color: #ffa07