2013年6月28日星期五

ADF_237:实现Table数据自动填充功能之一:修改

开发运行环境:JDeveloper 11.1.2.4 + Oracle Database XE 11gR2

前一个实验使用的是Form,实际情况中,还可能会使用Table。
考虑以下场景:用户修改一行记录时,修改其中某个字段后,希望能够带出其它字段值,即自动填充。

1. 为了不影响其它VO,新建一个VO:EmployeesDetailsView。
注意,为了演示自动填充的功能,这里手工添加了一个Transient Attribute:JobTitle。
也就是说,这个JobTitle并不是从Jobs EO中关联过来的。
我是为了演示输入JobId,自动把JobTitle带出来。

2. 修改JobsView,增加一个View Criteria:GetJobTitleByJobIdViewCriteria,用于根据JobId获取JobTitle。

3. 修改JobsViewImpl.java,增加一个方法:,用于根据JobId获取JobTitle,并将其暴露到Client。

    public void getJobTitleByJobId(String jobId) {
        this.setApplyViewCriteriaNames(null);
        ViewCriteria criteria = this.getViewCriteria("GetJobTitleByJobIdViewCriteria");
        this.setRangeSize(10);
        this.applyViewCriteria(criteria);
        this.setbv_job_id(jobId);
        this.executeQuery();

    }

4. 修改页面,在JobId上增加ValueChangeListener
完成后的页面代码如下:
<af:inputText value="#{row.bindings.JobId.inputValue}"
              label="#{bindings.EmployeesDetailsView1.hints.JobId.label}"
              required="#{bindings.EmployeesDetailsView1.hints.JobId.mandatory}"
              columns="#{bindings.EmployeesDetailsView1.hints.JobId.displayWidth}"
              maximumLength="#{bindings.EmployeesDetailsView1.hints.JobId.precision}"
              shortDesc="#{bindings.EmployeesDetailsView1.hints.JobId.tooltip}"
              autoSubmit="true" id="it6"
              valueChangeListener="#{viewScope.myBackingBean.onJobIdValueChange}">
    <af:autoSuggestBehavior maxSuggestedItems="10"
                            suggestItems="#{viewScope.myBackingBean.onJobIdSuggest}"/>
    <f:validator binding="#{row.bindings.JobId.validator}"/>
</af:inputText>

对应的Managed Bean中的方法代码如下:

    public void onJobIdValueChange(ValueChangeEvent valueChangeEvent) {
        String newValue = (String)valueChangeEvent.getNewValue();
        System.out.println("################################# 1 " + newValue);
        OperationBinding binding = ADFUtils.findOperation("getJobTitleByJobId");
        binding.getParamsMap().put("jobId", newValue);
        binding.execute();

        DCIteratorBinding it = ADFUtils.findIterator("JobsView1Iterator");
        Row[] allRowsInRange = it.getAllRowsInRange();
        if (allRowsInRange.length > 0) {
            Row row = allRowsInRange[0];
            String jobTitle = (String)row.getAttribute("JobTitle");
            System.out.println("################################# 2 " + jobTitle);
            ADFUtils.setBoundAttributeValue("JobTitle", jobTitle);
        }
    }

这里有一个问题要注意:就是何时会触发InputText上的ValueChange事件。
答案是:当输入内容和以前的内容不一样时,并且焦点离开当前InputText时。
所以,自动提示功能发生在前,用户选择和以前不同的某个选项后,才会发生ValueChange事件。
因此,不要担心ValueChange事件会调用多次,其实只有一次。

5. 修改页面的Bindings,这一点和前一个实验一样。
不同的地方是,还要多增加一个Attribute Binding:JobTitle和一个methodAction Binding:getJobTitleByJobId。

6. 运行
修改某行记录,选择某个JobId: 

选定后,JobTitle会自动带出来:


Project 下载:ADF_AutoComplete(2).7z

2013年6月27日星期四

ADF_236:使用AutoSuggest Behavior实现自动提示功能

开发运行环境:JDeveloper 11.1.2.4 + Oracle Database XE 11gR2

自动提示功能是页面上非常常见的一个功能。
本文以InputText组件为例,来说明如何实现自动提示功能。与网上的其它实现不同,本文介绍的实现方式具有一定的普遍性。

需求很简单:实现Employee的JobId字段的自动提示功能。

1. 选择Departments、Employees、Jobs表生成EO和VO。

2. 在JobsView上增加一个View Criteria:queryJobsByJobIdViewCriteria,根据JobId查询Jobs。

3. 定制JobsViewImpl.java,增加一个方法:queryJobsByJobId,并暴露到Client。
    public void queryJobsByJobId(String partOfJobIdStr) {
        this.setApplyViewCriteriaNames(null);
        ViewCriteria criteria = this.getViewCriteria("QueryJobsByJobIdViewCriteria");
        this.setRangeSize(10);
        this.applyViewCriteria(criteria);
        this.setbv_JobId(partOfJobIdStr);
        this.executeQuery();
    }

4. 拖放Employees Data Control生成Form
(1)在JobId上增加AutoSuggest Behavior
(2)设置AutoSubmit=true
完成后的代码如下:
<af:inputText value="#{bindings.JobId.inputValue}" label="#{bindings.JobId.hints.label}"
              required="#{bindings.JobId.hints.mandatory}"
              columns="#{bindings.JobId.hints.displayWidth}"
              maximumLength="#{bindings.JobId.hints.precision}"
              shortDesc="#{bindings.JobId.hints.tooltip}" autoSubmit="true" id="it6">
    <f:validator binding="#{bindings.JobId.validator}"/>
    <af:autoSuggestBehavior maxSuggestedItems="10"
                            suggestItems="#{viewScope.myBackingBean.onJobIdSuggest}"/>
</af:inputText>
(3)对应的Managed Bean中的方法:onJobIdSuggest,代码如下:
    public List onJobIdSuggest(FacesContext facesContext, AutoSuggestUIHints autoSuggestUIHints) {
        String param = autoSuggestUIHints.getSubmittedValue();
        OperationBinding binding = ADFUtils.findOperation("queryJobsByJobId");
        binding.getParamsMap().put("partOfJobIdStr", param);
        binding.execute();

        DCIteratorBinding it = ADFUtils.findIterator("JobsView1Iterator");
        it.setRangeSize(10);
        Row[] allRowsInRange = it.getAllRowsInRange();
        ArrayList selectItems = new ArrayList();
        for (Row o : allRowsInRange) {
            String var = (String)o.getAttribute("JobId");
            String desc = (String)o.getAttribute("JobTitle");
            selectItems.add(new SelectItem(var, var + " " + desc));
        }

        return selectItems;
    }

5. 修改页面的Bindings
(1)手工在Executables中增加Iterator:JobsView1Iterator



 (2)手工在Bindings中增加methodAction:queryJobsByJobId




6. 运行
当修改JobId时,会根据用户的输入自动提示相应的下拉选项。


Project 下载:ADF_AutoComplete.7z

2013年6月26日星期三

ADF_235:Groovy在ADF BC中的常见用法之三

开发运行环境:JDeveloper 11.1.2.4

前面介绍的都是在EO中使用Groovy,在VO中和在EO中使用Groovy的方法基本一样,同样支持
 (1)访问同一个VO中的Attribute
 (2)访问其它VO中的Attribute
 (3)定义Transient Attribute验证规则
 (4)调用ViewRowImpl中的自定义方法
 (5)使用集合功能
VO比EO更灵活的地方是,还支持
 (6)在Binding Variable中使用Groovy
在Binding Variable中的Groovy上下文默认是ViewObjectImpl,而不是单一的Row对象,所以无法访问每行的Attribute。
另外,Groovy可以调用对应的Java类中的自定义方法,在EO中是EntityImpl,在VO中是ViewRowImpl,而不是ViewImpl。
因为ViewImpl代表的是整个集合,而ViewRowImpl代表的是集合中每一行对象。

我以一个常见的一个需求为例来说明如何在Binding Variable中使用Groovy:
客户登录之后,显示员工详细信息,其中Manager一项使用LOV实现下拉列表,并且要求只显示和登录员工所在同一部门的其它员工。

1. 为了不影响原有的Employees VO,新增一个Employees VO:EmployeesGroovyView

2. 增加一个Binding Variable:bvDepartmentId
其中的Value为adf.userSession.userData.DeptId,也就是说,从session中获取一个叫DeptId的值。

3. 增加一个ViewCriteria:EmployeesGroovyViewCriteria


4. 为ManagerId 增加一个LOV



使用一个指向自己的VO作为Accessors:


选中先前定义的EmployeesGroovyViewCriteria:


5. 还有一个问题没解决,如何设置session中的DeptId的值呢?
当然你可以在用户成功登录后,获取标准的Http Session对象,然后写代码设置。
这个例子只是了为了测试Groovy,所以我通过重写Application Module中的prepareSession方法来设置,并且设定DeptId=80。
 @Override
 protected void prepareSession(Session session) {
        session.getUserData().put("DeptId",new Number(80));
 }

prepareSession方法是用来做一些与特定用户session相关的一些初始化工作,比如调用一个存储过程初始化数据,或者设置一些应用级的配置参数。
该方法在创建新用户session时被调用,就是从AM Pool获取AM实例时被调用。

6. 运行效果


可以看出,ManagerId的下拉列表只显示DepartmentId=80的员工列表。


Project 下载:ADF_Groovy(3).7z

参考文献:
1. http://www.jobinesh.com/2011/03/initializing-bind-variables-in-query.html
2. http://www.jobinesh.com/2009/08/tips-on-lov-runtime.html
3. http://andrejusb.blogspot.jp/2012/01/how-to-access-session-scope-in-adf-bc.html
4. http://blog.csdn.net/qingqingxuelang/article/details/5784943
5. https://forums.oracle.com/thread/975097
6. http://adfcodebits.blogspot.jp/2010/06/bit-21-overriding-preparesession-to-do.html

2013年6月19日星期三

ADF_234:Groovy在ADF BC中的常见用法之二

开发运行环境:JDeveloper 11.1.2.4

1. 访问其它EO中的Attribute
以Departments和Employees为例,二者是一对多的关系。
现在想在Employees中的AnnualSalary中引用Departments中的locationId。
首先,要看一下Departments和Employees之间的Assocation关系定义。
可以看到,在Employees中暴露了Departments Accessor。


那么AnnualSalary的Expression可以这样写:adf.object.getDefaultSalaryForGrade(Departments.LocationId)。

方法getDefaultSalaryForGrade(Integer locId)是定义在EmployeeImpl中的自定义方法:   
public static BigDecimal getDefaultSalaryForGrade(Integer locId) {
    System.out.println(" ################### " + locId);
    return new BigDecimal(20000);
}

2. 内置的属性:adf.currentDate 和 adf.currentDateTime
比如,你可以为Hiredate定义一个验证规则:
return (newValue < adf.currentDate)

3. 集合功能
<Accessor>.sum(Groovyexpression)
<Accessor>.count(Groovyexpression)
<Accessor>.avg(Groovyexpression)
<Accessor>.min(Groovyexpression)
<Accessor>.max(Groovyexpression)

(1)对于Departments的SalarySum,你可以使用Employees.sum("Salary")来统计该部门员工的工资和。
其中,Employees是Departments中的Accessor,Salary是Employees中的Attribute。
你甚至可以这样写:Employees.sum("Salary + 1000"),为每个员工涨1000元。
因为参数"Salary + 1000"将被首先解析,即获取每一个Employees EO上的Salary,然后加1000。
同样,你也可以在参数中再调用一个方法,比如:Employees.sum("Salary + adf.object.getBenefitsValue(JobId)")。
注意,方法getBenefitsValue是定义在EmployeesImpl中的。

(2)对于Employees的Salary,你可以增加一个验证规则:return newValue > Departments.Employees.min("Salary"),即要求用户的工资必须大于这个部门工资最低的员工工资。
这里,Departments是Employees EO中reference的对象,而Departments.Employees是Deparments EO中reference的对象。
注意,使用这个验证规则时,如果一个部门的员工很多,比如上千名员工,会有性能问题。

4. 更复杂一些的Groovy验证规则
(1)(JobId != "SALESMAN" ? newValue > 100 : newValue > 0)
(2)Employees.sum("CommissionPct != null ? CommissionPct : 0")
(3)Employees.count("CommissionPct != null && CommissionPct > 300 ? CommissionPct : null")

5. 使用adf.error抛出异常或警告
if (newValue > 1000){
if (newValue > 5000){
adf.error.raise("SALARY_TOO_HIGH_ERROR")
return false
}
adf.error.warn("SALARY_LIMIT_WARNING")
return true
}else{
       return true
}

Project 下载:ADF_Groovy(2).7z

2013年6月18日星期二

ADF_233:Groovy在ADF BC中的常见用法之一

开发运行环境:JDeveloper 11.1.2.4

1. 设置EO或VO的Attribute值
(1)在Employees EO上增加一个Attribute:AnnualSalary,Expression: (Salary != null ? Salary : 0 ) * 12。

(2)如果需要引用EntityImpl类中的自定义方法,需要加前缀:adf.object。
比如:adf.object.getDefaultSalaryForGrade()。
这里,adf.object指向的是当前EntityImpl。

2. 定义在EO的Attribute上的验证规则
(1)在Employees EO的Salary Attribute增加一个验证规则:

if (JobId == "SA_REP"){
 return newValue < 1000
} else
return true

在验证规则中reference一个Attribute时,Attribute-level的验证规则首先被触发,验证该Attribute的newValue值。newValue是用户输入后改变的值,oldValue是改变之前的值。

(2)在Employees EO的Salary Attribute增加另一个验证规则,其中调用了EntityImpl类中的自定义方法:getMaxSalaryForGrade。

if (JobId == "SA_MAN"){
 return newValue < source.getMaxSalaryForGrade(JobId)
} else
return true

注意,reference方法前要加前缀“source”。

3. 访问EntityImpl类的自有方法
(1)Override create方法
    /**
     * Add attribute defaulting logic in this method.
     * @param attributeList list of attribute names/values to initialize the row
     */
    protected void create(AttributeList attributeList) {
        super.create(attributeList);
        SequenceImpl seq = new SequenceImpl("EMPLOYEES_SEQ", getDBTransaction());
        oracle.jbo.domain.Number seqNextVal = seq.getSequenceNumber();
        setEmployeeId(Integer.valueOf(seqNextVal.intValue()));
    }
这样修改后,EmployeeId的值将从Sequence中获取。

(2)如果你不想Override create方法,也可以直接使用Groovy表达式来获取Sequence的下一个值。
设置EmployeeId的Expression:(new oracle.jbo.server.SequenceImpl("EMPLOYEES_SEQ",adf.object.getDBTransaction())).getSequenceNumber()
 注意,这里必须写全类SequenceImpl的路径名。

(3)如果你觉得(2)的方式有些不直观,可以在EntityImpl中定义一个Help方法。
    public oracle.jbo.domain.Number nextVal(String sequenceName) {
        SequenceImpl s = new SequenceImpl(sequenceName, getDBTransaction());
        return s.getSequenceNumber();
    }
然后,设置EmployeeId的Expression:adf.object.nextVal("EMPLOYEES_SEQ")。

4. 访问hints属性
(1)如果想访问LastName的label属性,可以这样写:adf.object.hints.LastName.label。
(2)如果在error message中想访问LastName的label属性,可以这样写:source.hints.LastName.label。

Project 下载:ADF_Groovy.7z

2013年6月15日星期六

Linux_001:安装Ubuntu桌面版

运行环境:Oracle VirtualBox 4.2.12 + Ubuntu-13.04-desktop-amd64

Ubuntu下载地址:http://www.ubuntu.com/download。笔者下载的是ubuntu-13.04-desktop-amd64.iso。
为了在Windows环境下方便使用,笔者使用Oracle VirtualBox把Ubuntu作为一个虚机安装。
安装过程相对比较简单,详见每一步的抓图。

1. 在Oracle VirtualBox中的设置







添加启动的Ubuntu的安装的iso文件。

2. 一切就绪,开始安装啦!






安装到最后一步,会提示重启,但是会报告一个出现一个问题:


忽略该问题,到Virtual Box中的存储中,去掉之前添加的Ubuntu的iso文件,否则重启时会再次提示安装。

2013年6月14日星期五

VirtualBox_005:Oracle VM VirtualBox 使用指南之五:主机与客机之间的复制和粘贴

运行环境:Oracle VirtualBox 4.2.12

主机与客机之间经常需要互相复制与粘贴文本,实现方法如下:

1. 先要安装“Install Guest Additions”
具体步骤请参考《Oracle VM VirtualBox 使用指南之三:设置共享文件夹》。


2. 在菜单“设置”中,选择常规->高级
默认情况下,共享粘贴板和拖放功能是“禁用”的。

把共享粘贴板改为“双向”

把拖放改为“双向”

设置完成后的样子如下:


现在主机和客机之间应该可以复制与粘贴文本了。

关于拖放文件功能,我这里没有测试成功,看到有人说是因为目前只支持Linux虚机,不支持Windows。
等我安装Linux虚机时再验证一下。

参考文献:
1. http://www.webupd8.org/2012/09/virtualbox-420-released-with-support.html

VirtualBox_004:Oracle VM VirtualBox 使用指南之四:虚机的网络设置

运行环境:Oracle VirtualBox 4.2.12

安装虚机的目的是为了使用虚机中的资源,比如数据库,这就要求主机和客机之间能够互相Ping通。
默认的虚机网络设置如下:


改成“桥接网卡”。


同时,把主机和客机的防火墙关掉(默认是启用的)。


重启虚机,检查主机和客机的IP地址,应该在同一网段,并且可以互相ping通。
现在你可以在主机上访问虚机上的资源了。

参考文献:
1. http://stackoverflow.com/questions/6117610/how-to-access-oracle-db-in-virtualbox-from-host-windows

2013年6月13日星期四

VirtualBox_003:Oracle VM VirtualBox 使用指南之三:设置共享文件夹

运行环境:Oracle VirtualBox 4.2.12

本文介绍如何为Win XP虚机设置共享文件夹,如果是虚机是Linux,请参考《Oracle VM VirtualBox 使用指南之一:装载虚机》。

1. 安装"Install Guest Additions"
安装好Win XP虚机后,在顶部的菜单中选择Devices,点击"Install Guest Additions"。
注意,如果找不到顶部的菜单,多半是因为不小心把它隐藏了,可以使用 Host-Key + Home组合键呼出顶部菜单。默认的Host-Key键是右Ctrl键。


安装过程会提示“软件没有通过微软验证”,不管它,一路点击OK。

2. 设置共享文件夹
在菜单“设置”中,点击共享文件夹,添加


这里使用了自动挂载和完全操作权限,这样虚机启动后会自动装载该共享文件夹,并且主机和客机都对该共享文件夹有完全操作权限。


VirtualBox_002:Oracle VM VirtualBox 使用指南之二:安装虚机

运行环境:Oracle VirtualBox 4.2.12

选择菜单新建,开始创建虚机。








如果OS介质是在光盘上,选择所在光驱,然后启动就开始安装了。


如果是OS介质是.iso文件,在设置中,点击存储,

点击绿色的加号,增加一个虚拟光盘,点击选择磁盘:


指向OS所在的.iso文件,确定后,选择启动,开始从iso文件安装。

参考文献:
1. http://blog.csdn.net/tangyajun_168/article/details/7063448