2009年6月3日星期三

博客搬家通知

     终于还是对GFW忍无可忍了,不知道Blogger何时才能解封,已经不报有任何幻想,在购买了域名、空间并进行备案后,决心转到了自己的独立域名网站(www.eleven-china.com)上去了,新站点名字将仍叫Dive into Technology ,这边的内容也将于近期搬过去,这里不出意外的话以后将不再更新或定期批量更新,希望朋友们能一如既往地关注和支持:)。

2009年5月10日星期日

Oracle Application Development Framework 简介

1.1    概述

    Oracle Application Development Framework是Oracle Fusion中间件体系架构的重要组成部分,是一个端到端的J2EE开发框架。ADF框架通过提供实用级别的基础架构服务和虚拟的声明式开发体验,从而简化了J2EE开发。不同于其他开源开发框架,Oracle ADF提供了一个包含Business Service、Model、View、Controller的完整MVC实现,在企业应用架构的每一个层次都提供了支持。

1.2    Oracle ADF 11g框架简介

    ADF架构图:
    如架构图所示,Oracle ADF 11g分别提供了MVC架构中每一层次的实现,并允许在不同层次使用不同的技术。如可以使用ADF Business Components构件Business Service层,使用structs构建Controller层等。这里主要描述使用Oracle ADF对MVC框架的实现构建,即Business Service层使用ADF Business Component,Model层使用ADF Model,Controller层使用JSF,View层使用JSF/ADF Faces。

1.2.1    View 层

    最简单的View层实现即为一个包含用户界面组件的页面,并允许该页面中的组件调用Model层中的方法。Oracle ADF为了提高View层的重用程度,引入了如下结构体系。
  • Page
    使用JDeveloper构建的Page即为最终向用户展现的Web 页面,其为Oracle 富客户端组件构建的基础。
  • Page Fragment
    Page Fragment的创建与使用与Page极其相似,对于开发人员来说,在JDeveloper中开发一个Page与开发一个Page Fragment基本没有什么区别。Page与Page Fragment的主要区别在于,Page Fragment并不是最终向用户展现的页面,它可以作为一个Page的组件所存在。在通常的开发场景下,常遇到部分页面的某部分布局和业务逻辑固定不变的情况。在ADF中可将其开发为一个Page Fragment,并将其作为页面组件放入所需Page中,以提高页面的可重用程度。
    得益于Page Fragment的可重用特性,在常见的应用场景中,并不需要为一个业务流程创建一组页面,而只需创建针对该业务流程的一个页面以及与其相对的一组Page Fragment即可。
  • Page Template
    Page Template可以用于构建Page 或 Page Fragment,并可将其上放置的ADF富客户端组件与Model/Controller绑定以执行业务逻辑。Page Template主要用于用于快速构建一组具有相同部分的页面。Oracle ADF开发人员可以创建一个模板,并基于模板创建Page或Page Fragment。
    在常的应用场景下,可以通过Page Template构建某应用的页眉、页脚、导航栏等,而将页面内容的构筑交予其他开发人员,进一步降低开发耦合度,提高团队协作水平。
  • Region
    Region是Fusion架构体系中新引入的概念,可以被应用于Page或Page Fragment。可以将Page Fragment或Task Flow作为Region放入Page中,而Page Fragment、Task Flow、目标Page将分别使用独立的上下文配置。
    常见的使用场景是使用Region构建页面内容,以实现导航菜单与页面内容的松耦合设计。
  • Contextual Event Framework
    Oracle ADF为实现View的松耦合设计,允许通过Context对页面富客户端组件添加绑定,以调用Controller或Model(Bussiness Service Application Model)中的方法,实现MVC架构中的调用关系。这种基于上下文的事件处理架构即称为Contextual Event Framework。
  • ADF富客户端组件
    在Fusion架构体系中,ADF提供了超过100种富客户端组件供开发人员在Page或Page Fragment上创建富用户交互界面。所有组件均基于JSF及AJAX技术构建。包括:
    • 页面布局控件
    • 表/树控件
    • List of Value控件
    • 输入控件
    • 导航控件
    • 输出控件
    • 查询控件
    • 数据图表组件

1.2.2    Controller层

ADF Controller是ADF应用的重要组成部分,用于控制用户交互及程序流的执行。其核心为Task Flow,包含如下内容:
  • Task Flow
    Task Flow任务流供开发人员定义一组Page或Page Fragment之间的流程及逻辑关系。Task Flow中的对象可视为一组活动(activities),如每一个加入Task Flow中的Page或Page Fragment均被视为一个视图活动(View activity)。当用户在一个Task Flow中的各个页面间跳转时,即可视为在各视图活动之间的跳转。亦可在Task Flow中添加其它类型的活动。Oracle ADF已经将Task Flow中的各种活动包装为组件供用户调用。
    Task Flow在Oracle ADF中包含两种类型的实现:Bounded Task Flow/Unbounded Task Flow。
  • Bounded Task Flow
    Bounded Task Flow是Oracle ADF框架支持的两种Task Flow实现之一。该类型的Task Flow支持事务处理和作为Region重用,具体具有如下特性:
    • 定义明确的事务处理边界;
    • 单个Task Flow入口及零至多个定义明确的Task Flow出口;
    • 支持PageFlow级别的变量传递,可以将定义的变量在Task Flow的各个活动间传递;
    • 允许为该Task Flow创建新的实例;
    • 允许在退出该Task Flow之前执行commit或rollback的操作;
    • 当该类型Task Flow被其他Task Flow调用时,允许在进入该Task Flow时传入参数作为PageFlow级别的变量;
    • 当该类型Task Flow被其他Task Flow调用时,允许在退出该Task Flow时返回变量。
  • Unbounded Task Flow
    Unbounded Task Flow与Bounded Task Flow的主要区别在于Unbounded Task Flow没有定义明确的边界,不支持将某活动定义为Task Flow的入口,亦不可将其作为Page的Region使用。每一个ADF应用中只包含一个Unbounded Task Flow。典型的应用情境是使用Unbounded Task Flow构造导航栏,调用Bounded Task Flow。
    注意,若一个ADF应用中定义了多个Unbounded Task Flow文件,编译时会对Unbounded Task Flow进行合并。若一个Unbounded Task Flow中的视图活动(View activity)被设置为bookmarkable时,则可通过URL直接访问这个视图活动。
  • Task Flow Template
    Task Flow Template提供了重用一个或多个Bounded Task Flow的方法。这里的重用包含两种机制:复制(Copy)和引用(reference)。

1.2.3    Model层

    Model层提供了访问源数据及业务逻辑的功能。在Fusion架构中,将源数据的访问与业务逻辑的访问分为两部分:Data Control与Business Service。Data Control使用Databinding文件连接View层与Business Service/源数据,是Model层的主要组成部分;Business Service主要分为Application Module和ADF Business Components,通过Data Control连接至View层。这种分离式的架构允许在View层已经创建的前提下为View层添加数据访问或Business Service访问。
  • Model
    • Data Control
      Data Control用于为Page或Page Fragment创建数据访问界限(Data Bounds),可以实现为诸如XML文件、Web Service或ADF业务组件。使用JDeveloper进行开发,当创建Page或Page Fragment时,会自动创建对应的Data Control。
    • Placeholder Data Control
      当进行Page或Page Fragment的UI开发时专用的Data Control,用于在界面建立后连接数据源。
  • Business Service
    在Fusion体系架构中,ADF业务组件(ADF Business Component)是主要的数据访问和业务逻辑来源。ADF业务组件,亦称为ADF BC,主要由以下几个部分组成:
    • Application Module
      Application Module(AM)定义了一组可更新的数据模型及顶层的可调用方法(亦称为Service Methods),是用户界面进行数据操作的接口。AM中包含View Object对象。
      为了简化应用集成的复杂度,可以为Application Module启用服务接口。这里的服务接口指的是一组允许第三方应用程序使用标准Web Service协议完成查找、创建、更新、删除业务数据的任务的可编程API。所有的业务验证规则均被封装在Entity Object中,并会在进行业务数据操作时自动进行验证。
    • View Object
      View Object是一条SQL查询的持久化对象,允许对SQL查询的结果进行操作。同时,View Object间允许通过建立连接(link)的方式创建主从(Master-Detail)结构。当用户从用户界面改变数据时,View Object会将数据改变传递至Entity Object触发对应验证规则和保存改变。
    • Entity Object
      Entity Object是数据库中某张表的某个行的持久化对象。Entity Object内部通过DML操作将数据改变反映至数据库。Entity Object支持通过建立Association的方式进行Entity Object之间的关联,并支持通过此方式反映数据源层次的表关系,如数据表的外键关联。


2009年4月27日星期一

ADF Validator

    实际应用中经常遇到对业务相关的数据进行Validate的情况。比如对输入数据进行格式限制/范围限制等。ADF为开发人员提供了相对灵活的解决方案。这里提供了几种Validator的不同实现方式。

    (下述均使用Oracle XE自带的HR示例中Employees表作为示例表)
  1. 使用EO层次的Validator

    首先,ADF框架中所有的Validator默认都使用EO级别的Validator。EO层次可以设置Entity级别的Validator或Attributes级别的Validator。

    选择相应的级别,点击界面上的加号即可创建Validator,ADF提供了灵活的Validator以供选择


    选择需要的Validator Rule Type,写入相应的验证规则后,即可在Failure Handling中写入验证失败的抛出消息。在此处设置的消息将写入资源文件中。


    最后,当VO被拖入页面时,对应控件将自动包含一个<f:validator>子控件,其绑定为EO层次的Validator。


  2. 在ManageBean中实现Validator

    ADF本身提供的Validator相对比较灵活,但当EO的重用程度比较高时(如不同的VO使用相同的EO,需要设置不同验证规则/不同弹出消息,甚至不同页面使用相同VO,需要不同验证规则),EO级别的Validator显然不能满足要求。则可以通过重写Validator并添加至页面控件的绑定以实现。
    <f:validator>绑定的对象为一个属性,所以首先应为该Validator创建一个实现Validator接口的Class,最简单的如下(可以写入更复杂的验证逻辑)

    public class testValidator implements Validator {
    public void validate(FacesContext context, UIComponent component,
    Object obj) throws ValidatorException {
    throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_WARN, "TEST", "TEST MESG"));
    }
    }

    在MB中创建一个该属性的实例,并构造相应的get/set方法如下

        private testValidator test = new testValidator();

    public void setTest(testValidator test){
    this.test = test;
    }

    public testValidator getTest(){
    return this.test;
    }

    最后,通过EL表达式将该属性绑定至<f:validator>

    <f:validator bindings="#{backing_ExpsProYbudgetUpdate.test}">


2009年4月24日星期五

Oracle ADF VO排序及VO的查询模式

    常规应用中,当需要使用Table向终端用户展示数据时,Table中数据的显示排序一致性极大程度的影响到了客户体验。通常希望诸如多次查询结果显示顺序相同、插入数据在原数据上方等的实现。

    ADF为开发人员提供了两种级别的排序,即数据库级别的排序及内存级别(In-Memory)的排序。需要同时使用这两种排序法,才能取得合适的排序效果。
    (使用Oracle示例数据库HR中的Employees表作为示例)

  1. 数据源排序
    编辑VO的Query,设置Order by字段,这里设置Manager作为排序字段
    需要注意的是,这里的排序只是针对从数据源选取数据时的排序,当对VO进行插入/删除操作时,修改并不会立即Commit至数据库,而是存储在EO/VO中,这就导致这种排序法对于不在数据库中存在实体行的数据是无效的,也是需要使用In-Memory排序方式的原因。


  2. VO的查询模式(View Object's SQL Mode)
    在开始VO的In-Memory排序之前,首先应该了解VO的查询模式。
    ADF中一个VO具有如下查询模式
    • ViewObject.QUERY_MODE_SCAN_DATABASE_TABLES
      VO的默认查询模式,该模式下VO每次executeQuery时会从数据库检索数据

    • ViewObject.QUERY_MODE_SCAN_VIEW_ROWS
      检索已经存在于VO的Row set中的数据,允许在此查询模式下使用VO的In-Memory Filtering

    • ViewObject.QUERY_MODE_SCAN_ENTITY_ROWS
      检索存储于EO缓存中的数据

    可以使用setQueryMode()方法进行VO查询模式的设置。可以单独设置一个查询模式或使用Java的OR(|)设置多个查询模式。例如
    setQueryMode(ViewObject.QUERY_MODE_SCAN_DATABASE_TABLES | ViewObject.QUERY_MODE_SCAN_ENTITY_ROWS)

    当设置多个查询模式时,会自动跳过重复行。
    设置过查询模式后,当执行executeQuery()方法时,查询模式的设置即可生效。


  3. In-Mamory排序
    使用setSortBy()方法进行针对VO的运行时In-Memory排序。setSortBy()方法的输入参数类似于SQL中的order by子句,不同的是将列名称替换为VO上的Attribute Name。例如
    setSortBy("ManagerId desc, EmployeeId");

    根据查询模式的设置不同,当执行executeQuery()时,setSortBy()会自动转换为相应的排序方法。如当使用数据库检索时,会将setSortBy转换为ORDER BY子句,当使用In-Memory查询时,会将setSortBy转换为SortCriteria对象。
    使用示例:
    vo.setSortBy("ManagerId");
    vo.setQueryMode(ViewObject.QUERY_MODE_SCAN_VIEW_ROWS);
    vo.executeQuery();

    只需要在如插入数据时执行该语句,即可完成In-Memory的排序功能。


  4. 自定义VO的排序方法
    当需要的排序方法比较复杂,使用setSortBy设置排序规则无法满足需求时,可以通过重写VO的public void sortRows(Row[] rows)方法和public Comparator getRowComparator()方法达成目的。其中sortRows方法即是进行In-Memory排序时执行的排序方法;getRowComparater方法返回一个Comparator类型对象,被VO的compareTo()方法调用。

2009年4月20日星期一

Weblogic开启关闭


概述
近日由于项目需要,花了些时间部署了我们开发的demo环境到客户公司,客户公司使用的是IBM的AIX操作系统,由于是ADF应用,应用服务器也就毫无疑问地选择了Weblogic。而在应用部署过程中,总结了一些比较有用的Weblogic启动和关闭的脚本。

实现
1、使用相关Telnet工具登录到远程服务器,创建将要发布应用的domain,这里名为demo_domain。
2、正常启动Weblogic:
cd到/bea/user_projects/domains/demo_domain/bin
使用命令./startWebLogic.sh启动Weblogic
此时,Weblogic可以正常启动,但是当使用ctrl+c或关闭当前窗口后,Weblogic即立刻停止,显然在正式应用中这样的方式一般来说是不符合要求的。
3、后台启动(以服务方式启动)Weblogic:
为解决上面描述的问题,需要在后台启动Weblogic,也就是经常提到的以服务的方式启动Weblogic,将信息输出到文件中,这里输出到demo.log。
#nohup ./startWebLogic.sh >demo.log 2>&1 &
在执行上面的命令后,将返回一个进程号,可以记录该进程号,当Weblogic无法正常关闭时,可以通过命令行结束进程。
4、一般来说,启动Weblogic时一般需要输入用户名和密码,在这里假定用户名和密码都是weblogic。当后台启动Weblogic时,输出信息到提示输入用户名时即停止,无法继续正常开启。为解决这样的问题,可以通过修改相应的启动脚本。在这里,我修改了bin目录下的setDomainEnv.sh,添加了如下代码:
WLS_USER=weblogic
WLS_PW=weblogic
JAVA_OPTIONS=-Dfile.encoding=UTF-8
5、停止Weblogic:
./stopWebLogic.sh
有时使用此命令无法正常停止Weblogic,经测试,这种现象还是经常发生的,在出现该状况时需要强行停止该进程。
首先使用ps –ef|grep java查看当前进程,找到该进程,当然,如果刚才已经记录了进程编号,这里就无需再进行查询。
查到后使用 kill -9 进程编号 停止该进程。
有的操作系统用线程模拟进程,部分操作系统下面,Weblogic进程变现为多线程,需要用脚本杀死Weblogic多线程。
ps –ef|grep java |awk ‘{printf “kill -9 ”$2}’>killjava
sh killjava 就可以停掉Weblogic的java进程,但是需要注意该机器是否还有其他java进程,切记小心停错!

参考:http://cyr1974.itpub.net/post/2066/247321
http://liwp_stephen.itpub.net/post/3553/24829
http://www.lupaworld.com/bbs/thread-14112-1-1.html

ADF实现树状查询

最近项目遇到要用start with connect by做树状查询,即:选择一个部门老大,要把老大下面的小老大和各个小弟全查出来,ADF貌似没有提供这类查询(至少我没发现,呵呵,如果有那就更好了,可以不给下面看了),这个办法不一定最好,但是,绝对可以实现,~~
1.数据库里面表的结构示例:
user(userid username PanrentId)
2.建VO的sql
2_1)如果你能知道你要查询的最顶层user的userid,那么你可以这样建VO,绑定一个参数p_userId,默认值就是最顶层的user的userid;VO上面的sql为:
select * from user start with userid=:p_userId connect by prior parent_id = user_id
2_2)如果你不知道你要查询的最顶层user的userid,那么你可以这样建VO,绑定一个参数p_userId,默认值就是最顶层的user的userid;VO上面的sql为:
select * from user start with userid in (select userid from (select userid, 1 flag from users union all select :p_userId, 2 flag from dual) where flag = decode(:p_userId, null, 1, 2) ) connect by prior parent_id = user_id

这样,如果用户没有输入查询参数,查出来的就是全部;如果用户输入了查询参数,把用户输入的userid在manageBena或者vo实现类里面赋值给VO上面的绑定参数

解决ORA-00020错误

项目上使用的Oracle服务器经常出现无法登陆的情况,尝试查看数据库dump文件,没有发现在无法登陆时的报错信息,使用SecureCRT连接服务器登录sqlplus时出现错误"ORA-00020: maximum number of processes (150) exceeded",解决方法如下:

开始时怀疑数据库死锁,通常这种情况下可以通过查询下列表/视图,杀死死锁的进程解决:
V$LOCK列出当前Oracle持有的锁和未解决的锁请求
V$SESSION列出当前连接到数据库的Session信息
DBA_BLOCKERS显示锁住对象的会话
DBA_WAITERS显示等待被锁住对象的会话
DBA_DDL_LOCKS列出所有DDL锁和未解决的DDL锁请求
DBA_DML_LOCKS列出所有DML锁和未解决的DML锁请求
DBA_LOCK列出所有锁和latch,以及所有未解决的锁请求
DBA_LOCK_INTERNAL每个锁或latch显示一行,每个未解决的锁请求显示一行


通过查询V$SESSION视图,发现连接总量已达到上限150,由于在做Oracle ADF开发,该OracleDB作为开发数据库,连接较多属于允许范围内,遂增大最大连接数,修改对应init.ora文件中"procdsses=150",重启DB。

10g里面如此修改无效,可以通过sqlplus连接至数据库
SQL> show parameter processes; //查看processes连接数设置
SQL> show parameter sessions; //查看sessions连接数设置
SQL> alter system set processes=300 scope=spfile; //设置processes连接数
不需要设置sessions连接数,sessions连接数为processes * 1.1 + 5

重启DB,问题解决。

附:用于确定锁住数据库对象的锁的SQL

select c.owner,
c.object_name,
c.object_type,
b.sid,
b.serial#,
b.status,
b.osuser,
b.machine   
from v$locked_object a,
v$session b,
dba_objects c   
where b.sid = a.session_id   
and a.object_id = c.object_id;

项目中遇到的一个动态查询

需求: sql 伪代码:select * from users where user_id in (select id from 如果参数flag=1 ,那么表名字为table1 ;如果参数flag=2,那么table的名字就为table2) 转换为2个sql即:如果flag=1,sql为:select * from users where user_id in (select id from table1); 如果flag=2,sql为:select * from users where user_id in (select id from table2);

实现: select * from users where user_id in
( select id from
( select t1.id, 1 flag from table1 union all select t2.id, 2 flag from table2
)where flag = decode (&flag,1,1,2,2)
);

2009年4月19日星期日

windows下安装基于Apache的Subversion服务器


在Windows下安装最为方便的办法就是下载安装程序包,详细过程如下:
1.安装程序与下载说明
Apache Web服务器 :http://httpd.apache.org/download.cgi
(Apache2.0.X版,我用的是2.0.59版;用2.2.X在windows下配置文件修改后无法启动!)
Subversion :(http://subversion.tigris.org/files/documents/15/35379/svn-1.4.2-setup.exe)
TortoiseSVN 客户端工具 (TortoiseSVN-1.4.1.7992-win32-svn-1.4.2.msi)
网络上比较多,这里就不给出。
2. 程序安装
(1)Apache2.0.63安装
防止安装有问题,如果机器上装有IIS,在安装前我先将IIS端口改为8080,然后按提示安装完成
打开 http://loccalhost,如果能出现内容,那你的Apache就安装成功了。
注:如果你将Apache安装成一个服务,它将以本地system帐户来运行。为Apache创建一个单独的用户来运行它将会更安全一些。由于常用的端口号冲突比较多,可以将Apache的端口号该为其他,比如81等。
请确保运行Apache的用户有版本库的完全访问权限(右键版本库目录->属性->安全)。要不然,用户将无法提交他们的更改。就算Apache以本机system来运行,你也要设置它能完全读写版本库目录。
如果没有为Apache配置这个许可,用户将会得到"禁止访问"的错误信息,在Apache的错误日志中表现为错误500。

(2)运行Subversion安装程序,如果安装程序能够识别你已经安装了Apache,那么你的安装基本上就OK了。如果它不能(我的机器未能识别),那么你需要做以下手工配置:
注:如果你先安装Apache,再安装Subversion,正常情况下下面的工作(2.3配置前)Subversion安装程序已经帮你完成了使用资源管理器,进入Sibversion的安装目录(通常为c:\program files\Subversion),找到文件 httpd/mod_dav_svn.so和mod_authz_svn.so,将它们拷贝到Apache的modules目录(通常为c:\ program files\apache group\apache2\modules)。

从Subversion的安装目录拷贝文件libdb43.dll到Apache的modules目录。

使用如记事本之类的文本编辑器编辑Apache的配置文件(通常为c:\program files\apache group\apache2\conf\httd.conf),修改以下内容:

去掉以下行的注释(将开头的#删除):

#LoadModule dav_fs_module modules/mod_dav_fs.so
#LoadModule dav_module modules/mod_dav.so

在LoadModule节的最后添加以下两行:
LoadModule dav_svn_module modules/mod_dav_svn.so
LoadModule authz_svn_module modules/mod_authz_svn.so
在配置文件的最后添加下面这些行:

<Location /svn>
DAV svn
SVNParentPath D:\SVN
AuthType Basic
AuthName "Subversion repositories"
AuthUserFile D:\passwd
#AuthzSVNAccessFile D:\svnaccessfile
Require valid-user
</Location>
配置表示:你所有的版本库将位于D:\SVN目录下,要访问你的版本库可以使用这样的
URL:http://YourIP/svn/,访问权限将由passwd文件中的用户名/密码来限制。
详细说明如下:
---------------------------------------------------------------------------
<Location /svn>
意味着可以通过像这样的URL(http://MyServer/svn)来访问Subversion版本库
DAV svn
告诉Apache哪个模块负责服务像那样的URL--在这里就是Subversion模块
SVNListParentPath on
在Subversion 1.3及更高版本中,这个指示器使得Subversion列出由SVNParentPath指定的目录下所有的版本库
SVNParentPath D:\SVN
告诉Subversion在目录D:\SVN下寻找版本库 #(修改成你自己的目录)
AuthType Basic
启用基本的验证,比如用户名/密码
AuthName "Subversion repositories"
当一个验证对话框弹出时,告诉用户这个验证是用来做什么的
AuthUserFile D:\passwd #(密码文件,不是文件夹哦)
指定D:\passwd用为密码文件用来验证用户的用户名及密码
AuthzSVNAccessFile D:\svnaccessfile
指定D:\svnaccessfile来限定各个用户或组在版本库中目录的访问权限
Require valid-user
限定用户只有输入正确的用户名及密码后才能访问这个路径
---------------------------------------------------------------------------
创建passwd文件
打开命令行(DOS窗口CMD),
将当前目录切换到apache2目录(通常为c:\program files\apache group\apache2\bin),
输入命令:
htpasswd.exe -c passwd username
Automatically using MD5 format.
New password: ****** (输入密码)
Re-type new password: ******
Adding password for user username
copy passwd d:\ (Httpd.conf 中设定的是在D:)
重启Apache服务。
重启后打开http://YourIP/svn/ 输入密码后,还是出错(因为还没有建立版本库)
Forbidden
You don't have permission to access /svn on this server.
--------------------------------------------------------------------------------
Apache/2.0.59 (Win32) SVN/1.4.2 DAV/2 Server at 192.168.18.8 Port 80
(3)TortoiseSVN
按提示安装完成,需重启电脑
3.SVN项目设置
(1).打开资源管理器,在D:\SVN目录下新建WWW目录
(2).在WWW文件夹上右击TortoiseSVN--->Create repository here...
(也可以通过命令的方式:到SVN的安装BIN目录。运行:svnadmin create D:\svn\www
d:\svn目录需先建好,要不会出错:
svnadmin: 档案库创建失败
svnadmin: 无法创建最上层目录
(3).然后选择Native filesystem(FSFS)
(4).到项目文件夹上,右击TortoiseSVN-->import...
在URL of repository:中输入http://YourIP/svn/www
(5).项目成员,可在自己机上新建一个空的项目文件夹.右击SVN checkout...
在提示的用户名和密码对话框中输入服务端配置的用户名和密码;然后点击OK;后面对话框中即会显示逐个加入的文件和目录信息,最后一条显示“Completed At Version 1”。
如果成功,可以在本地目录下看到相应的代码文件。选中文件或目录,可以进行“SVN Update”(从服务器更新版本)和“Svn Commit”(提交更改到服务器)操作。
另外,如果使用的是Netbeans等集成开发环境,里面有可能会带有SVN的管理工具,或者支持SVN,可以通过这些开发环境直接将项目导入到svn中。在这里,我将附上我的一些配置文件:

passwd:
eleven:******
meteor:******
syani:******
evergreen:******
其中××××代表密码
[groups]
admin = eleven
developteam = syani, meteor, evergreen

svnaccessfile:
#管理员和开发人员都具有读写权限
[/]
@admin = rw
danger =
@developteam = rw

2009年4月18日星期六

实现ADF应用集成

概述
本文主要描述在ADF开发中如果实现不同应用之间的集成。
场景:
在开发过程中,为了降低开发难度和风险,有时会采用在基础框架应用外新建应用进行开发,待新应用开发完成后,再将其集成到基础框架应用中。这里将以howtoappsintegration1和howtoappsintegration2两个应用为例,说明如何实现系统之间的集成。

实现
1、分别创建新应用howtoappsintegration1和howtoappsintegration2,两个应用都有Model项目和ViewController项目。
2、我们将howtoappsintegration2应用集成到howtoappsintegration1应用中。在howtoappsintegration1应用中打开howtoappsintegration2应用的Model项目,这里名为Model2。此时,在howtoappsintegration1应用中,应该包含Model,Model2和ViewController三个项目,由于在一个应用中只能包含一个ViewController项目(多个ViewController项目Runtime时会报异常)。
3、修改howtoappsintegration1中的ViewController项目的Dependencies,同时依赖于Model和Model2两个项目,在Data Control面板中可以看到包含两个Data Control。



4、在完成了上述步骤后,我们就可以开始将ViewController项目进行合并。打开两个应用所在的文件夹,首先拷贝howtoappsintegration2应用的task flow到howtoappsintegration1的对应目录下(一般task flow存在于public_html中的WEB-INF文件夹中),在这里为app2-task-flow.xml。
5、在Jdeveloper中刷新Application Navigator面板,可以看到该task flow已经存在于对应目录下,打开该task flow。发现该task flow中的页面包含叉号,说明页面出错或不存在,这里是由于不存在,点开该task flow的source,查看该页面在howtoappsintegration2应用的存放目录。






<?xml version="1.0" encoding="UTF-8" ?>


<adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">


<task-flow-definition id="app2-task-flow">


<default-activity>Jobs</default-activity>


<view id="Jobs">


<page>/Jobs.jspx</page>


</view>


</task-flow-definition>

</adfc-config>


6、通过步骤5我们发现该页面直接存在于其public_html目录下,拷贝该页面到howtoappsintegration1应用中,在这里也拷贝到了public_html目录下。完成后刷新Jdeveloper的刷新Application Navigator面板,此时再次查看app2-task-flow.xml的source,发现错误消失。
7、在howtoappsintegration1应用中打开上步中拷进来的页面,即Jobs.jspx,点击其Source,发现有非常多的警告,查看后发现,确实了页面的绑定信息,即缺少了页面对应的PageDef文件(如果页面使用了模板,还需要根据实际情况修改模板信息或删除模板)。
8、找到howtoappsintegration2应用中Jobs.jspx页面绑定文件,一般在adfmsrc目录下,这里名为JobsPageDef.xml,将其拷贝到howtoappsintegration1应用的对应目录下。完成后刷新Jdeveloper的刷新Application Navigator面板,此时再次查看Jobs.jspx页面的source,发现警告没有消失,此时虽然PageDef已经存在于该应用中,但是由于没有在DataBindings.cpx中指定页面和页面绑定信息的关联信息,因此页面仍然无法找到其绑定信息。
9、在howtoappsintegration1应用中打开DataBindings.cpx文件,我们直接修改其Source。需要修改的地方主要有:
(1)dataControlUsages:定义dataControl信息
(2)pageDefinitionUsages:定义页面绑定信息
(3)pageMap:定义页面和页面绑定的关联信息
在这里将该文件进行如下修改:







<?xml version="1.0" encoding="UTF-8" ?>


<Application xmlns="http://xmlns.oracle.com/adfm/application" version="11.1.1.51.88" id="DataBindings"


SeparateXMLFiles="false" Package="howto.howtoappsintegration1.view" ClientType="Generic">


<pageMap>


<page path="/Employees.jspx" usageId="howto_howtoappsintegration1_view_EmployeesPageDef"/>


<page path="/Jobs.jspx" usageId="howto_howtoappsintegration1_view_JobsPageDef"/>


</pageMap>


<pageDefinitionUsages>


<page id="howto_howtoappsintegration1_view_EmployeesPageDef"


path="howto.howtoappsintegration1.view.pageDefs.EmployeesPageDef"/>


<page id="howto_howtoappsintegration1_view_JobsPageDef"


path="howto.howtoappsintegration1.view.pageDefs.JobsPageDef"/>


</pageDefinitionUsages>


<dataControlUsages>


<BC4JDataControl id="AppModuleDataControl" Package="howto.howtoappsintegration1.model.services"


FactoryClass="oracle.adf.model.bc4j.DataControlFactoryImpl" SupportsTransactions="true"


SupportsFindMode="true" SupportsRangesize="true" SupportsResetState="true"


SupportsSortCollection="true" Configuration="AppModuleLocal" syncMode="Immediate"


xmlns="http://xmlns.oracle.com/adfm/datacontrol"/>


<BC4JDataControl id="AppModuleDataControl1" Package="howto.howtoappsintegration2.model.services"


FactoryClass="oracle.adf.model.bc4j.DataControlFactoryImpl" SupportsTransactions="true"


SupportsFindMode="true" SupportsRangesize="true" SupportsResetState="true"


SupportsSortCollection="true" Configuration="AppModuleLocal" syncMode="Immediate"


xmlns="http://xmlns.oracle.com/adfm/datacontrol"/>


</dataControlUsages>

</Application>


10、保存,在howtoappsintegration1应用中再次打开Jobs.jspx页面,点击查看source,发现页面绑定信息已经找到(或者通过点击Binding,通过关联链接进而点击查看PageDef信息)。



11、虽然页面文件已经找到,但是在页面的source中仍然存在一些警告信息,这是由于该页面中的属性绑定到了backing scope的managed bean(或称为backingbean)上,此时此managed bean尚未存在于该应用中。根据source中managed bean的名称在howtoappsintegration2应用中的adfc-config.xml中找到该managed bean的路径和名称,将其拷贝到howtoappsintegration1应用的对应目录下(一般存在于src目录下),此时刷新Application Navigator后,查看到该managed bean,务必选中该manged bean,右键点击rebuild,否则在下步中将有可能无法看到该类(如果package的路径不同,有可能需要打开代码手动修改该路径,修改后重新rebuild)。

12、完成后将其按照source中的名字定义到howtoappsintegration1应用中的adfc-config.xml中,这里名为backing_Jobs,Scope为backing scope。



13、此时再次查看howtoappsintegration1应用中的Jobs.jspx的source,发现绑定到managed bean中属性的信息上的警告消失,此时所有拷贝和修改已经基本完成。
14、保存应用,运行拷贝进来的task flow,进行测试。

最终运行页面:




总结:
应用集成是一个看似容易却相对琐碎和比较容易出问题的环节,所以在进行应用集成的过程中,尽量按照步骤,按部就按地进行,切忌将所有的内容全部拷贝然后再去修改配置,如果是这样的话,出了问题就将非常难以排查,尤其是在需要多人协同开发的项目上(这里建议的最佳实践就是由专人负责应用集成)。一般而言,只要细心,按照步骤进行,是不会有问题的。下面是在集成过程中需要特别注意的几点:
(1)文件一定要拷贝完整
(2)注意library是否正确,数据库连接是否正确
(3)DataBinding.cpx文件修改一定要小心,根据需要注册Data Control到该文件
(4)Managed Bean拷贝完成后一定要注意其Package路径,并进行rebuild
(5)在最终集成到的应用中,一定只能包含一个ViewController项目(Model项目可以有多个)。
(6)如果task flow中有用到了method call,有可能要根据实际情况重新拖拽。


相关代码请查看:
howtoappsintegration.rar

将ADF应用部署到Weblogic


概述
在Jdeveloper 11g中,Oracle已经将Weblogic集成到了IDE中,在使用Jdeveloper 11g进行ADF的开发时,Weblogic也是其默认的部署和运行服务器。同时,在实际应用中,有很多情况下,也需要将ADF部署到Weblogic上,本文主要描述在ADF开发中如何将ADF应用部署到Weblogc。
约定:
1、 Weblogic的域已经存在或使用Jdeveloper默认的Weblogic的域
2、 Weblogic的域在创建时,在“选择域源”步骤中,在“生成一个自动部署的域以支持下列产品”项中勾选了“Application Development Framework”。
3、 将要发布到Weblogic上的应用已经成功创建并完成。
下面我们将部署howtodeploytoweblogic应用到weblogic,这里使用Jdeveloper默认的域。这里仅为部署步骤简介,详细的说明参见文档(Page963 b31974.pdf 第32章 )。

实现
1、 启动Weblogic。如果是通过Jdeveloper打开,可以点击Run-Start Server Instance,若不是通过Jdeveloper打开,可以通过运行脚本。
2、 选择Model项目,双击打开Application Module,点击Configurations,继续选中Local的Configuration,将Connection Type设置成为JDBC DataSource,选择后Datasource Name为java:comp/env/jdbc/howtoDS。



3、 选择ViewController项目,双击点开Project Setting,选择Deployment,删除已经存在的Deployment Profiles,点击New,选择Archive Type为War File,Name为test。



4、 创建WAR后,在Edit War Deployment Profile Properties页面中,选择Web Application’s Context Root为Specify Java EE Context Root,设置为test,点击OK完成。



5、 在应用上点击右键,选择New,在Deployment Profiles的Items中,可以看到只有EAR可用,选择EAR,命名为howtodeploytoweblogic。



6、 在Edit Ear Deployment Profile Properties页面中,选择Application Assembly,选中ViewController.jpr下的test,点击OK完成。



7、 删除原有的Ear,确保此时Deployment Profiles中只有howtodeploytoweblogic,点击OK完成。



8、 在应用上点击右键,点击Deploy,选择howtodeploytoweblogic,进一步选择to EAR file。



9、 发布完成以后记录此时EAR包所在路径,同时登陆Weblogic的管理页面,这里使用Jdeveloper默认域,用户名和密码均为weblogic,url为http://127.0.0.1:7101/console/
10、登录成功后,首先为应用创建数据源,在域结构中点击 服务-JDBC-数据源,选择新建,进入数据源向导。
10.1、点击新建,名称为howtodeploytoweblogic,JNDI的设置需要和步骤2中的Datasource保持一致,这里为jdbc/howtoDS,数据库类型选择Oracle,驱动使用Oracle’s Driver(Thin) Versions:9.0.1,9.2.0,10,11。
10.2、点击下一步到连接属性设置,完成数据库连接属性,点击下一步显示所有的连接属性,点击测试配置,确定连接测试成功。
10.3、继续点击下一步到选择目标,勾选将要应用该数据源的服务器,然后点击完成,消息提示“已激活更改,不需要重新启动”。



10.4、选中刚创建成功的数据源,点击 配置-连接缓冲池,选择高级,勾选保留时测试连接项,继续选择监视,测试数据源,若测试成功,说明数据源配置成功。至此,数据源配置完成。



11、登录成功后在域结构中点击部署,此时在右边可以看到所有已部署在服务器上的应用和库。点击安装,进入安装向导。
11.1、输入刚才EAR包的路径,选中该EAR。



11.2、点击下一步,Weblogic会自动判断将要部署的内容为应用还是库,这里为应用。
11.3、继续点击下一步,根据需要进行设置,这里使用默认设置,直接点击完成。提示部署成功,即可通过浏览器进行访问。url为http://127.0.0.1:7101/test/faces/Main.jspx需要注意的是,这里url地址中的test,对应的是在设置war profiles中的Web Context Root。


最终运行结果:


相关代码请查看:
howtodeploytoweblogic.rar

2009年4月17日星期五

复合LOV的实现


概述

在实际应用中,经常会用到对于同一个属性使用不同LOV,本技巧主要介绍了对于同一属性使用多个LOV的方法,以及不同LOV之间切换的方法。
场景:
1、 定义新员工时,根据员工学历的不同,使用不同LOV选择起薪范围
2、 根据职位不同,LOV中显示的内容也不同或需使用不同LOV显示内容
3、 其他应用场景
下面我们以EmployeesVO中的DepartmentID属性为例,根据员工Salary的不同为该属性添加使用不同LOV,以显示不同内容。

实现
1、创建新应用后,创建Entity Object:EmployeesEO,该EO基于数据库中的Employees表,可以看到其关联(EmpManagerFkAssoc)被自动创建。
2、创建基于1中创建的EO的View Object:EmployeesVO。
3、创建名为EmpManagerFkLink的View Link,在“Selected Source Attribute”和“Selected Destination Attribute”中都选择EmployeesVO中的EmpManagerFkAssoc,点击“OK”完成。



4、创建DepartmentId的LOV数据源VO(DepartmentLOV),并设置DepartmentId属性为Key Attibute,其SQL如下:
SELECT Departments.DEPARTMENT_ID,
Departments.DEPARTMENT_NAME,
Departments.MANAGER_ID,
Departments.LOCATION_ID
FROM DEPARTMENTS Departments


5、为EmployeesVO中的DepartmentId属性添加两个LOV,其List Data Source均选择为前面创建的DepartmentsLOV(DepartmentsLOV1),List Attribute为DepartmentId,List Return Values使用默认,即DepartmentId-DepartmentId,在UI Hints中设置不同的显示项,分别如下:
LOV_DepartmentId:



LOV_DepartmentId1:



6、为DepartmentId属性添加LOV Swicher,单击选中该属性,在“List of Values: DepartmentId”中设置List Type UI Hint为Input Text with List of Values,在List of Values Switcher点击新建,命名为lovSwitcher,在“Edit Attribute: lovSwitcher”面板中选择Value Type为Expression,添加Value内容,即Groovy脚本,以判断使用哪个LOV,在这里根据Salary进行判定(若Salary>10000,使用LOV_DepartmentId1,若Salary<=10000,使用LOV_DepartmentId)。
Value:
if(Salary != null && (new oracle.jbo.domain.Number(Salary)).intValue() > 10000)
{
return 'LOV_DepartmentId1'
}
return 'LOV_DepartmentId'



7、新建页面,命名为Main,为方便扩展功能,产生出了其Backing Scope的Managed Bean。
7.1、选中并点开“Data Controls”中“AppModuleDataControl”内的EmployeesVO2(与AM中名字相同,不一定为该名),继续点击“Named Criteria”,将All Queriable Attributes拖动到页面中,选择ADF Query Panel with Table,根据需要调整显示属性,完成后确定。



7.2、在“Structure”面板中选择af:table – redId1,双击打开其属性编辑器,选择“Columns”项设置所有属性(除lovSwitcher)的“Component To Use”为输入类型控件。
7.3、保存设置,运行页面。


最终运行结果:
在Search区中在Salary选项中填入9999(<10000),DepartmentId此时使用的LOV是LOV_DepartmentId,显示DepartmentId和DepartmentName两项。


更改Salary为10001,重新查看DepartmentId,发现其显示列数分别为DepartmentId,DepartmentName与ManageId,使用的LOV为LOV_DepartmentId1。


清空Search中内容,使用默认空值进行查询,在Table中继续验证。


相关代码请查看:
howtomultiplelov.rar

ADF 11g Multiple RowSelect on RichTable

在实际应用中,经常遇到需要在表格中选定一条或多条记录进行操作的情况。
  1. 使用ADF RichTable提供的Multiple RowSelect功能

  2. 在RichTable中增加列,使用基于VO的Checkbox进行RichTable的行多选

  3. 在RichTable中增加列,不基于VO进行RichTable的行多选

下面将详述三种实现方式,使用Oracle JDeveloper 11.1.1.0.1,数据源使用OracleXE版本中HR示例数据的Employees表,创建页面时默认绑定backingbean。

1.使用ADF RichTable提供的Multiple RowSelect
优点:RichTable原生支持,稳定快速,代码简单
缺点:只能按住键盘Ctrl键或Shift键通过鼠标进行多选,用户操作不直观
步骤:设置RichTable的属性,将RowSelection设为multiple,并将SelectedRowKeys设为空,即可在后台通过如下代码获得选定行的RowKey,进而可以通过在VO上通过对应RowKey得到对应行。

    public Boolean getIsChecked() {
//return (Boolean) getAttributeInternal(ISCHECKED);
return getAttributeInternal(ISCHECKED) == null ? false :
(Boolean)getAttributeInternal(ISCHECKED);
}


补充:为什么将SelectedRowKeys置为空
修改之前SelectedRowKeys值为#{bindings.EmployeesVO1.collectionModel.selectedRow},该EL表达式表明之前该Table的SelectedRowKeys绑定到了对应VO的selectedRow,但client端的状态改变并不能实时的提交到Server端,以至于当此绑定存在时,取到的是Server端存储的selectedRow而非client端当前的selectedrow。
2.在RichTable中增加列,使用基于VO的Checkbox进行RichTable的行多选
优点:使用checkbox的方式提供选择,代码复杂度中等
缺点:必须修改每一个需要使用该多选方式的RichTable对应的VO
步骤:
  1. 在VO添加非数据库项IsChecked,设置其Type为Boolean,Updateable为Always


  2. 为该VO生成ViewRowImpl.java,并修改IsChecked的get方法

        public Boolean getIsChecked() {
    //return (Boolean) getAttributeInternal(ISCHECKED);
    return getAttributeInternal(ISCHECKED) == null ? false :
    (Boolean)getAttributeInternal(ISCHECKED);
    }


  3. 将VO拖入页面创建RichTable,修改IsChecked列,将标签改为<af:selectBooleanCheckbox>,并将其Autosubmit/Immediate属性设为true,并在header中拖入另一个SelectBooleanCheckbox


  4. header里的Checkbox不能添加ValueChangeListener,如果已经添加,需要将ValueChangeListener重置为Default(空)。为Header里的Checkbox创建ClientListener和ServerListener,分别设置属性如下:
    ClientListener
    method: mainClientCall
    type: click

    ServerListener
    method: callMBmainmethod
    type: onSelectedOrDeselected()

    完成后如下





  5. 在页面转入Source模式,在标签 <af:document... <af:messages... 间加入如下代码

          <script type="text/javascript">
    <![CDATA[function mainClientCall (event)
    {
    var currcheckbox = event.getSource();
    var selectstatus = currcheckbox.getValue();
    //call method in manage bean
    AdfCustomEvent.queue(currcheckbox, "callMBmainmethod",{params:selectstatus}, true);
    }
    ]]>
    </script>




  6. 在页面backingbean中修改onSelectedOrDeselected方法,添加以下代码以实现全选功能。注意修改this.getTable()为你的目标表。
        public void onSelectedOrDeselected(ClientEvent clientEvent) {
    boolean isSelected =
    (Boolean)clientEvent.getParameters().get("params");
    RichTable rt = this.getTable1();
    for (int i = 0; i < rt.getRowCount(); i++) {
    JUCtrlHierNodeBinding rowData =
    (JUCtrlHierNodeBinding)rt.getRowData(i);
    Row row = rowData.getRow();
    if (isSelected) {
    row.setAttribute("IsChecked", true);
    } else {
    row.setAttribute("IsChecked", false);
    }
    }
    // Refresh target table state
    AdfFacesContext adfContext = AdfFacesContext.getCurrentInstance();
    UIComponent refreshComponent = this.getTable1();
    if (refreshComponent != null) {
    adfContext.addPartialTarget(refreshComponent);
    }
    }


  7. 可以在AppModel或ManageBean中通过遍历VO判断IsChecked的状态来取出选定行。


3.在RichTable中增加列,不基于VO进行RichTable的行多选
优点:使用checkbox的方式提供选择,
不必修改VO
缺点:
代码复杂度较高
步骤:

  1. RichTable中插入列,设置宽度(width)为13RowHeader属性为trueAlign属性为Center;在列及列header中分别拖入SelectBooleanCheckBox,设置checkboxsimple属性为true,删除其Text属性中的值。将全选checkboxID设置为maincheckbox,单选的checkbox
    ID
    设置为subcheckbox:(参考2.3)

  2. 分别为两个Checkbox创建一组ClientListener/ServerListener,属性设置如下(参考2.4)
    MainCheckbox
    clientListener
    Method: mainClientCall
    Type: click
    serverListener
    Type: callMBmainmethod
    Method: mainCheckBoxChecked()
    SubCheckbox
    clientListener
    Method: subClientCall
    Type: click
    serverListener
    Type: callMBsubmethod
    Method: subCheckBoxChecked()


  3. 在页面转入source模式,复制如下代码至<af: document ...<af: messages ...标签之间(参考2.5)
          <script type="text/javascript">
    <![CDATA[function mainClientCall (event)
    {
    var currcheckbox = event.getSource();
    var selectstatus = currcheckbox.getValue();

    //call method in manage bean
    AdfCustomEvent.queue(currcheckbox, "callMBmainmethod",{params:selectstatus}, true);
    }
    function subClientCall (event)
    {
    var currcheckbox = event.getSource();
    var selectstatus = currcheckbox.getValue();

    //call method in manage bean
    AdfCustomEvent.queue(currcheckbox, "callMBsubmethod",{params:selectstatus}, true);
    }
    ]]>
    </script>


  4. 修改页面对应Manage
    bean
    中的方法,修改mainCheckBoxChecked()subCheckBoxChecked()方法,添加如下代码
        public void getSelectedRows(ActionEvent actionEvent) {
    System.out.println("Selected Rows"); //debug
    System.out.println("******************************************"); //debug
    RowKeySet selectedRowsList = this.getMultipleCheckBoxProcess().getSelectedRowsList(this.getResId1());
    if (selectedRowsList != null && selectedRowsList.size() != 0) {
    for (Object temp : selectedRowsList)
    System.out.println("Get Row : " + temp);
    }
    System.out.println("******************************************"); //debug
    }
    public void CleanUpStatus(ActionEvent actionEvent) {
    this.getMultipleCheckBoxProcess().CleanUpStatus(this.getResId1(), "maincheckbox", "subcheckbox");
    }
    public void mainCheckBoxChecked(ClientEvent clientEvent) {
    Boolean currstatus = (Boolean)clientEvent.getParameters().get("params");
    this.getMultipleCheckBoxProcess().mainCheckBoxChecked(this.getResId1(), "subcheckbox", currstatus);
    }
    public void subCheckBoxChecked(ClientEvent clientEvent) {
    Boolean currstatus = (Boolean)clientEvent.getParameters().get("params");
    this.getMultipleCheckBoxProcess().subCheckBoxChecked(this.getResId1(), currstatus);
    }
    public MultipleCheckBox getMultipleCheckBoxProcess(){
    FacesContext ctx = FacesContext.getCurrentInstance();
    Application app = ctx.getApplication();

    MultipleCheckBox curr = (MultipleCheckBox)app.evaluateExpressionGet(ctx, "#{ viewScope.view_MultipleCheckbox}", Object.class);
    return curr;
    }


  5. 复制MultipleCheckbox.java至目标位置(在Demo中可以找到),在当前所使用的TaskFlow中添加Manage Bean名为view_MultipleCheckbox,Scope为view级别的Bean,指向MultipleCheckbox.java,修改MultipleCheckbox.java中的Package,使其与实际路径相符。


  6. 注意在适当的时候调用MultipleCheckbox的CleanUpStatus方法,一般在RichTable中数据发生改变时需要调用,如页面存在Query,则需在Query中调用MultipleCheckbox的CleanUpStatus方法。



补充:MultipleCheckBox中方法说明
  • public RowKeySet
    getSelectedRowsList(RichTable tartab);
    输入参数1:使用多选择CheckboxRichTable对象
    返回参数:选中的行的RowKey

  • public void setSelectedRowsList(RowKeySet
    selectedRowsList, RichTable tartab);
    输入参数1:选中行的RowKey
    输入参数2:使用多选择CheckboxRichTable对象

  • public void subCheckBoxChecked(RichTable
    tartab, Boolean checkStatus);
    输入参数1:使用多选择CheckboxRichTable对象
    输入参数2:当前Checkbox的状态
    该方法应在多Checkbox RichTable中某行前的Checkbox被选中/反选时调用

  • public void mainCheckBoxChecked(RichTable
    tartab, String subcheckboxid, Boolean newstatus);
    输入参数1:使用多选择CheckboxRichTable对象
    输入参数2:每行前Checkboxid
    输入参数3:当前全选Checkbox的状态
    该方法应在全选Checkbox被选中/反选时调用

  • public void CleanUpStatus(RichTable
    tartab, String maincheckboxid, String subcheckboxid);
    输入参数1:使用多选择CheckboxRichTable对象
    输入参数2:全选Checkboxid
    输入参数3:每行前Checkboxid
    该方法用于清除Checkbox的被选状态,通常需要执行查询前均需调用

Demo:
MultipleRowSelect1.rar

MultipleRowSelect2.rar

MultipleRowSelect3.rar

2009年4月16日星期四

Oracle SQL常用函数总结(2)


31、Tanh(arg)
说明:返回双曲线的正切值
例:Tanh(20) 结果为 1

32、Sysdate
说明:取系统当前时间
例:SELECT SYSDATE FROM DUAL;

33、Greatest
说明:取一组数中的最大值,比较字符的编码大小
例:Greatest('A','C','B') 结果为C

34、Least
说明:取一组数中的最小值,比较字符的编码大小
例:Least('A','C','B') 结果为A

35、To_Number
说明:将字符串转换为数字
例:To_Number(‘12345’) 结果为 12345
补充:这里的字符串必须由数字组成,否则不能转化

36、Last_Day(Sysdate)
说明:返回该月的最后一天的当前时刻
例:Last_Day(Sysdate) 结果为 2008-10-31 15:28:20

37、To_Char(date,’format’)
说明:将时间转换为指定格式
例:To_Char(Sysdate, ‘yyyy/mm/dd hh24:mi:ss’) 结果为 2008/10/07 15:34:13

38、To_Date(string,’format’)
说明:将string根据给定的时间匹配方式转换成为时间类型
例:To_Date('20081007153413','yyyymmddhh24miss') 结果为 2008-10-7 15:34:13

39、Next_Day(date,’dayofweek’)
说明:计算下个星期相同日的日期
例:Next_Day(’07-10月-2008’,’星期二’) 结果为 2008-10-14

40、Add_Months(date,month)
说明:给指定日期加上指定月份
例:To_Char(Add_Months(To_Date('2008,10,07 15:52:00',
'yyyy,mm,dd hh24:mi:ss'),
1),
'yyyy,mm,dd hh24:mi:ss') 结果为 2008,11,07 15:52:00

41、Months_Between(date2,date1)
说明:返回date2-date1的月份
例:Months_Between(Add_Months(Sysdate,1),Add_Months(Sysdate,-1)) 结果为 2

42、New_Time(date,this,that)
说明:返回this时区=that时区的日期和时间
例:SELECT To_Char(Sysdate, 'yyyy.mm.dd hh24:mi:ss') bj_time,
To_Char(New_Time(Sysdate, 'PDT', 'GMT'), 'yyyy.mm.dd hh24:mi:ss') los_angles
FROM DUAL; 结果为 2008.10.07 16:01:46 2008.10.07 23:01:46

43、RowIdToChar(rowid)
说明:将rowid转换成字符
例:RowIdToChar('AAAAfKAACAAAAEqAAA') 结果为 AAAAfKAACAAAAEqAAA

44、CharToRowId(char)
说明:将字符转换成rowid
例:CharToRowId(‘AAAAfKAACAAAAEqAAA’) 结果为 AAAAfKAACAAAAEqAAA

45、Convert(c,dset,sset)
说明:将原字符串c由原字符集sset转到目标字符集dset
例:Convert('Eleven.Xu 徐','we8hp','f7dec') 结果为 Eleven.Xu e>

46、HexToRaw(arg)
说明:将arg从16进制转换成为raw类型数据
例:HexToRaw(‘123’) 结果为 0123

47、RawToHex(arg)
说明:将arg从raw类型数据转成为16进制数据
例:RawToHex(‘0123’) 结果为 30313233

48、To_Multi_Byte(arg)
说明:将arg从单字节字符转换成为多字节字符
例:To_Multi_Byte(‘hello’) 结果为hello

49、User
说明:返回当前用户用户名
例:SELECT User FROM DUAL 结果为 APPS

50、UID
说明:用户的唯一标识
例:SELECT du.username, du.user_id
FROM dba_users du
WHERE du.username = (SELECT User FROM DUAL);

51、Soundex
说明:返回一个与给定字符串相同读音的字符串
例:略

52、Userenv(arg)
说明:若arg为 ‘isdba’,表示判断当前用户是否为DBA
若arg为 ‘sessionid’,表示判断当前sessionid
若arg为 ‘entryid’,表示会话人口id
若arg为 ‘instance’,表示当前instance标志
若arg为 ‘language’,表示当前环境语言
若arg为 ‘lang’,表示当前环境语言缩写
若arg为 ‘terminal’,表示当前终端或机器的标志
例:Userenv('isdba') 结果为 FALSE
Userenv('sessionid') 结果为 1416799
Userenv('entryid') 结果为 0
Userenv('instance') 结果为 1
Userenv('language') 结果为 SIMPLIFIED CHINESE_CHINA.UTF8
Userenv('lang') 结果为 ZHS
Userenv('terminal') 结果为 HAND-ELEVEN

53、VSize(arg)
说明:返回arg的字节数
例:VSize(‘1234’) 结果为 4

54、Decode(value,if1,then1,if2,then2,if3,then3,...,else)
说明:如果value等于if1时,DECODE函数的结果返回then1,...,如果不等于任何一个if值,则返回else。
例:SELECT Decode(Sign(10-11),-1,10,11) FROM DUAL;(取最较小值)结果为 10
补充:详细解释请参考http://blog.csdn.net/elevenXL/archive/2008/10/08/3031647.aspx

55、Trunc(arg1,arg2)
说明:根据arg2规定的精度截取arg1
例:SELECT Trunc(111.112233,2) FROM DUAL; 结果为 111.11
SELECT Trunc(111.112233,-2) FROM DUAL; 结果为 100

56、Round(arg1,arg2)
说明:根据arg2规定的精度对arg1按四舍五入进行截取
例:SELECT Round(111.41,1) FROM DUAL; 结果为 111.4
SELECT Round(111.45,1) FROM DUAL; 结果为 111.5

57、BFileName(dir,fullfilename)
说明:指定外部dir目录下的fullfilename文件,在Insert语句中使用较多
例:略

58、initcap(arg)
说明:将arg中的首字母大写
例:略



详细的SQL脚本请查看:Oracle SQL 内置函数总结.sql

Oracle SQL常用函数总结(1)


1、Substr(arg1,arg2,arg3)
说明:arg1是待分割的字符串,arg2和arg3分别是起始分割字符号
其中arg3可选
例:substr(12345, 1,4) 结果为 1234
补充:两个参数,即arg3为空的用法在SQL SERVER中不适用

2、Instr(arg1,arg2,arg3,arg4)
说明:arg1为待搜索内容,arg2为搜索内容,arg3为搜索起始位置,arg4为搜索匹配位置,即第几次匹配(arg4默认为1)
例:Instr('CORPORATE FLOOR','OR', 3, 2) 结果为 14

3、Concat(arg1,arg2)
说明:将arg1和arg2连接起来,类似于使用||符号进行连接
例:Concat(1,2) 结果为 12

4、Trim(arg)
说明:默认为去掉arg中的前后空格
例:Trim(‘ 12345 ‘) 结果为 12345 等同于使用 Trim(‘ ’ from ‘ 12345 ’)
补充:Trim又可以细分为LTrim和RTrim,分别为去掉左、右空格。
其他用法:Trim(arg1 from arg2)表示 若arg2以arg1字符开头或结束,则将所有连续的arg1字符去掉,例如 Trim(‘s’ from ‘stuff’) 结果为tuff,Trim(‘f’ from ‘stuff’)结果为 stu
为使意义更清晰,有时还会添加leading和trailing来区分arg2的匹配位置,例如 Trim(‘t’ from ‘test’) 结果为 es
Trim(leading ‘t’ from ‘test’) 结果为 est
Trim(trailing ‘t’ from ‘test’) 结果为 tes

5、Length(arg)
说明:计算arg的长度
例:Length(12345) 结果为 5

6、Ascii(arg)
说明:返回与指定字符对应的十进制数
例:Ascii(‘A’) 结果为 65

7、Chr(arg)
说明:返回与指定整数对应的字符
例:Chr(65) 结果为 A

8、Lower(arg)
说明:将arg全部变为小写
例:Lower(‘TesT’) 结果为 test

9、Upper(arg)
说明:将arg全部变为大写
例:Upper(‘TesT’) 结果为 TEST

10、Initcap(arg)
说明:将arg的首字母变为大写
例:Initcap(‘test’) 结果为 Test

11、LPad(arg1,arg2,arg3) & RPad(arg1,arg2,arg3);
说明:LPad和RPad分别为在arg1左、右粘贴字符,arg2表示字符串总占据长度,arg3表示的是如果arg1不能占据所有长度,则使用什么进行粘贴。
例:LPad(‘abc’,5,’*’) 结果为 **abc RPad(‘abc’,5,’*’) 结果为 abc**
补充:若arg1的长度大于arg2规定长度,则将arg1按照arg2的长度从第一个字符进行截取,此时arg3将不影响结果,例:LPad(‘abcdefg’,5,’*’) 结果为 abcde

12、Replace(arg1,arg2,arg3)
说明:arg1表示待处理的字符串
arg2表示待替换的字符
arg3表示要替换成为的字符
例:Replace('string test','t','*') 结果为s*ring*es*

13、ABS(arg)
说明:返回arg的绝对值
例:ABS(-100) 结果为 100
ABS(100) 结果为 100

14、Ceil(arg)
说明:返回大于或等于arg数字的最小整数
例:Ceil(3.14) 结果为 4

15、Floor(arg)
说明:返回arg数字的整数部分(取整)
例:Floor(3.14) 结果为 3

16、Sin(arg)
说明:返回arg数字的正弦值
例:Sin(1.5) 结果为 0.997494986604054

17、ASin(arg)
说明:返回反正弦的值
例:ASin(0.5) 结果为 0.523598775598299

18、Cos(arg)
说明:返回arg数字的余弦值
例:Cos(-3.1415927) 结果为 -0.999999999999999

19、ACos(arg)
说明:返回反余弦的值
例:ACos(-1) 结果为 3.14159265358979

20、Tan(arg)
说明:返回arg数字的正切值
例:Tan(20) 结果为 2.23716094422474

21、ATan(arg)
说明:返回arg数字的反正切值
例:ATan(1) 结果为 0.785398163397448

22、Exp(arg)
说明:返回e的arg次方根
例:Exp(2) 结果为 7.38905609893065

23、Ln(arg)
说明:返回arg的对数
例:Ln(1) 结果为 0

24、Log(arg1,arg2)
说明:返回以arg1为底arg2的对数
例:Log(2,4) 结果为 2

25、Mod(arg1,arg2)
说明:返回arg1除以arg2的余数
例:Mod(3,2) 结果为 1

26、Power(arg1,arg2)
说明:返回arg1的arg2次方
例:Power(2,3) 结果为 8

27、Sign(arg)
说明:取arg的符号,若arg>0,返回1
若 arg=0,返回0
若arg<0,返回-1
例:Sign(2), Sign(0), Sign(-2) 结果分别为 1,0,-1



28、Sinh(arg)
说明:返回双曲线的正弦值
例:Sinh(20) 结果为 242582597.704895

29、Cosh(arg)
说明:返回双曲线的余弦值
例:Cosh(20) 结果为 242582597.704895

30、Sqrt(arg)
说明:返回数字arg的平方根
例:sqrt(64) 结果为 8



详细的SQL脚本请查看:Oracle SQL 内置函数总结.sql


在Region之间传递Event


概述

本文主要介绍如何在ADF应用中实现不同Region之间的event的传递。

下面以RegionA和RegionB为例,详细说明如何实现在Region之间传递event。

需求:在RegionA中点击导航栏中的任意一按钮时,在RegionB中触发相同事件。


实现

1、新建ADF应用,并根据需要创建基于Employees和Departments两个表的BC(EO/VO/AM)。

2、在ViewController项目中创建两个Bounded-Task-Flow with Fragments,这里名字分别为RegionA-task-flow和RegionB-task-flow。

3、从“Component Pallete”中分别拖“View”到刚创建成功的两个Task-Flow中,名字分别为RegionA和RegionB,双击根据向导生成两个.jsff的fragments,生成后的名字应分别为RegionA.jsff和RegionB.jsff。



4、打开RegionA.jsff,从“Data Controls”中拖动EmployeesView1到该fragment,根据提示生成form,并带有导航按钮。

5、同样的,打开RegionB.jsff,从“Data Controls”中拖动DepartmentsView1到该fragment,根据提示生成带有导航按钮的form。

6、新建.jspx页面,这里命名为main.jspx,使用“Panel Splitter”Layout进行布局,将页面分割成为左右两部分。分别将RegionA-task-flow和RegionB-task-flow拖到两部分中,生成后的页面如图所示: 


7、双击打开RegionA.jsff的PageDef文件,为其导航按钮添加event,查看“Structure”面板,以First按钮为例:在First按钮上点击右键,在“Insert inside First”中选择events,继续在events中点击右键,在“insert inside events”中选择event,命名为FirstEvent,为其他按钮添加同样的操作。进而对RegionB.jsff的PageDef中的导航按钮也添加同样的操作,完成后保存。


8、打开main.jspx页面,在“Structure”面板中的PageDef上点击右键,选择“Edit Event Map”,如图设置event的mapping关系,将所有的event都进行设置。


9、保存以上设置,运行main.jspx页面。
最终运行结果:


详细代码请查看:howtopasseventbetweenregions.rar

2009年4月15日星期三

监听dialog的取消按钮事件

function openPopup(evt){

var dialog = AdfPage.PAGE.findComponent('dlg2');

dialog.HandleEvent = function(event) {

if (event.getType() == AdfDialogEvent.EVENT_TYPE) {

event.propagatesToServer = function(){

return true;

}

}

}

AdfUIDialog.superclass.HandleEvent.call(this, event);

}

Manage Bean中向客户端写js

private void addScript(String script) {

FacesContext fc = FacesContext.getCurrentInstance();

ExtendedRenderKitService erks =

Service.getRenderKitService(fc, ExtendedRenderKitService.class);

erks.addScript(fc, script);

}

Oracle 数据库打开时出现ORA-16038

Oracle
10g默认启用日志归档,db_recovery_file默认为2GB,不清理日志一般经过一段时间的使用闪回区便被占满,造成数据库无法归档,无法启动的错误。一般ORA-16038、ORA-19809、ORA-00312这三个错误会同时出现。

解决方法通常包括:

1. 提高参数db_recovery_file_dest_size

  • 使用sqlplus /nolog登录,SQL> connect /as sysdba;

  • SQL> startup mount;
  • SQL> ALTER SYSTEM SET
    DB_RECOVERY_FILE_DEST_SIZE=4G SCOPE=BOTH;

  • SQL> alter database open;

2. 关闭闪回功能(如果你确实不再需要该功能)

  • 使用sqlplus /nolog登录,connect /as sysdba;
  • SQL> alter system archive log
    stop;

3. 删除无用的归档文件并更新rman记录

  • 查看对应initxxx.ora,找不到的话可以在$ORACLE_HOME下搜索,找对应SID的即可

  • 在initxxx.ora中找到# File
    Configuration部分,可以看到db_recovery_file_dest=XXXXX,即为数据闪回区的日志文件记录位置。

  • 进入闪回区目录,查看对应SID下的archivelog文件夹内内容,删除旧的日志归档文件

  • 使用RMAN命令,RMAN> connect target /
  • 使用命令RMAN> crosscheck archivelog
    all,该操作将标明哪些归档文件已被删除

  • 使用命令RMAN> delete expired archivelog
    all,该命令将删除rman controlfile中相应的记录

2009年4月14日星期二

使用Google文档发布日志到Blogger

最近工作之余在研究Google离线应用,而Google Docs,即Google文档基于Gears技术实现了离线撰写文档的功能,研究了下,发现可以使用Google文档发布文章到自己的博客中,由于我的博客就是Blogger,因此实现起来非常简单,以下是一些步骤:
1、安装Google Gears
2、打开Google文档 ,撰写离线日志。
3、日志撰写完成后,等方便上网时同步到自己的Google文档。
4、打开文档,选择“共享”中的“以网页形式发布”。

5、选择“张贴到博客”,根据向导完成简单的配置。
在完成了以上步骤,就可以离线编写日志,然后再联网后同步到Google文档中,再非常方便地发布到博客中。

2009年4月7日星期二

Jdeveloper11g Update2发布

Jdeveloper11g Update2终于发布了,相比前一个版本来说,修复了很多Bug
期待Jdeveloper越来越好。

2009年3月30日星期一

在Task-Flow中实现页面间传值

概述

本文主要说明ADF中如何实现在Task-Flow中的页面之间传值。

在本示例中将创建两个页面,分别为PageAPageB,并完成PageAPageB中的传值。


实现

思路:

步骤一:使用set action listenerset property listener,设置fromtotypeset property listener,将当前页面的值设置到pageFlowScope中,例如设置在#{pageFlowScope.var1}

当然,在task-flow中传值时,可以在actionlistener里面设置值到pageFlowScope中。

步骤二:在页面中取值时,如果需要在一进入页面时就将该值取到并显示在页面中,可以将取值的相关代码写在页面对应的Backing Scope ManagedBean的构造方法中。

1、 创建应用,并基于EmployeesRegions两张表创建EO/VO,并将各自实例添加到AM中。

2、 创建Bounded Task-Flow,拖两个View到该Task-FlowId分别为PageAPageB,完成之间的导航设置。分别双击两个页面,用向导创建出PageA.jspxPageB.jspx

3、 PageA中,右键点击导航到PageB的按钮,在其中添加Set Action Listener,如图完成相关值的传递。

注:此时#{pageFlowScope.var1}会被标红,忽略此处。

4、打开PageB对应的Backing Scope ManagedBean,在其构造方法中添加如下代码:

RequestContext rc = RequestContext.getCurrentInstance();

String value = (String)rc.getPageFlowScope().get("var");

List list = ADFUtils.getBindingContainer().getAttributeBindings();

for (int i = 0; i <>

AttributeBinding abb = (AttributeBinding)list.get(i);

if (abb.getName().equals("FirstName")) {

abb.setInputValue(value);

}

}

rc.getPageFlowScope().clear();

5、保存以上更改和设置,运行该Task-Flow,查看结果:



相关代码请查看:howtopassvaluebetweenpages.rar