本文是由英文幫助翻譯所得:
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