本文是由英文帮助翻译所得:
1>task flows
“任务流 task flows”可以包括非可视化的组件,比如方法调用。
“页片段 page fragment”可以运行在一个页面的某个局部区域,最大限度地提高复用性。
ADF Task Flow是在JSF Controller的基础上扩展而来的,它除了包括View Activities和导航规则之外,还可以包括method calls等非可视化Activity。
ADF Task Flow分为两种:Bounded task flow和Unbounded task flow
1、Bounded task flow
(1)、作为局部、分支的页面流程。
(2)、有一个唯一入口和零到多个出口。
(3)、拥有自己私有的Control flow rules,Activities,Managed bean,Transactions.
(4)、可以重用。
2、Unbounded task flow
(1)、作为顶级的页面流程。
(2)、出口不固定。
ADF ManagedBean与JSF Managed Bean的区别在于,ADF Managed Bean的Scope比JSF Managed Bean多了pageFlowScope、viewScope、backingBeanScope。
. pageFlowScope:Managed Bean在Task Flow中的所有Page可见,且访问的同一个实例。如果其它Task flow中 Page访问该Managed Bean,将会创建一个新实例,供这个Task flow中的所有Page使用。
. viewScope:只在当前这个view(可能是root browser window,也可能是region)可见,当viewId发生改变后,该Managed Bean被释放掉。
. backingBeanScope:一个Page中含有一个Task Flow,并且这个Task flow使用了多个region,设置此值可以起到隔离各个region的作用(会在每个region中创建一个实例),避免数据冲突。
2>布局
为了使组件能够自适应浏览器窗口大小,首先需要放置根一级的可伸缩的布局组件。 以下组件可以根据父容器的大小自动伸缩(加星号的还可以伸缩其子组件):
. * Decorative Box
. Panel Accordion
. Panel Box (当type="stretch"或"default")
. Panel Collection
. Panel Dashboard
. Panel Group Layout (当layout="scroll"或"vertical")
. * Panel Spliter
. * Panel Stretch Layout
. Panel Tabbed
对于这些组件,不要设置其宽度、高度的百分比,设置了反而不好。
以下组件不能根据父容器的大小自动伸缩:
. Panel Border Layout
. Panel Box (当type="flow"或"vertical")
. Panel Form Layout
. Panel Group Layout (当layout="default"或"horizontal")
. Panel Header
. Panel Label and Message
. Panel List
如果你必须使用一个不能自动伸缩的容器,但又想达到自动伸缩的效果,可以为该容器套上一个可以自动伸缩的容器,如Panel Group Layout(当layout="scroll"或"vertical")。
最佳实践:(1)、布局时,根容器要可以自动伸缩。(2)、无法伸缩的容器,可以考虑外套一个Panel Group Layout(当layout="scroll"或"vertical")。
3>eo的自定义属性
为Entity Object增加Transient Attribute,比如自动计算订单总价:单价*数量。
(1)、因为是Transient Attribute,所以不要选择Persistent。
(2)、在View Object中增加该Attribute。
(3)、设置重新计算的条件:Always--每次都重新计算;Never--只在创建时计算一次;Based on the following expression--根据表达式返回值(True or false)来计算是否重新计算。
为Entity Object增加Association,比如订单项目中的产品与产品关系。
(1)、Association命名规则是<DetailEntityMasterEntity>FKAssoc。
(2)、搞清楚一对多的关系中,谁是一、谁是多。
4>vo添加View Link
为View Object增加View Link,比如订单项目中的产品与产品关系。
View Object是对Entity Object的查询结果,结果包括Entity Object的全部或部分属性。一般来说,一个View Object对应一个Entity Object。当然,你也可以完全根据SQL语句自定义一个View Object。View Link表明的是两个View Object之间的关系,一个View Link对应一个Association。
(1)、View Link命名规则也是<DetailEntityMasterEntity>FkLink。
(2)、同样,也要搞清楚一对多的关系中,谁是一、谁是多。
5>am添加Data Model
为Application Module增加Data Model。
View Object表明数据被访问的方式。客户端通过访问View Object而访问Data Model。Data Model保存在Application Module中。Application Module是一种类型的业务服务,其它类型的业务服务有Web Service、EJB。在Data Model中,View Object所带的数字“1”、“2”、“3”....,表明这是该View Object的第几个实例。
6>LOV
ADF BC特色功能之一:级联式下拉列表
在实际应用中,经常会使用两个级联式下拉列表的情况,比如先选择国家列表,在根据选择的国家显示 城市列表。你可以把View Object某个Attribute定义成LoVs(全称:List of Values)。这样,由于是定义在模型层上,UI界面无需做任何改动。
7>ADF BC特色功能之二:强大的自动计算与验证功能
1、通过新建Transient Attribute来实现自动计算。
2、验 证Foreign Keys:Key Exists。LOVs可以限制用户只能从已有的外键中选择,但有时界面要求使用文本输入框,而不是下拉列表,这时候就需要使用Key Exists来验证用户输入的外键是否正确。并且,Key Exists可以让我们在程序中使用该功能,不通过界面。LOVs是定义在View Object上的,而Key Exists是定义在Entity Object上的。
3、约束性条件依赖:Compare。某个Attribute的值与设定值进行比较,为真则满足要求,否则报错。其中可以设定触发条件和触发Attributes。
4、使用Groovy脚本验证。如果脚本中有对象为null,Groovy不会抛出NullPointerException,而是表达式结果为null,null对应的Boolean值为false。
8>使用ADF Faces之一:开发用户界面
如何使用其它Project的资源:共享资源如servlet classes,helper classes,images和templatess,可以通过创建ADF library来为其它Project所共用。
9>使用ADF Task Flows之一:菜单与页面导航
一个应用的页面之间的上下级关系就像一棵树。每个节点都是一个页面。
一个典 型的ADF应用由一个或多个unbounded task flow和bounded task flows组成。创建unbounded task flow时不要选择“Create as Bounded Task Flow”,并且以adfc-xxx-config.xml的格式来命名,比如adfc-sale-config.xml。adfc- config.xml是应用默认的unbounded task flow,它应该作为应用的总入口、根节点。
10>客制化:个性化的一种服务(customize(v),customization(n))
表示根据客户的需求进行特别的定制,已满足其需要。一般用于软件/服务/加工制造等方面。针对顾客的需求,对一个标准的产品进行改变、用新的部件替换标准的部件、或是在一个标准产品中加入特殊的功能,提供顾客一个更完整的产品组合。
个性化:就是非一般大众化的东西。在大众化的基础上增加独特、另类、拥有自己特质的需要,独具一格,别开生面,打造一种与众不同的效果。
问题:在ADF中如何定制ADF应用?
根据行业的不同,客户的不同,需要对ADF应用做一些定制化操作,ADF应用的定制化分为两种:
(1)、个性化:允许用户运行时对应用进行定制。
(2)、客制化:允许用户设计时对应用进行定制。
无论是那种定制方法,这些定制的内容均不会对已开发完成的应用作出修改,而是存储在MDS(Metadata Service repository)中。MDS支持两种方式的实现:文件和数据库。ADF应用默认使用基于文件的MDS。
基于文件的MDS:选择Application -> Application Properties -> RUN -> MDS。Directory Content:你可以设置是否在每次执行应用前清空MDS存储的定制化信息。
如何实现用户客制化(Customize)
客制化是在设计时,在原有ADF应用的基础上,增加定制化层,每一层可能对应不同的行业、不同的公司。这样做的好处就是,既能满足不同风格的展现要求,同时又不改变原有应用的基础代码。
(1)、一个ADF应用允许设置多个客制化层,如industry层及site层。
(2)、每一个客制化层允许具有多个客制化值,如industry层可以具有healthcare和financial等。
(3)、运行时,每一个层只有一个客制化层值有效。
(4)、客制化层的顺序由adfc-config.xml中各个客制化类的顺序决定。
实现客制化的具体步骤如下:
one、定义CustomizationLayerValues.xml。
打开文件C:\Oracle\Middleware\jdeveloper\jdev\CustomizationLayerValues.xml,该文件定义了所有客制化层以及每层的值。
two、创建customization.properties文件
在Model Project中,新建一个文件customization.properties。该文件表明运行时,每个客制化层设定为哪个值。
three、创建和发布客制化类
MDS使用客制化类来决定应用哪个客制化层,一个客制化类对应一个客制化层,其接口如下:
. CacheHint getCacheHint();
决定customization的类型,返回值包括ALL_USERS、MULTI_USER、REQUEST、USER四种。
ALL_USERS:customization为针对某应用全局有效的,通常用于static类型的customization层。
MUTI_USER:针对复数用户有效的customization。
REQUEST:针对当前请求有效的customization。
USER:针对某特定用户有效的customization,通过用户访问应用的Session来决定具体用户。
. String getName();
返回当前customization类对应customization层的名称。
. String generateIDPrefix(RestrictedSession sess, MetadataObject mo);
返回在MDS中对应当前customization层元素加的前缀,以使该客制化层的元素在MDS中具有唯一标示。这一前缀在所有customization层中必须是唯一的,出于性 能考虑应小于4个字符。
. String[] getValue(RestrictedSession sess, MetadataObject mo);
返回反映客户化层加载顺序的列表,按照列表顺序加载。通常只需要有一个customization层,故该列表通常只返回一个值。
在Model Project中,新建一个Java类:SiteCCTV.java,内容自己再去看。写好类后,编译Model Project,发布Model Project为一个Jar文件。这里需要注意三 点:
(1)、选中Include Manifest File。
(2)、只选中定制化类和customization.properties文件。
(3)、必须把该Jar文件Copy到C:\Oracle\Middleware\jdeveloper\jdev\lib\patches目录下。
four、修改adf-config.xml文件
选择Application Resource -> Descriptors -> ADF META_INF,双击adf-config.xml打开overview editor。增加前面新建的定制化类。
five、为ViewController Project选中Enable Seeded Customizations。
six、创建基础应用:TaskFlows,JSF Pages等等。
seven、 在基础应用上客制化应用。这时,要以Customization Developer角色重新进入JDeveloper。方法是Tools -> Preferences -> Roles,选择Customization Developer。重启JDeveloper后,应该能看到之前做的设置。
11>开发端到端的ADF应用之一:开发富互联网应用
1、创建Read-Only Vo:创建此种Vo一般作为下拉列表选项出现在UI中。
2、 为EO的字段定义验证规则:除了可以为EO的字段定义验证规则(Attributes Level Validator)之外,你还可以为整个EO定义验证规则(Entity Level Validator)。Attributes Level Validator在attribute值变时触发;Entity Level Validator在验证Entity时触发。如果想要自定义Java验证规则,则Rule Type选择Method方式,会自动帮你生成相应的方法,Java验证规则也可以定义在Attributes或Entity上。
3、在EO的字段设置默认值
当前日期,adf.currentDate,显示格式。当前时间,adf.currentDateTime。注意,必须选择Expression方式。
4、创建一对多的UI界面(部门与员工是一对多的关系)
注意,拖放EmployeesVo时,要选择DepartmentsVo下的EmployeesVo,这样才能自动关联一对多的关系。
5、创建VO(Updatable access throuth entity objects)
(1)、创建VO的方式除了从Tables的方式创建以外,也可以通过抽取多个EO的属性来创建。
(2)、为VO增加自动计算的Transient属性,如SumSalary=Salary*12。
(3)、为EmpDetails VO增加View Accessors:Job VO,供以下拉列表的方式选择Job。
注意,手工创建的VO必须加入Application Model的Data Model中才能使用。
6、创建VO(Read-only access through SQL query)
可以用自定义SQL query的方式来创建Read-Only VO,可以带查询参数,参数直接定义在查询语句中,如select first_name from employees where email=:p_email。这种方式定义的VO,生成Data Control时,会在该VO的Operations下生成对应的ExecuteWithParams。
12>开发端到端的ADF应用之二:使用EJB、JPA、JSF开发Web应用
1、使用EJB3和JPA建立业务服务层。
(1)从数据库表创建JPA Entities,生成相应的java文件,主要包括Entities的属性和CRUD方法。以后可以根据需要,手工添加一些方法。
(2) 创建EJB Session Bean,选择Persistence Type=JPA,选择暴露哪些Entities上的方法。Entity如果增加了新的方法想要通过Session Bean调用,右键点击EJB Session Bean,选择Edit Session Facade,把新方法加进来即可。
(3)测试Session Bean非常方便,右键点击EJB Session Bean,选择New Sample Java Client,就会帮你生成测试代码,你只需要传一些必要的参数即可。测试前 ,先要运行Session Bean(把Session Bean发布到WLS上,并启动),然后右键点击Java Client,选择Run。
(4)JPA Entities也可以不通过Java EE Container——直接在Java SE Container中就可以调用。你需要创建一个新的persistence unit,右键点击 persistence.xml选择New Java Service Facade,选择Persistence Type=JPA,选择暴露哪些Entities上的方法。在main函数中,可以增加你的测试代码,并 直接运行——不需要先运行Session Bean。
2、为Session Bean创建Data Control。
右键点击EJB Session Bean,选择Create Data Control,选择暴露的Session Bean的Interface:Local或Remote。
3、如何让一个普通Project可以使用JSF/ADF组件?
(1) 右键点击Project,选择Project Properties,选择JSP Tag Libraries,选择Select Distributed libraries,增加ADF Faces Components 11,(只选这个就 可以,其它的会带过来)。
(2)继续选择Technology Scope,选择JSF(只选这个就可以,其它的会带过来)。
13>如何显示提示信息
1、显示在某个组件的旁边
要想显示在组件的旁边,首先要得到这个组件的ID,然后就是构造FacesMessage,并显示出来。
(1)组件已经绑定在MB中
FacesContext context = FacesContext.getCurrentInstance();
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR,"Please supply a valid file name to upload",null);
context.addMessage(this.getFileInputComponent().getId(),message);
(2)通过组件上的事件获取组件
FacesContext context = FacesContext.getCurrentInstance();
FacesMessage messagge = new FacesMessage("Successfully uploaded file"+file.getFilename()+"("+file.getLength()+"bytes)");
context.addMessage(event.getComponent().getClientId(context),message);
(3)获取根组件
public static void addFacesErrorMessage(String attrName, String msg){
FacesContext ctx = FacesContext.getCurrentInstance();
FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, attrName, msg);
ctx.addMessage(JSFUtils.getRootViewComponentId(), fm);
}
(4)通过根组件+组件ID查找组件
pubic static void addFacesErrorMessage(String attrName, String msg){
FacesContext ctx = FacesContext.getCurrentInstance();
FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, attrName, msg);
ctx.addMessage(JSFUtils.getRootViewComponentId().findComponent("productpriceIT").getClientId(ctx), fm);
}
14>页面之间传值的几种方法:
两个页面,第一个页面点击某个command component,传一个参数到第二个页面。
方法一:选中task flow中的某个View Activity,设置属性Input Page Parameters,from, to。设置到pageFlowScope中。
方法二:在按钮上设置set action listener事件,导航前该listener被触发。使用set action listener设置from,to,将当前页面的某个值设置到pageFlowScope中。
方法三:在按钮上设置set property listener事件,设置from, to, type。type=action,表明监听该command component的action event。
说明:建议使用set property listener。
两个页面,第二个页面接收第一个页面传过来的参数值
Map pageFlowScope = RequestContext.getCurrentInstance().getPageFlowScope();
Object myObject = pageFlowScope.get("myObjectName");
RequestContext adfContext = RequestContext.getCurrentInstance();
adfContext.getPageFlowScope().clear();
15>commit操作
因为Commit操作往往需要保存到数据库,操作相对费时,因此最好不要让用户随便点击。需要Enable时才Enable,其判断依据就是表单数据是否已经更改。
实际应用中,在做其它操作时,我们也需要判断数据是否已经更改,比如:用户修改了某个表单,没有提交,然后直接转到其它页,这时我们应该提醒它数据已经修改,是否保存?也就是说,我们需要在MB中判断bindings.Commit.enabled的值,方法如下:
public boolean isCommitEnabled(){
Boolean commitState = (Boolean)JSFUtils.getManagedBeanValue("bindings.Commit.enabled");
boolean commitEnabled = commitState != null ? commitState.booleanValue() : false;
return commitEnabled;
}
判断数据是否已经更改的一种更底层方法(因为是在AM上判断的),只要AM上的VO发生了改变,都可以用这个方法监测到。方法如下:
public boolean isDirty(){
ApplicationModule am = ADFUtils.getDCBindingContainer().getDataControl().getApplicationModule();
return am.getTransaction().isDirty();
}
在使用ADF Commit按钮时,还有一个常见问题:当用户修改某一个表单项后,即使焦点转移后,Commit按钮也不会被Enable。如果出现这种情况,需要增加两个参数:(1)增加autoSubmit="true"。(2)刷新Commit按钮。
建议方法:设定Commit按钮的PPR,指向该表单项,这样当该表单项变动后,会局部刷新Commit按钮。
16>关于Table
关于Table:点击某个按钮后,传入选中行的整个对象
方法:为该按钮添加setActionListener,其from值绑定到 Table对应iterator的currentRow,to值绑定到页面对应MB中ViewRowImpl类型的变量rowObject(该变量就是用 来接收传进来的行对象的)。实例<af:setActionListener from="#{bindings.tableIterator.currentRow}" to="#{MB.rowObject}">,然后在rowObject的set方法中就可以设置行中字段值了。
关于Table:Table显示时为只读模式,点击Table后,选中行变为修改模式,保存后回到只读模式:
适合场景:表列项比较少的情况,增加,修改,删除功能都在一个页面完成,不用打开新页面。
方法:拖放Table时,选择ADF Table(不要选择ADF ReadOnly Table),然后设置属性EditingMode=clickToEdit(默认是editAll,一般不用这种方式)。
关于Table:Table显示时为只读模式,点击某个按钮后,弹出一个窗口修改选中行,保存后关闭弹出窗口,Table回到只读模式:
方法:1、拖放Table时,选择ADF ReadOnly Table(因为修改是在弹出窗口中,因此Table只读即可)。
2、使用Popup窗口是比较好的选择,因为Popup窗口支持popupCanceledListener,当直接关闭窗口时,可以捕捉到该事件。
3、在按钮中设置showPopupBehavior。
4、在合适的位置放置popup组件,内嵌一个dialog组件。设置popup的popupCanceledListener事件、 popupFetchListener事件及Dialog的dialogListener事件,都将 对应方法绑定到页面对应的MB中。
5、MB中对应的各个listener代码。比如:
public void cancelListener(PopupCancelEvent popupCancelEvent){//cancel listener
BindingContainer bc = getBindings();
OperationBinding ob = bc.getOperationBinding("Rollback");
ob.execute();
}
public void fetchListener(PopupFetchEvent popupFetchEvent){//fetch listener
//根据触发事件的source client id,来判断点击的是哪个按钮:Edit or Insert。
if(popupFetchEvent.getLaunchSourceClientId().contains("insert")){
BindingContainer bc = getBindings();
OperationBinding ob = bc.getOperationBinding("CreateInsert");
ob.execute();
}
}
public void dialogListener(DialogEvent dialogEvent){//dialog listener
if(dialogEvent.getOutcome().name().equals("ok")){
BindingContainer bc = getBindings();
OperationBinding ob = bc.getOperationBinding("Commit");
ob.execute();
}else{
BindingContainer bc = getBindings();
OperationBinding ob = bc.getOperationBinding("Rollback");
ob.execute();
}
}
17>LOV
Vo中制作LOV时,将字段UI Hints中的Default List Type设为Input Text with List of Values:
(1)、 为JobId增加LOV,并设置Default List Type:Input Text with List of Values;选择显示JobId和JobTitle,即在Display Attributes下Available中的将JobId和 JobTitle拉入Selected中。
(2)、拖放view至页面,选择生成ADF Form,发现JobId的显示组件自动设置为ADF List of Values Input。如果选中Query List Automatically,弹出查询页面时,会直接 显示所有结果。
(3)、运行,JobId为一个文本输入框,旁边有一个查询按钮,点击后可以查询,选择一条记录,会返回到文本输入框。注意,无论查询显示的是哪些字段,返回的都是 JobId。
(4)、设置inputListOfValues组件的autoSubmit="true"。除了(3)的运行效果之外:
. 当输入AD_,按下Tab键,会弹出所有以AD_开头的JobId选项。
. 当输入AD_V,按下Tab键,会直接补全为AD_VP,因为只有一个值满足条件。
(5)、 为inputListOfValues组件增加autoSuggestBehavior,如:<af:autoSuggestBehavior suggestedItems="#{bindings.JobId.suggestedItems}"/>此时,除了(4)的运 行效果之外,会新增一种效果:随着用户的输入,会自动下拉显示匹配的结果。经过测试,中文也可以支持自动匹配。
(6)、有时候我们需要为弹出的查 询窗口预设置一些过滤LOV,这是可以考虑使用LaunchPopupListener。inputListOfValues组件增加了属性 launchPopupListener,如: launchPopupListener="#{backing_Bean.filterLOV}",绑定MB中方法。最后在MB中添加方法,如下:
public void filterLOV(LaunchPopupEvent launchPopupEvent){
BindingContainer bc = getBindings();
FacesCtrlLOVBinding lov = (FacesCtrlLOVBinding)bc.get("JobId");
lov.getListIterBinding().getViewObject().setNamedWhereClauseParam("salary",4000);
}
18>popup
popup组件常常嵌套组件Note Window、Dialog、Menu,一起使用。
点击一个按钮,根据某些条件,动态来决定是否弹出一个窗口?
此种情况适合以下场景:
(1)当用户修改了表单,如果没有保存,接着直接转到其它地方,此时应该提示:“数据已更改,是否保存?”;如果保存了,则不提示。
(2)允许用户删除多条记录功能:
当用户点击Delete按钮时,需要确认用户选择了哪些记录,如果超过一条(包括一条),则提示用户:“数据将被删除,是否确认?”;否则提示“你没有选 择任何数 据”。当然,如果前端可以控制成如下效果则更好:当用户选择了一条以上(包括一条)的记录时,Enable Delete按钮;否则Desable Delete按钮。
实现步骤:
(1)在页面中创建popup对象。
(2)在按 钮上,添加actionListener(不要添加showPopupBehavior组件,因为showPopupBehavior组件会在 Action事件之前触发,也就是说,只要点击按钮就会弹 出窗口,这不符合要求),我们只在actionListener对应MB中的方法中写代码条件弹出popup窗口。
public void dialogListener(DialogEvent dialogEvent){
if(dialogEvent.getOutcome() == DialogEvent.Outcome.ok) {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExtendedRenderKitService extendedRenderKitService = Service.getRenderKitService(facesContext, ExtendedRenderKitService.class);
String script = "var popup; popup = AdfPage.PAGE.findComponent(‘"+"p2"+"‘); popup.show();";
extendedRenderKitService.addScript(facesContext, script);
}else if(dialogEvent.getOutcome() == Dialog.Outcome.cancel) {
//Nothing to do here...
}
}
18>am
Nested AM与Root AM的Transaction关系
在实际应用中,为了达到逻辑重用的目的,会使用嵌套的AM。在Root AM和Nested AM中都有各自的VO,在一个页面中,可能同时用到了不同AM的不同VO,那么当一个AM提交时,另一个AM是否也会提交呢?
实验略,实验中需要巩固的代码:
如果VO中字段为Number类型,那么给该字段设置值时,应该这样--vo.setMinSalary(new oracle.jbo.domain.Number(1000));
获取AM对象的代码:
ApplicationModule am = ADFUtils.getApplicationModuleForDataControl("AppModuleDataControl");
AppModuleImpl service = (AppModuleImpl)am;
NestedAppModuleImpl nestedService = (NestedAppModuleImpl)service.getNestedAppModule();//从Root AM中获取Nested AM对象
结论:
(1)Nested AM与Root AM使用的是同一个Transaction,都是Root AM中的Transaction,无论使用哪个AM提交,另一个AM中的VO也会被提交。
(2)多个Root AM之间的Transaction互不干涉,各自管理自己的。
(3)所以,想要避免提交本不想提交的表单,该表单所对应的VO的AM必须是Root AM。
注:Nested AM并不是另外一种AM,只不过是次AM是嵌套在另一个AM中的,所以叫它Nested(嵌套的)AM。
Transaction和DBTransaction的区别与联系
DBTransaction和Transaction都是接口,DBTransaction是Transaction的子类,Transaction主要提供了一些事务常用到的方法:
.commit:Commits the transaction and saves all changes to the database.
.connect: Attempts(试图) to establish(建立) a connection to the given database URL.
.disconnect: DisConnects the server from the database.
.getLockingMode: Gets the preferred(首先的、当前的) locking mode for this transaction.
.rollback: Rolls back the transaction and discards(丢弃) all changes.
.setLockingMode: Sets the preferred(首选的、当前的) locking mode for this transaction.
DBTransaction则继承了以上方法,并提供了一些针对EO的方法:
. findByPrimaryKey()
. getSession()
. getEnvironment()
验证实例略,需记住和巩固代码:
当给vo中添加row时,vo.createRow()后一定要记住vo.insertRow(row)。
在MB中先获取DataControl对象,在从中获取AM对象代码,
DCDataControl dc = BindingContext。getCurrent().getDefaultDataControl();
ApplicationModule am = (ApplicationModule)dc.getDataProvider();
AppModuleImpl service = (AppModuleImpl)am;
验证结论:
(1)使用getTransaction().commit()和getDBTransaction().commit(),数据库都提交了。
(2)一般情况下,使用Transaction就可以了,除非你需要使用DBTransaction上独有的方法。
19>Database Connection Pool调优
在AM的配置项中,有Connection Pool一项,其分为两种:JDBC URL和JDBC Datasource。
(1)在开发环境中,一般使用JDBC URL,由ADF管理ConnectionPool。这时,你可以修改相关的参数。但一般来说,开发时不会关心数据库连接池的优化。因此,一般不修改 这些参数。
(2) 在生产环境中,一般使用JDBC Datasource,由应用服务器管理Connection Pool,与ADF无关。所有在AM上设置的Connection Pool参数将被忽略。AM也将从应用服务器 管理的Connection Pool中获取数据库连接。
使用JDBC Datasource好处是不用在开发端配置数据库的具体信息,将来数据库变了,只要JNDI名字不变,程序就不用修改。
所以,总的来说,我们不需要修改Connection Pool的配置,因为这个是由应用服务器管理的,不同的应用服务器配置不同,需要查相关手册。
20>Application Module Pool调优
Application Module Pool使用用来存放有同一类型的AM实例的池子,多个浏览器客户端可以“共享使用”少量的Application Module实例,这样就可以提高应用的性能。只有根一级的Application Module才可以建立Pool,换句话说,内嵌的Application Module也将“共享使用”根一级的Application Module Pool,包括数据库连接,事务,缓存。
Application Module实例也分为状态和无状态两种。对于有状态的AM实例,它保存用户session的相关信息,用户做下一步操作时,将会继续使用该AM实例。如 果用户请求很多,AM实例已经接近峰值,那么将会“钝化”这些有状态的AM实例,即把有状态信息持久化。然后把这些AM实例腾出来供其它用户使用,等到该 用户继续做下一个有状态操作时,再用一个新的AM实例,并匹配“激活”刚才“钝化”的信息。
AM Pool主要参数说明如下:
1、Pool Behavior Parameters
. Failover(失效备援) Transaction State Upon Managed Release: 默认false,建议设置为true。
在有状态的AM实例被释放回池中时执行“钝化”。
. Disconnect Application Module Upon Release:默认值false,建议设置为false。
强制AM实例每次释放回池中时,同时释放掉其对应的JDBC连接。默认为false,好处是不仅不用再从数据库连接池获取连接,并且连prepared statements也可以直接 拿来就用。
. Support Dynamic JDBC Credentials(文凭、信用状): 默认值true,建议设置为true。
允许程序在新的用户Session开始的时候通过代码来修改数据库的连接的用户和口令。
. Reset Non-Transactional State Upon Unmanaged Release: 默认true,建议设置true。
当AM实例以无状态的方式释放回池中时,重置所有的Non-Transaction State,比如VO的运行时设置,JDBC的Prepared Statements,绑定变量等等,保证放回池中的 AM实例是“干净”的
. Enable Application Module pooling: 默认false,建议设置为true。
. Row-Level Locking Behavior Upon Release: 默认false,建议设置为true。
强制AM实例被释放回池时不去在数据库中创建一个pending transaction state。
ADF web application应该设置锁定模式为乐观锁“optimistic”(默认为悲观锁“pessimistic”),以避免创建行级锁。
关于参数“Disconnect Application Module Upon Release”实验结论:
(1)在使用AM Pool时,发现数据库连接消耗的特别快。用户每次访问一个页面,都会新占用一个数据库连接,这是不合理的。
(2)Disconnect Application Module Upon Release参数默认不选中是有利于性能优化的。
(3) 参数Idle Instance Timeout、Pool Polling Interval、Maximum Instance Time to Live应该配合使用,基本原则是:Maximum Instance Time to Live > Idle Instance Timeout + Pool Polling Interval(否则还没等AM Pool标记清除之前,AM实例就已经被清除了)。
(4)每 个AM池的设置可以根据自身情况有所不同,比如如果AM的调用时间比较长,可以适当增大Maximum Instance Time to Live;Maximum Pool Size基本接近或略高于用 户并发数的峰值,如果用户并发数很高,可以适当减小Maximum Instance Time to Live,便于回收AM实例和数据库连接;对应操作频繁,但事务较小的AM,可以适当增 大。
(5)ADF 自带的数据库连接池一般不用于生产环境。在生产环境下,应该使用JDBC Datasource的方式,使用WLS的数据库连接池来管理。基本原则是数据库连接池的最大值 =AM Maximum Pool Size。Idle Instance Timeout、Pool Polling Interval,尽量直接重用AM实例,无需重新获取。
对AutoSubmit、PartialSubmit、Immediate、PartialTriggers更深层次的了解:
1、autoSubmit只在输入组件上才有的一个属性,如RichIputText。
2、partialSubmit只在命令组件上才有的一个属性,如CommandLink。
autoSubmit 与partialSubmit的区别是,后者仅处理更改的组件本身以及在其partialTriggers属性中包含引用的所有组件,不会触发表单中的必 填项的验证,除非这些 必填项的partialTriggers指向设置了autoSubmit=true的组件。
值得注意的 是,CommandToolBarButton默认partialSubmit=true,所以如果你需要刷新某个组件,必须设置 partialTriggers,否则页面不会被刷新。而CommandButton默认partialSubmit=false,默认会刷新整个页面。
技术因素不是网站成功的因素,决定网站成功的关键因素是内容。
如果你定一个高得离谱的目标,就算失败了,那你的失败也在任何人的成功之上——詹姆斯.卡梅隆。
22>TaskFlow之Reentry属性的使用
在实际应用中,用户可能会点击浏览器的回退按钮回到上一页面,在有些情况下会导致一些问题。 ADF Bounded TaskFlow有一个选项(Unbounded TaskFlow无此选项)可以设置是否允许用户使用浏览器的回退按钮回到上一页面,这个属性就是Reentry。
Reentry可以设置为:
. reentry-allowed:默认选项。允许该bounded task flow中的所有页面实用化浏览器的回退按钮重新进入上一页面。
. reentry-not-allowed: 不允许该bounded task flow中的所有页面使用浏览器的回退按钮重新进入上一页面。这里的不允许不是禁止用户点击浏览器的回退按钮,这个是 没有办法禁止的。它的不允许时这样体现的:当用户点击浏览器的回退按钮后,可以重新进入上一页面,但如果你在该页面做任何事情,也如点击按钮,将会抛出异 常: InvalidTaskFlowReentry。
. reentry-outcome-dependent:能否允许该bounded task flow中的所有页面使用浏览器的回退按钮重新进入上一页面取决于上一次从该bounded task flow返回的outcome ,也就是说,取决于Return Activity上的Reentry属性设置。适合的典型场景:购物网站如果用户取消了某此采购,那么允许回退;如果订单已经提交,则不允许回退。
注:reentry- not-allowed的行为和我们想象的不一样,事后警告用户抛异常,这个页面效果肯定不太友好,这时,我们应该创建一个exception handler来处理这个异常,提示用户页面过期,需要重新登录之类的警告,并在几秒之后自动跳转到登录页面。
几个需要明确的问题:
(1)、重新进入Task Flow后,Task Flow上的输入参数将使用当前值(如果有新的赋值的话),而不是初始值。
(2)、重新进入Task Flow后,Manage Bean中的值也跟着回退到之前的值,所有在用户回退之后的修改将丢失。可以通过设置View Activity上的redirect属性来改变这一行 为。这个有待实验考证。
23>TaskFlow之Transaction的使用
ADF TaskFlow有一个很重要的特性:在Bounded TaskFlow上可以设置事务。也就是说,在这个TaskFlow中的所有Activity(View和其它非可视化的)都将属于一个事务。区别于 Java EE Container上的事务设置(要么是直接设置在EJB的方法上,要么是设置在ejb.xml中,运行时由Container解析);TaskFlow 这个是设置在控制器层的,这使得我们很容易控制一个TaskFlow的事务。
打开一个Bounded Task Flow,在Property中找到Behavior,其中Transaction属性:找到
. No Controller Transaction:不加入任何事务中。
. Always Begin New Transaction:开始一个新的事务,无论是否已经在一个事务之中。
. Always Use Existing Transaction:加入已有的事务,如果没有事务可加入,抛异常。
. Use Existing Transaction If Possible:加入已有的事务,如果没有事务可加入,开始一个新的事务。
事务的提交是在Task Flow Return Activity上完成的,在Property中找到Behavior,其中End Transaction属性,我们可以设置如何提交事务(commit or rollback)。
进一步对Bounded TaskFlow对事务支持程度的研究,以下实验结论:
实验一结论:Bounded TaskFlow中的所有View Activity都属于一个事务。
实验二结论:Bounded TaskFlow中事务处理是跨AM的,即不同AM中的VO的事务操作,也可以作为一个全局事务来管理。页面都没有
实验三结论:Bounded TaskFlow中事务处理是跨AM、跨数据库的,即不同AM中的VO的事务操作,VO来自于不同的数据库,也可以作为一个全局事务来管理。
实验四:2个AM,3个DB Connection,1个Bounded TaskFlow(包含两个修改页面和一个Method Call,每个页面都没有事务提交功能,而是交给Task Flow Return Activity负责)。
实验四结论:EJB Transaction由Java EE Container负责,不能与TaskFlow Transaction一起作为一个全局事务来管理二者的事务各自独立,互不干涉。
转载自:http://blog.csdn.net/qq136722979/article/details/12856843