2013年4月16日星期二

ADF_210:使用View Link Accessor

开发运行环境:JDeveloper 11.1.2.3.0 + Oracle Database 11g Express Edition。

实际使用中,经常需要在某个VO上增加一个Attribute,该Attribute的值来自于其它关联的的VO的Attribute值。
使用View Link有一个Accessor特性可以轻松地帮我们实现该功能。

重要步骤说明:

1. Accessor 是定义在View Link上的,默认情况下是没有定义的。
以Department和Employee为例,其一对多的关系是定义在EmpDepFkLink.xml上的。
默认情况下,Accessor是没有定义的,即Exposed in View Object没有定义。


2. 建立Accessor。

 建立Accessor后,会看到DepartmentsView1已经暴露到EmployeesView中了。


3. 在EmployeesView中增加一个Attribute:DepartmentName,Expression 表达式:DepartmentsView1.DepartmentName。


4. 运行AM
点击EmployeesView1,
查看日志,会发现两条SELECT 语句,也就是说,使用Accessor关联要调用两次SQL查询。
不过,点击前后记录,会发现不是每次都执行查询,只有新的Employee才会执行。
访问过的记录,都是从内存中获取的。

[77] INFO: No app _def_ in BindingContext for: AppModule
[78] DCUtil, returning:oracle.jbo.uicli.binding.JUApplication, for AppModule
[79] Replacing: null with: AppModule_EmployeesView1_0PageDef
[80] INFO: No app _def_ in BindingContext for: AppModule
[81] DCUtil, returning:oracle.jbo.uicli.binding.JUApplication, for AppModule
[82] INFO: No app _def_ in BindingContext for: PROJECT_GLOBAL_VARIABLES
[83] INFO: mDCRefMap lookup failed. Does the cpx have a dataControlUsages 'dc' entry? PROJECT_GLOBAL_VARIABLES
[84] Resolving VO:AppModule.EmployeesView1 for iterator binding:oracle_jbo_uicli_binding_JUIteratorBinding_9
[85] DCUtil, RETURNING: for AppModule.EmployeesView1
[86] EmployeesView1 ViewRowSetImpl.execute caused params to be "un"changed
[87] Carrying over CappedRowCount:-1for ViewRowSet:EmployeesView1
[88] Column count: 15
[89] executeQueryForCollection ViewObject:EmployeesView1, RowSet:EmployeesView1
[90] EmployeesView1>#q computed SQLStmtBufLen: 494, actual=347, storing=377
[91] SELECT Employees.EMPLOYEE_ID,         Employees.FIRST_NAME,         Employees.LAST_NAME,         Employees.EMAIL,         Employees.PHONE_NUMBER,         Employees.HIRE_DATE,         Employees.JOB_ID,         Employees.SALARY,         Employees.COMMISSION_PCT,         Employees.MANAGER_ID,         Employees.DEPARTMENT_ID FROM EMPLOYEES Employees
[92] ViewObject: [model.EmployeesView]AppModule.EmployeesView1 Created new QUERY statement
[93] Bind params for ViewObject: [model.EmployeesView]AppModule.EmployeesView1
[94] DBTransactionImpl.mDefaultSparseArrayThreshold is 20
[95] Entity with key:oracle.jbo.Key[100 ] owned by row:oracle.jbo.Key[100 ]
[96] Employees_Departments1_EmpDeptFkLink_DepartmentsView_0 ViewRowSetImpl.setParamValues params changed
[97] Employees_Departments1_EmpDeptFkLink_DepartmentsView_0 ViewRowSetImpl.execute caused params to be "un"changed
[98] Carrying over CappedRowCount:-1for ViewRowSet:Employees_Departments1_EmpDeptFkLink_DepartmentsView_0
[99] Column count: 5
[100] executeQueryForCollection ViewObject:Employees_Departments1_EmpDeptFkLink_DepartmentsView, RowSet:Employees_Departments1_EmpDeptFkLink_DepartmentsView_0
[101] Employees_Departments1_EmpDeptFkLink_DepartmentsView>#q computed SQLStmtBufLen: 250, actual=216, storing=246
[102] SELECT Departments.DEPARTMENT_ID,         Departments.DEPARTMENT_NAME,         Departments.MANAGER_ID,         Departments.LOCATION_ID FROM DEPARTMENTS Departments WHERE Departments.DEPARTMENT_ID = :Bind_DepartmentId
[103] ViewObject: [model.DepartmentsView]AppModule.Employees_Departments1_EmpDeptFkLink_DepartmentsView Created new QUERY statement
[104] Bind params for ViewObject: [model.DepartmentsView]AppModule.Employees_Departments1_EmpDeptFkLink_DepartmentsView
[105] For RowSet : Employees_Departments1_EmpDeptFkLink_DepartmentsView_0
[106] Binding param "Bind_DepartmentId": 90
[107] Entity with key:oracle.jbo.Key[90 ] owned by row:oracle.jbo.Key[90 ]
[108] RecalcExpression trueresovles to true
[109] Employees_Departments1_EmpDeptFkLink_DepartmentsView_1 ViewRowSetImpl.setParamValues params changed
[110] Employees_Departments1_EmpDeptFkLink_DepartmentsView_1 ViewRowSetImpl.execute caused params to be "un"changed
[111] executeQueryForCollection ViewObject:Employees_Departments1_EmpDeptFkLink_DepartmentsView, RowSet:Employees_Departments1_EmpDeptFkLink_DepartmentsView_1
[112] **** refreshControl() for BindingContainer :AppModule_EmployeesView1_0PageDef
[113] RecalcExpression trueresovles to true
[114] Employees_Departments1_EmpDeptFkLink_DepartmentsView_2 ViewRowSetImpl.setParamValues params changed
[115] Employees_Departments1_EmpDeptFkLink_DepartmentsView_2 ViewRowSetImpl.execute caused params to be "un"changed
[116] executeQueryForCollection ViewObject:Employees_Departments1_EmpDeptFkLink_DepartmentsView, RowSet:Employees_Departments1_EmpDeptFkLink_DepartmentsView_2
[117] RecalcExpression trueresovles to true
[118] Employees_Departments1_EmpDeptFkLink_DepartmentsView_3 ViewRowSetImpl.setParamValues params changed
[119] Employees_Departments1_EmpDeptFkLink_DepartmentsView_3 ViewRowSetImpl.execute caused params to be "un"changed
[120] executeQueryForCollection ViewObject:Employees_Departments1_EmpDeptFkLink_DepartmentsView, RowSet:Employees_Departments1_EmpDeptFkLink_DepartmentsView_3
[121] ##### QueryCollection.finl oracle.jbo.Key[90 ]


Project 下载:ADF_ViewLink_Accessor.7z

参考文献:
1. http://andrejusb.blogspot.jp/2011/12/groovy-with-view-link-accessor.html

2013年4月5日星期五

ADF_208:InputDate组件使用指南之一:基本功能

开发运行环境:JDeveloper 11.1.2.3.0 + Oracle Database 11g Express Edition。

1. 创建表Calendar
CREATE TABLE CALENDAR 
(
  ACTIVITY_ID VARCHAR2(20) NOT NULL 
, PROVIDER VARCHAR2(20) NOT NULL 
, START_DATE DATE NOT NULL 
, END_DATE DATE NOT NULL 
, TITLE VARCHAR2(200) NOT NULL 
, CONSTRAINT CALENDAR_PK PRIMARY KEY 
  (
    ACTIVITY_ID 
  )
  ENABLE 
);
注意这里Start_Date和End_Date是Date类型。

2. 创建应用,使用ADF BC生成模型层
注意到EO和VO的StartDate和EndDate的数据类型都是Timestamp,看来ADF BC把Date类型的表字段默认使用Timestamp数据对象来表示。

3. 创建页面,拖放DataControl生成InputDate组件

4. 运行效果
(1)默认效果,注意没有时间项可以选择。


(2)在EO上设置日期的格式
Format Type: Simple Date。
Format: yyyy-MM-dd HH:mm:ss。


 再次运行,可以看到时间项选择,但是变成Inline Popup。

 保存后,在数据库中查询Calendar表,发现只有日期保存了,而时间没有保存。
 怀疑是一个Bug。
 于是把Calendar表Start_Date字段类型改为Timestamp。

再次运行,发现时间也保存到表了。

Project 下载:ADF_InputDate.7z

参考文献:
1. https://forums.oracle.com/forums/thread.jspa?threadID=1074781
2. http://jdevadf.oracle.com/adf-richclient-demo/docs/tagdoc/af_convertDateTime.html
3. http://dkleppinger.blogspot.jp/2011/09/how-to-ignore-time-component-of-date.html
4. http://maping930883.blogspot.jp/2012/08/adf148table-date.html
5. http://jobinesh.blogspot.jp/2011/08/what-you-may-need-to-know-about-data.html
6. http://oracleseeker.com/2009/12/02/adf_history_column_history_type/
7. https://cn.forums.oracle.com/forums/thread.jspa?threadID=1074781
8. https://blogs.oracle.com/shay/entry/a_timehour_selector_in_adf_fac
9. http://jdevadf.oracle.com/adf-richclient-demo/faces/components/inputDate.jspx

2013年4月1日星期一

ADF_207:Task Flow使用指南之十:通过程序调用TaskFlow

运行环境:JDeveloper 11.1.2.3.0 + Oracle Database 11g Express Edition。

Task Flow使用指南之九中介绍了如何调用Remote TaskFlow, 但那是通过TaskFlow Call Activity调用的,如何通过程序调用一个Bounded TaskFlow呢?

首先我们必须知道TaskFlow的URL的格式,以Remote TaskFlow应用为例,其中的employee TaskFlow的URL如下:
http://127.0.0.1:7101/ADF_TaskFlow_RemoteProducer-ViewController-context-root/faces/adf.task-flow?adf.tfId=employee-btf&adf.tfDoc=/WEB-INF/employee-btf.xml&departmentId=60
其中参数adf.tfId是TaskFlow的ID,参数adf.tfDoc是TaskFlow的Document。
这两个参数对应TaskFlow Call Activity上的同名属性。
注意,我这里还带了一个参数departmentId,因为employee TaskFlow的输入参数就是departmentId。
你可以复制这个URL,直接在浏览器中访问,如果能看到departmentId=60的员工列表,就说明employee TaskFlow可以通过URL访问。
有了URL,我们就可以通过程序调用employee TaskFlow。

重要步骤说明:

1. 创建Remote TaskFlow Producer
这一步和《Task Flow使用指南之九》一样,这里不再赘述。

2. 在departments.jsf页面中,添加按钮:Invoke Employee TaskFlow
(1)页面代码如下:

<af:commandButton text="Invoke Employee TaskFlow" id="cb2" action="#{myBackingBean.invokeEmployeeTaskFlow}">
    <af:setPropertyListener from="#{bindings.DepartmentId}" to="#{requestScope.departmentId}" type="action"/>
</af:commandButton>
(2)对应的Managed Bean的方法:invokeEmployeeTaskFlow

    public String invokeEmployeeTaskFlow() {
        FacesContext fctx = FacesContext.getCurrentInstance();
        ExternalContext ectx = fctx.getExternalContext();
        Map m = ectx.getRequestMap();

        String tfId = "employee-btf";
        String tfDoc = "/WEB-INF/employee-btf.xml";
        Map parameters = new HashMap();
        parameters.put("departmentId", m.get("departmentId"));

        TaskFlowId taskflowId = new TaskFlowId(tfDoc, tfId);
        //TaskFlowId taskflowId = TaskFlowId.parse("/WEB-INF/employee-btf.xml#employee-btf");
        String taskflowUrl = ControllerContext.getInstance().getTaskFlowURL(false, taskflowId, parameters);
        //String taskflowUrl = "http://127.0.0.1:7101/ADF_TaskFlow_RemoteProducer-ViewController-context-root/faces/adf.task-flow?adf.tfId=employee-btf&adf.tfDoc=/WEB-INF/employee-btf.xml&departmentId=60";

        try {
            ectx.redirect(taskflowUrl);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return taskflowUrl;
    }

Project 下载:ADF_TaskFlow_RemoteProducer.7z

参考文献:
1. http://oracleseeker.com/2009/10/28/adf_current_taskflow_id/
2. http://oracleseeker.com/2009/11/09/adf_backing_action_taskflow/
3. https://forums.oracle.com/forums/thread.jspa?threadID=1082973
4. https://blogs.oracle.com/jdevotnharvest/entry/how-to_navigate_in_bounded_task_flows
5. http://adfdeveloper.blogspot.jp/2011/07/adf-auto-redirect-while-still-passing.html
6. https://blogs.oracle.com/jdeveloperpm/entry/how-to_efficiently_redirect_to_an_adf_faces_view_using_adf_controller
7. http://udayarocks.wordpress.com/2011/07/22/how-to-invoke-a-method-from-managed-bean-when-jspx-page-loads-in-adf/