Issuu on Google+

第二部分: 构建你的业务服务

第二部分包含以下内容: ■

Chapter 4, ADF业务组件概述

Chapter 5,使用视图对象查询数据

Chapter 6,使用实体对象创建业务层

Chapter 7, 使用基于实体的视图对象构建可更新的数据模型

Chapter 8, 使用应用模型实现业务服务

Chapter 9, 在实体对象中实现业务规则

Chapter 10, 应用模块数据绑定概述

--------------------------------------------------115---------------------------------------------------------------


--------------------------------------------------116---------------------------------------------------------------


Chapter 4

ADF 业务组件概述

本章提供了 Oracle ADF 的业务组件层的概述,包括构建业务服务的关键特性描述。 本章包括如下章节: ■ Section 4.1, 业务服务的说明性方法和可重用代码 ■ Section 4.2, 什么是ADF 业务组件和它们能做什么? ■ Section 4.3, ■ Section 4.4, ■ Section 4.5, ■ Section 4.6,

4.1 业务服务的说明性方法和可重用代码

J2EE 平台为服务器端的开发和服务的开发定义了模型,然而现实世界的商务应用需要的编写、 复用和定制健壮的功能仍然交给开发团队的每个成员自己解决。尤其是,j2EE 规范没有涉及到: ■ 使用统一的方式编写和强化业务应用逻辑 ■ 在多个应用中复用业务逻辑 ■ 访问手边任务的可更新的业务数据视图 ■ 应用提交后维持和定制业务功能 从多年的在J2EE平台上构建电子商务站点应用的经验来看,Oracle注意到在构建j2EE解决方案 时,你需要花费大量的时间和努力。ADF业务组提供描述性的方法用于解决这些挑战性的任务,为 JDeveloper提供可重用的软件组件库和设计时间插件确保他们描述的时间测试的方法的简洁性。 Oracle 的 ADF 中的 ADF 业务组建技术是将 Oracle 应用,Oracle 工具和 Oracle 服务器技术分支 设计和开发与关于良好的架构,数据库为核心的企业 j2EE 应用在现在和以后将如何构建方面进行 的结合的完美选择。

-------------------------------------------------117-----------------------------------------------------------------


与 Oracle ADF 的其它层一起,ADF 业务组件每天由超过 4000 个 Oracle 内部自己的开发者使 用,并由包括 Oracle 合作伙伴在内的几千外部客户使用。这意味着这是被证明了的值得你信赖的解 决方案。

4.2

什么是 ADF 业务组件和它们能做什么?

ADF 业务组件是提供创建、部署和维护业务服务的构建块。ADF 业务组件通过为你提供一系 列的只能软件构建块大幅度的简化企业J2EE业务应用开发和定制。这些构建块通过将大部分典型的 开发任务变为声明而大大的节约开发时间。它们易于管理通用资源,这些资源用于: ■ 在自动集成数据库的组件中创建和测试业务逻辑 ■ 通过多个基于 SQL 的数据视图复用业务逻辑,支持不同的应用任务 ■ 有效地访问和更新浏览器、桌面、收件和 web 服务客户端视图 ■ 在应用提交后如果需求不再改变,很容易的定制应用的功能。 通过消除与通用应用构建相关的本质代码和测试工作,ADF业务组件使得应用开发者专注于业 务解决方案的实现。ADF业务组件,你的业务层应用组件扩展用于构建一个在如下领域需要使用的众 多的设计模式的健壮地实现: 简化数据访问 设计一个仅包含必要数据的数据模型用于客户端显示 ■ 包括复杂的主要/详细的层次作为数据模型的一部分 ■ 使用代码实现终端用户根据例子查询的数据过滤 ■ 自动的根据业务对象层协调数据模型的变化 ■ 自动的验证和保存对数据库进行的改变 ■

强化业务域的验证和业务逻辑 ■ 显式的强化所需的领域,主键唯一性、数据精确度和比例,以及外键引用 ■ 使用多级验证支持,通过程序或者声明很容易地捕获和强化简单的和复杂的业务规则 ■ 导航业务域对象之间的关系以及强化与复合组件相关的约束

-------------------------------------------------------118----------------------------------------------------------------


使用多页面单元支持复杂的 UI ■ 自动的映射在用户界面中业务服务应用逻辑所作的修改 ■ 从相关表中获取引用信息,当用户改变外键值时自动维护信息 ■ 使用自动的 web 层状态管理简化多步骤的基于 web 的业务交易 ■ 处理图像、视频、声音和没有代码的文档 ■ 同步多个数据视图的数据变化。 ■ 在应用中使用提示、工具提示和格式化的修饰以及错误信息 ■ 为业务组件定义客户源数据以支持原数据驱动的用户界面或者应用函数 ■ 在运行时加入动态属性简化每行的状态管理 实现最好的实践、高性能的面向服务的建构 ■ 强化好的基于接口的编程风格 ■ 使用自动的 JAAS 整合和审计简化应用的安全性 ■ “只写一次,到处部署” :使用类似 Java 类、EJB 会话 bean 或者 Web service 的相似的业务服务 ■ 从 2 层到 3 层的部署不需要修改客户端的代码 ■ 通过高效的批操作为远程客户端减少网络阻塞 流水化应用定制 不用修改源代码就可以扩展组件的功能 ■ 不修改应用即可使用扩展的组件代替已经提交的组件 ■ 提交应用升级不用手工的丢掉或者重新应用原有的定制 ■

所有的这些特征可以用在你的 J2EE 业务服务层中使用 ADF 业务组件可使你的生活变得更容易 来形容。提供业务服务实现的关键的 ADF 业务组件有以下几部分组成: 实体对象 每个实体对象代表数据表中的一行,可以通过处理所有的 DML 操作为你简化修改。它可以为 每行封装业务逻辑,确保实施你的业务规则。你可以将一个实体对象关联到其他对象上,映射数据 库中潜在的关系,以创建在多个应用中复用的业务域对象层。 ■

应用模块 应用模块是 UI 客户端用以圆形应用数据的交易组件。 它定义了一个可更新的数据模型和与终 端用户任务相关的逻辑单元的顶级的程序和功能(称为服务方法) ■

----------------------------------------------------119-------------------------------------------------------------


视图对象

一个视图对象代表了一个 SQL 查询,视图对象简化了使用查询结果所作的工作。你可以使用 你熟悉的 SQL 语言的所有功能将数据连接、计划、过滤、排序和聚合成终端用户的任务需要的形 状。这包含了将一个视图对象与别的对象连接以创建任何一个符合体的主要/细节层次。当终端用户 在用户界面中修改数据时,你的视图对象结合实体对象共同验证和保存变化。 当基本的组件使用它们复杂的内建行为处理所有的通用实例时,这些优点不会折衷你自己的能 力,由于有基本组件提供的任何自动的行为都很容易由几行策略性的代码重载,所以在解决问题时, 你不会被只局限于某种特定的方式。

4.3

与熟知的 4GL 工具相关的 ADF 业务组件

在企业构建中你可能习惯使用 4GL 胜过使用 J2EE 开发,ADF 业务组件提供了实现域 4GL 类 似功能的组件。本节将辅助你理解 ADF 中的关键的业务组件模型是如何与 4GL 中的组件进行映射 的。

4.3.1 Oracle Forms开发者熟悉的概念 ADF 业务组件以适用于任一种用户接口的方式实现熟知的 Oracle Forms 运行时数据为中心的所 有方面的功能。在 Oracle Forms 中,每一个 Form 既包括可视化的对象如曲线,窗口,警告和 LOVs, 又包括不可使的对象如数据块,关系和记录组等。个别的数据块项既有可视化属性,如 Foreground Color 和 Bevel,又包含非可视化的属性如 Data Type 和 Maximum Length。甚至 Forms 定义的不同事 件 处 理 触 发 器 可 以 处 理 可 视 化 的 和 非 可 视 化 类 。 例 如 , 像 WHEN-BUTTON-PRESSED 和 WHEN-MOUSE-CLICKED 类 的 触 发 器 是 可 视 的 , 它 们 与 前 端 UI 相 关 联 , 然 而 像 WHEN-VALIDATE-ITEM 和 ON-INSERT 类的域后端数据处理相关联。虽然将可视化对象和非可视 化对象进行合并简化了学习难度,但是反过来使得复用变得复杂。使用将 UI 相关的和数据相关的 元素进行清晰的分离,将会因不用考虑后端业务逻辑使重新设计用户界面变得简单,使得在多种不 同的 forms 中重新设计后端业务逻辑变得更加简单。 为了想象一下UI和数据的分离,假设将一个form剪掉一半,仅仅保留非可视化的数据相关的方 面。剩下的将变成数据块、关系和记录组的容器。该容器将会继续为共享数据块提供数据库连接, 并负责协调事物提交和回滚。当然,你仍然可以使用非可视化的验证和事务处理触发器提高或者改 变缺省的数据处理行为。你考虑的非可视化的对象是一种智能数据模型或者具有数据和业务逻辑的 普通的应用模块,但是没有用户界面元素。将应用模块与可视化的东西进行分离允许任一种你将来 使用的用户界面可用作和数据服务。 ------------------------------------------120-------------------------------------------------------------------------------


留意一下数据块在应用模块中扮演的角色。它们将会使用 SQL 语句从数据库中查询数据行, 协 调 与 别 的 数 据 块 master/Detail 关 系 , 验 证 具 有 WHEN-VALIDATE-RECORD 和 WHEN-VALIDATE-ITEM 触发器的用户数据项,在提交数据服务的事物时将用户的变化与 INSERT、 UPDATE 和 DELETE 语句进行交流。 经验告诉我们,需要依据手头的任务对终端用户数据进行过滤、连接、排序和分组。另一方面, 我们应用到业务数据的验证规则基本上不会随时间变化。看到这些,编写业务实体验证将会非常有 用,任何时候平衡由用户在应用操作的数据。 启用这个弹性机制需要对我们的数据块功能的更加深入的成本。我们需要一种 SQL 查询对象 代表我们应用所需要的不同的数据视图,另一种业务实体对象实施业务规则并与数据库基表进行一 致性通讯。通过将这类的事物进行分离,在 SQL 查询代表相同的业务数据时,我们可以有使用相 同的实体对象的不同的视图对象。 通过提供实现在 Java 世界中我们熟悉的表格激发的功能的易用的 Java 组件,Orace ADF 实现 了 UI/数据分离,与清晰分离的功能行进行分割。 应用模块是一个无头的Form模块 ApplicationModule 组件是 form 的数据部分,它是一个数据服务,该服务包括我们的客户端接 口进行查询需要的数据模型。它还提供了它包含的组件所使用的交易和数据库连接,它可以包含称 为服务方法的封装在服务实现中的 form 级别的程序和函数。我们可以选择这些程序和函数中哪个 为私有的哪个为共有的。 像 Forms Record Manager 一样进行实体验证和行保存 实体对象实现了数据块功能表的验证和数据库变化部分。在 Forms 的运行时,该职责由记录管 理器实施。它负责对数据库中变化了的数据块保存痕迹,触发数据块和数据项中的验证触发器,亦 可用于协调数据库中的数据保存。这正是实体对象所能为我们做的。与基表相关,他是一个可以呈 现我们业务实体的组件,并给我们一个单独的区域去封装与业务对象相关的验证、默认行为和数据 库行为的业务逻辑。 视图对象像数据块一样进行数据查询 ViewObject 组件对象执行数据块功能的数据获取部分。每个视图对象封装了一个 SQL 查询, 在运行时每个视图对象管理它自己的查询结果集。如果我们在 master/detail 关系中关联两个或者更 多的视图对象,它们之间的协调会自动处理。虽然定义了视图对象,你可以将任何查询列域实体对 象相关联。通过捕获这个信息,视图对象和实体对象可以在运行时自动的协作,实施业务逻辑而不 管用户需要的业务数据的形状是什么。

------------------------------------------121--------------------------------------------------------------------------


4.3.1 PeopleTools 开发者熟悉的概念 如果你之前使用 PeopleTools 开发,你可能比较熟悉 PeopleTools 的组件结构。ADF 业务组件 实现了你所熟知的 PeopleTools 的数据访问功能。 应用模块是无头的组件 ADF 使用 MVC 模式将模型和视图进行分离。在视图层,使用标准的基数如 JSF 和基于 web 应 用的 ADF Faces 组件或者 Swing 定义了类似与你所熟知的 PeopleTools 组件的页面,用于忠实的桌 面客户端显示。 ADF 应用模块定义了正如 PeopleTools Component Buffer 的数据结构。通过定义主要的/详细的 可以生成数据行集合的 ADF 查询组件之间的关系,你可以保证任何应用数据运作的应用模块可以 复用本来的层次关系,这点域 Component Buffer 中的 scroll 等级。 与你熟悉的 Component Interface 类似,Application Module 是一个可以访问标准方法和附加开 发人员定义的业务逻辑的服务对象。为了呈现一个特殊的用户接口的无头的数据服务,Component Interface 限制了很多与 UI 交互相关的 PeopleTools 的功能的使用。Application Module 与 Component Interface 类似,因为它提供不带头的数据服务,但是不同的,它不是通过将现有的用户接口包上一 个严格的视图实现的。相反,Application Module 专门用于处理业务逻辑和数据访问。不采用在 Component 上构建 Component 接口, 使用 ADF,你首先构建独立于用户接口的 Application module 服 务,然后在该服务上构建一个或者多个页面完成终端用户任务。 Application Module 与 Transaction 对象相关联,方式与 PeopleTools ComponentBuffer 相同。 Application Module 还为它包含的组件提供了数据库连接。任何一个类似与 Component PeopleCode 类似的与事物相关联的逻辑,在 ADF 中,你定义成 Application Module 上的逻辑。 你写成Component Record PeopleCode 或者Component Record Field PeopleCode的与事物中的记录 相关联的逻辑,现在可能不会在Application Module上进行定义。当相同的记录出现在不同的组件中 时,ADF有View Object可以进行更好的重用。 实体对象是记录定义 实体对象是潜在的数据结构的映射,正如PeopleTools Record Definition映射到潜在的表或者视图一 样。你可能会经常在你的应用程序中为需要操作的每一个表创建一个实体对象。

与使用 PeopleTools 的翻译值声明一系列有效地字段如“Customer Status”类似,在 ADF 中, 你 可 以 给 实 体 对 象 的 属 性 增 加 声 明 式 的 验 证 。 写 成 Record PeopleCode 或 者 Record Field PeopleCode 的与应用中记录相关的逻辑,在 ADF 中,可以在实体对象上进行定义。

---------------------------------------------122------------------------------------------------------------------------


像行集合一样使用视图对象查询数据

正如 PeopleTools 的行集合,视图对象可以具有 SQL 查询。不像行集合,视图对象定义可以包 括业务逻辑。 在Component Record PeopleCode中可以看到的任何一个逻辑就像定义在视图对象的备选项。 Component Record PeopleCode直接绑定到Component,但是视图对象可以与不同的Application Modules关联。虽然在PeopleTools中可以使用相同的Record Definition,Oracle ADF允许你重用在 多个应用之间重用业务逻辑。 视图对象在确切的对当前应用非常有用的形状中查询数据。许多视图���象可以在相同的实体 对象上构建视图对象。 你可以定义视图对象之间的关系以创建master-detail结构,正如你在PeopleTools Component. 中看到的scroll等级一样。

4.3.2

SiebelTools 开发者熟知的概念

如果你过去使用SiebelTools版本7或者之前的版本开发,你将会发现ADF业务组件实现了你熟 悉的所有的数据访问功能,还有很多得到了增强。 实体对象是具有业务逻辑封装的表对象 类似于Siebel 表对象,ADF实体对象描述了单个表的物理特性,包括列名和物理的数据类型。 两者都包含了足够的信息用以产生可以在数据库中创建物理表的DDL。在ADF中,你可以定义实体对 象之间的关联用以映射表中出现的外键,并且这些关联用于自动的在用户接口页面或者屏幕使用的 视图对象查询中加入信息。从数据列中引用的对象值的列表在ADF中声明式的实体验证规则和视图 对象查询相结合进行处理。你也可以封装别的具有这些实体对象表处理器的声明式或者程序式的业 务逻辑,他们可以在你创建的数据视图中复用。 视图对象是一个业务组件 像 Siebel Business Component一样,ADF视图对象在物理表的呈现上描述了一个逻辑映射。 它们都允许你提供满足用户需要的逻辑的字段名、数据和字段。与使用业务组件一样,你可以定义 从不同的表中连接信息的视图对象。相关的ADF视图连接与Siebel Link对象类似,允许你定义 master/detail关系。在ADF中,你的视图对象定义可以使用SQL语言的全部功能对用户接口需要的 数据进行定型。 业务模型是具有连接和交易的业务对象 Siebel Business Object允许你定义业务组件的集合。ADF应用模型履行类似的职责,允许你 创建master/detail视图对象的集合,充当相关用户接口页面的数据模型。另外,应用模块为本组 数据视图提供了一个交易和数据库连接上下文。你可以向从应用模块中获取的对象发送多个请求并 且它们在相同的交易中。 ---------------------------------------------123-------------------------------------------------------------------------


4.3.3

ADO.NET 开发人员熟知的功能

如果你过去使用Visual Studio 2003或者 2005开发,你可能对使用ADO.net框架访问数据比 较熟悉。ADF业务组件实现了从ADO.NET中的所有数据访问功能,并有所提高。 应用模块是一个增强的数据集 应用模块组件充当ADO.NET数据集相同的角色。它是一种被称为视图对象的的呈现了行集合的 服务组件,它与ADO.NET DataTables类似。应用模块与相关的交易对象一起提供视图对象执行的SQL 查询的上下文以及有实体对象保存的对数据库的修改,这担当ADO.NET DataAdapter的角色。 实体对象是一个增强了的、强类型的数据DataAdapter 实体对象类似于强类型的ADO.NET DataAdapter。它代表了特定表中的行,并对这些行进行通 过主键进行查询、插入、更新、删除和锁定处理。在ADF中,你不用自己指定这些语句,但是你可 以在需要的时候进行复写。实体对象封装了与属性或者表中的行相关的验证或者别的业务逻辑。当 终端用户使用与下面的实体对象相关的视图对象修改和保存数据时实施验证。 视图对象是增强的Data Table 视图对象组件封装了SQL查询,并管理结果集。它可以关联到下面的实体对象上用以自动的协 调验证和由用户修改行所作的保存工作。在视图对象的查询数据和封装了业务逻辑的实体对象之间 的协作通过将业务逻辑封装到业务对象提供了Data Table的所有的优点。向ADO.NET数据表一样, 你可以很容易的使用像XML一样的视图对象数据或者使用视图对象读取XML数据,基于它包含的信息 对行进行自动的插入、更新或者删除。

4.4

ADF 业务组件实现架构概述

在接下来的几章分成一个个关键的组件之前,首先理解一些Oracle ADF的设计和实现的指导原 则很重要。

4.4.1

基于标准的Java和XML

与Oracle ADF的其他部分一样,ADF业务组件技术使用Java编写的。基本组件实现了大批的通 用的、元数据驱动的函数,通过丰富的工作和测试过的代码层节省你的开发时间。遵循ADF业务组 件的元数据,通过清晰分离的XML文件控制配置每个组件的运行时行为的元数据,而遵循J2EE标准。

-------------------------------------------------124-------------------------------------------------------------------


由于ADF业务组件经常用于bet-your-business应用,了解Oracle ADF的源代码,包括ADF业务 组件层在全球范围内都有技术支持这点很重要。框架的源代码会对你诊断定位问题和根据你的需求 扩展基本框架功能会是一个非常重要的工具。

4.4.2

与任何一种应用服务器或者数据库协同工作

由于你的业务组件是由Java类和XML文件实现的,你可以在装了java虚拟机的运行环境中使用 他们。这意味着使用ADF业务组件构建的服务在J2EE服务器(作为运行时的应用程序的容器)内外 都易于使用。客户依然可以使用应用模块的不同的配置如命令行批处理程序、web services、 servlets、JSP页面以及使用Swing构建的友好桌面客户端及其他。 使用ADF业务组件构建的应用可以在任何具有java能力的应用服务器(包括j2EE应用服务器) 上运行。这点在Section 4.6.1, "Choosing a Connection, SQL Flavor, and Type Map"中进行了 描述。除了构建优化的Oracle 数据库应用程序外,你也可以构建使用不是Oracle的数据库的应用 程序。 4.4.3 实现你需要的所有的J2EE设计模式 ADF业务组件层实现了J2EE中比较受欢迎的所有的设计模式,这些模式可以用于理解、实现和 调试以创建现实世界的企业级的J2EE应用。在J2EE你可能看到过许多关于由ADF业务组件如何实现 的设计模式,如果你要经常交叉引用这些名字,那么你可以参考Appendix E, "ADF Business Components J2EE Design Pattern Catalog".

4.4.4 组件被组织成包 由于ADF业务组件是由Java实现的,它是由组织成包的类和接口实现的。Java包是由开发者用 点号分割的名字进行标识,并将代码组织成层次命名结构。为了确保你的代码不与其他组织的可重 用性代码发生冲突,包名最好选择以你的组织名字或者网络域名开始。比如,Apache组织选择 org.apache.tomcat 作为与Tomcat web服务器相关的包名,而Oracle采用oracle.xml.parse作为它的XML 解析器的包名。你为你的应用程序创建的组件存在于名字诸如com.yourcompany.youapp类的包或者 子包中。 作为一个具体的例子,组成SRDemo应用主要业务服务的ADF业务组件组织到 oracle.srdemo.model包和子包中。如图4-1所示,这些组件驻留在工作目录中的DataModel工程中,并 按照如下方式进行组织: ■ oracle.srdemo.model 包含SRService 应用模块 ■ oracle.srdemo.model.queries 包含对象视图 ■ oracle.srdemo.model.entities 包含实体对象 ■ oracle.srdemo.model.design 包含叙述服务的UML图表

----------------------------------------------------125------------------------------------------------------------------


Figure 4–1 在SRDemo应用中的ADF业务组件的组织

在你的应用中,你可以选择你最信得过的包组织你的程序。特别需要注意的是,不要像SRDemo 应用的创建者一样将同种类型的放到单一的包中。 由于JDeveloper支持重新定制,你可以在任何时候修改一个组件的名字或者将他们挪到另一个 不同包结构中去。换句话说,你不一定在第一次把结构固定。你的业务服务的包机构会随着时间的 推移和你对ADF换件的不断熟悉而不断完善。 没有魔幻数字可以描述在一个包中可以包含组件的最优数量。然后,依经验讲,你会发现你的 开发团队的正确的结构介于下面两个例子之间: 所有的组件在一个包中

每个组件在属于它自己的单独的包中

这点在Section 25.7, "Working with Libraries of Reusable Business Components",中做了更详细的描 述。由于一个ADF业务组件包是一个JDeveloper支持的可以导出用在其他工程中的单位,有时,你 可能从这个角度去可虑你如何组织组件。

--------------------------------------------126--------------------------------------------------------------------------


4.4.5 基本ADF业务组件层的架构 ADF业务组件层提供的预定义的类和接口代码在oracle.jdbc.jbo包和大量的子包中。然而,在你 使用ADF业务组件的过程中,你可能主要使用两个关键的包中的类和接口,它们是oracle.jbo和 oracle.jbo.server。oracle.jbo包含所有的为业务服务客户端设计的所有接口,而oracle.jbo.server包包含 了所有的实现这些接口的类。

注:术语"client"意味着作为业务服务访问应用模块组件的模型、视图或者控制器层中的任意代码 。

图4-2 展示了应用模块组件的一个具体的例子。应用模块的客户端接口是oracle.Jbo包的 ApplicationModule接口。这个接口定义了客户端使用应用模块时可以使用的方法的名字和签名,但 是,它不包含相应功能的具体实现。实现应用模块组件基本功能的类在oracle.jbo.server包中的 ApplcationModuleImpl。 Figure 4–2

Oracle ADF业务组件将接口和实现进行分离。

4.4.6 组件是具有可选的定制功能的元数据驱动的Java 代码 在ADF业务组件中的每类组件与通过声明式设置进行控制的内建的运行时功能模块相对应。这 些设置存放在与组件名具有相同命名的XML组件定义文件中。当你需要为一个组件编写定制代码 时,你可以启用组件的一个可选的定制Java类。

4.4.6.1 只有XML的组件的例子 图4-3展示了你在com.yourcompany.yourapp包中创建的像YourService应用模块类的特定应用组件的 XML组件定义文件。相应的XML定义在JDeveloper的工程的源路径的根目录中的子目 录./com/yourcompany/yourapp中。该XML文件记录了在运行时提供给应用模块实现的Java类的名 字。在本例中,XML记录了Oracle ADF提供的oracle.jbo.server.ApplicationModuleImpl类 的名字。 .

---------------------------------------------------127--------------------------------------------------------------------


Figure 4–3 一个应用模块的XML组件定义文件

如果你不需要扩展ADF 业务组件的内建的功能,也不需要编写定制代码去处理它的内建的事 件,你可以使用这个只有XML的组件。这意味完全通过XML组件定义来定义组件,并且不需要任 何与组件相关的Java定制代码甚至是Java类文件实现所有的功能。

4.4.6.2 具有定制Java类的组件的例子 当你需要增加定制java代码扩展一个组件的功能或者处理事件是,你可以为你创建的任一种类 型的ADF业务组件启用一个定制Java类。你在JDeveloper的相关的组件编辑器的Java面板上为一个组 件启用定制代码类。这为一个符合可配置命名标准的命名的组件相关的定制类创建一个Java源文件。 该类的名字记录在组件的XML组件定义文件中,它可以为你提供一个为组件编写定制java代码的地 方。一旦你启用了一个组件的定制类,你在任何时候都可以使用组件的Application Navigator 上下 文菜单的相关的Go To…Class选项定向到它。 图4-4演示了当你为 YourSerice应用模块启用一个定制Java类的情况。一个YourServiceImpl.java 源代码文件在与你的XML组件定义文件相同的路径中被创建。YourService.xml文件被更新映射了在 运行时,组件使用com.yourcompany.yourapp.YourServiceImpl 类而不是基类 ApplicationModuleImpl Figure 4–4 Component with Custom Java Class

--------------------------------------------------128--------------------------------------------------------------------


=========================================================================

注:本指南中的例子对定制组件类和接口的名字的产生使用默认的设置。如果你向为你的 应用程序修改缺省的设置,可以使用JDeveloper Tools Preferences对话框的Business Components: Class Naming。你所作的修改仅仅影响新建的组件。

4.4.7 ADF业务组件设计时间引用配置建议 你可以对JDeveloper是否为它支持的每个组件类型产生缺省的定制Java文件进行配置管理,也可 以对JDeveloper是否需要在每个使用包对应的XML文件中维护一张Oracle ADF 业务组件列表进行 配置。本节描述了Oracle对开始使用ADF Business Components开发者如何配置这些选项的一些建议。

4.4.7.1 初次启用定制java产生器的建议 你的应用可以将仅有XML的组件和有相关定制Java文件的组件混合在一起。比如,你可以定义 一个完全的功能性的、可更新的数据模型,而这个模型使用仅有XML组件现实的实施业务规则。另 一方面,一些开发者更愿意为他们创建的每个组件定义产生java类,作为他们团队的代码风格的一 部分。 对于刚开始使用 ADF Business Components 的开发者而言,Oracle 建议对 JDeveloper 的最初 的配置采用默认的不产生定制 java 类的方式。采用这种方式,你可以发现为什么需要使用定制 java,然后会在你的应用中自觉为组件启用它。时间久了,你会有一个属于你自己的最优的选择。 注意,这个建议不是缺省的,因此,你需要按照下述推荐的步骤对Java 生成器参数进行配置: 从JDeveloper中选择 Tools | Preferences... ■ 在左边的树中选择Business Components 目录 ■ E 保证所有的复选框没有被标记,如图4-5所示,然后单击OK . ■

Figure 4–5 缺省的不产生Java的业务组件参数设置


--------------------------------------------------129--------------------------------------------------------------------4.4.7.2 建议不使用 XML 文件包 通常情况下,Oracle ADF 是向上兼容以前版本的,JDeveloper 在每一个包含 Oracle ADF 业务组件的目录中维护 XML 文件包。当 ADF 运行加载以前的版本时,XML 文件包版本是可选择的。 自从维护这个 XML 文件包以来,反而使团队的发展变复杂了,Oracle 建议通过设置“复制 XML 文件 包到根路径”选项中的“业务组件:普通”这个 IDE 参数选择面板来禁用任何一个 XML 文件包,如 图 4-6 所示。 Figure 4–6 Disabling the Use of the Optional Package XML File for ADF Business Components

========================================================= 注意:为了在一个已存在的工程中不使用 XML 文件包,可以通过“工程属性”对话框, 选择“业务组件:选项”面板,如上图所示不勾选对应的选择框。 ========================================================= 4.4.8 基础数据类型 Java 语言为字符串、日期、数字和其它数据,提供了许多内置的数据类型。你可以使用这些 类型在 ADF 业务组件中,但是通常情况下你将会从 oracle.jbo.domainh 和 oracleord.im 包中使用 一个最优的类型。如图 4-1 所示,这些类型改进了 Oracle 数据库的性能,原因是其允许数据保持 在它原来的地方,消除了一些不必须的数据内部转换。例如对于字符串基础类型,ADF 业务组件通 常使用 java.lang.String 类型。


---------------------------------------------------------------130----------------------------------------------------------------------------

=============================================================== 注意:oracle.jbo.domain.Number 类和内置的 java.lang.Number 类有相同的类名。Java 编 译 器 会 自 动 导 入 java.lang.* 到 每 一 个 类 中 , 你 ��� 要 在 涉 及 到 的 地 方 明 确 的 导 入 oracle.jbo.domain.Number 类。典型的,JDeveloper 将会自动的为你做这些,但是如果你 写更多的你自己的代码习惯,你将会遇到编译器报出“Number is an abstract class”的 错误,意思是你不注意使用 java.lang.Number 代替了 oracle.jbo.domai.Number。这时候 在你的类的最上面,包名后添加一行“import oracle.jbo.domai.Number”,可以消除这类 的错误。 =============================================================== 4.4.9 强制类型转换类 API 在应用模块、视图体和实体中,你可以选择使用一组普通的 API 或使用 JDeveloper 生成的代 码到 Java 类中来做强制类型转换。例如,在一个视图体中,你可以使用普通 API 存取属性值结果 在任意一行中,如: Row row = serviceRequestVO.getCurrentRow(); Date requestDate = (Date)row.getAttribute("RequestDate"); 注意上面使用了普通的 API,你通过字符串作为参数,得到了例子中的 Date 返回类型。另外,也可 以使用强制类型转换,如下: ServiceRequestsRow row = (ServiceRequestRow)serviceRequestVO.getCurrentRow(); Date requestDate = row.getRequestDate(); 在这个例子中,使用返回类型已知的生成的方法,代替传递字符串的名字并且获取返回值。后 续章节中会解释如何使用强制类型转换。


---------------------------------------------------------------131----------------------------------------------------------------------------

通过设计,业务域的业务服务设计实体不再设计成可直接被客户端引用的。而是客户端通过视 图体作为一部分的应用数据模型作为数据的需要。在后面的章节,你将会在 7.7 节中看到“理解怎 样使视图体和实体协作运行”,视图体在业务域中自动和实体协作,来调整确认并保存用户改动过 的数据。 因此,在你的业务服务中有如下可视化的客户端组件: 应用模块——指服务本身 视图体——指发起请求部分 视图行——指返回请求结果的每一行 4.4.10.1 客户端接口框架组成 Oracle.jbo 包为业务服务提供了容易理解的的客户端 API 就像一组 Java 接口。像上面提到的 一样,这个包不包含任何实体接口或任何允许客户端直接为实体服务的方法。而是客户端代码为接 口服务,如: 应用模块——为应用模块服务 视图体——为视图体服务 行——为视图行服务 4.4.10.2 定制的客户端接口组成 当为 Oracle ADF 业务组件添加定制代码时,希望客户端也能够访问到,这时可以用“publish” 为可视化客户端组件来定义那个功能性的客户端。在每一个组件中,对于客户端接口来说,至少一 个定制方法是发布的,JDeveloper 自动的维护涉及到的 Java 接口文件。因此,假定为应用模块服 务,像 SRService 模块应用在 SRDemo 应用中一样,就可以使用定制接口,如: 定制应用模块接口 SRService extends ApplicationModule 定制视图体接口 StaffListByEmailNameRole extends ViewObject 定制视图行接口 StaffListRowClient extends Row 客户端代码可以抛出普通的客户端接口到更特殊的接口,包括为特殊组件选择一组易理解的客 户端方法。

---------------------------------------------------------------132----------------------------------------------------------------------------


4.5 理解动态数据模型 可以实现业务服务执行的、简单的、有益于使用 ADF 业务组件的方法就是为动态库模型应用模 型的支持。对于有 Forms/4GL 背景的开发者来说,他的工作仅仅是使用以前的工具。 4.5.1 什么是动态数据模型 典型的J2EE业务服务是将负担放在了客户端层,开发者具有如下责任: 调用服务方法获取当前的返回数据, 跟踪客户端创建、删除或修改的数据 通过改变一个或更多不同的服务方法来使其生效并保存它们。 ADF 应用模型的设计者已经认识到重做、创建、编辑、删除和保存在企业业务应用中已经非 常普遍了,因此一种更轻便、更普通的方法的出现是必需的。为业务服务使用应用模式,可以对 动态的视图体实例在应用模型的数据模型中简单绑定客户端的 UI 控制。 你的 UI 显示自动的更新数据模型中涉及到的任何行的改变。这包括显示为网页或活动体创建的 JSP 或 JSF 页,就像用户桌面电脑 UI 中包含的操作系统和面板使用的 Swing 一样。这些“动态的” 数据显示包含对数据模型的改变,这些工作的通过你自定义的业务服务方法来直接或间接的完 成。 在应用模型组件下执行一组普通的服务方法使动态数据库模型能够容易的应用在 SOA 中。客 户端层在 oracle.jbo 包中简单的使用 ADF 业务组件接口。这些接口提供一个更高级的 API,满足 你的数据模型的最终的搜索、创建、删除、更改和保存的需要。隐藏了所有的低版本的普通的 SOA 方法。而且,当你创建有利的 ADF 模型层的 UI 显示来声明数据绑定时,在动态数据模型处一般 不需要写客户端代码。你展示的是绑定在数据模型中的视图体和当你需要完成任何类型的逻辑业 务服务函数时普通的业务服务方法。 4.5.2 动态数据模型在实际中的例子 考虑下面三个简单的、具体的动态数据类型在实际中的例子: 新的数据在有关的显示中出现没有再询问 使用者在 SRDemo 应用中记录日志并且看到大量的公开的服务请求。当他返回到他的主页时, 他要访问一些指导页和创建一个新的服务请求,这新的服务请求出现在他的大量的公开的请求 中不用再访问数据库。

---------------------------------------------------------------133----------------------------------------------------------------------------


业务层引起的改变自动被影响到 经理编辑一个服务请求并且通过一张人员名单指派一名工程师去做。业务逻辑装载入 ServiceRequest 实体中,在业务层的数据模型之后,包含一个简单的规则来更新分配的日期到目 前的日期,无论何时服务请求的分配属性被改变。用户接口自动更新涉及到的分配日期,那些在 业务层逻辑上被改变的日期。 业务服务方法再请求的数据和目前的行 在一个树状结构中,用户工作在树中的细节节点。这个声明在应用模型中调用一个业务服务 方法,来再请求主要的细节信息和目前适当的行。这个展示更新到涉及到的新的主要/细节数据 和目前展示的行。 除了动态数据模型,开发者使用智能度稍低一些的业务服务强迫在客户端去写更多的代码,每 天执行。另外,为了保持每天更新,通过强制管理“refresh flags”来使控制层在从业务服务请 求数据来反映可能被更改数据。当使用 ADF 应用模式来执行你的业务服务时,可能集中在业务逻辑 上,而不是你的业务工作的最终期望上。 4.5.3 动态数据模型使你去除大部分的的客户端代码 因为应用模型的动态数据模型特性确保客户端使用接口总是最新的,能够避免在与安装或 操作数据模型有关的客户端写代码。Oracle 推荐在你的应用数据组件中压缩任何的代码。无论 操作视图体的代码标题是执行全部功能性业务服务的样子,都应该在应用模型的 Java 类中通过 书写通用的方法来压缩细节部分。包括如下代码,也不仅仅这些。 配置视图体属性来查询正确的数据来展示 迭代视图体的所有行来返回一个合计的结果 执行一个或更多个视图体上任何类型的程序上的复合过程 通过在应用模型上集聚这些执行的细节部分,可以获得下面的利益: 使代码的目的对于客户端来说更加清晰 如果需要,可以允许成倍的客户端容易的调用相同的代码 可以使业务服务的测试简单化 对改进你的执行保持选项公开而不用影响客户端 能够在你的记录中公布合理的业务函数 另一个客户端代码的典型类型是不用写使用 ADF 业务组件,当有重要的改变时与之相应的细节数 据就会收集起来。通过将在下一章节中学习到的联系视图体,能够自动的为你完成。

---------------------------------------------------------------134----------------------------------------------------------------------------


4.6 ADF 业务组件设计工具的综述 JDeveloper 为 ADF 业务组件提供广泛的设计支持。这一节集中了指导业务组件工作的工 具。 4.6.1 选择一个连接、SQL 类型和 Map 类型 第一次创建组件时,将会看到业务组件的初始化,对话框如图 4-7 所示。使用这个对话 框选择一个设计数据库连接来为工程中的业务组件服务。“Connection”的下拉菜单可以显示 所有已经创建的连接的名字,如果下拉菜单中没有你需要的,可以选择“New”按钮,创建一 个新的连接。

SQL 类型设定控制 SQL 声明的语法,视图体将会使用并且 DML 声明的语法也将会使用。如果 JDeveloper 发现你正在使用 Oracle 数据库驱动程序,它会默认设置为 Oracle SQL 类型。支持的 SQL 类型包括: Oracle ---- 缺省的,对 Oracle 起作用 OLiet ----- 为 Oracle 轻级数据库 SQLServer ----- 对 Microsoft SQLServer 数据库起作用 DB2 ----- 对IBM DB2 数据库起作用 SQL92 ----- 对任何一个支持SQL92 的数据库起作用

---------------------------------------------------------------135----------------------------------------------------------------------------


TypeMap 设置决定你在项目中使用优化的 Oracle 数据类型集,还是仅仅使用基本的 Java 数据 类型。如果 JDeveloper 检测到你正在使用 Oracle 数据库驱动,将缺省的设置为 Oracle Type map, 支持的类型映射有: Oracle—使用the oracle.jbo.domain包中的优化的数据类型 Java—仅使用基本的 java 数据类型 注:如果你打算使你的应用可以运行在oracle和非oracle数据库上,在开始构建你的应用程序时,你 应该选择SQL92 SQL Flavor。虽然这使得应用程序可在Oracle或者非Oracle数据库上进行移植, 它 使Oracle SQL Flavor中内置的一些oracle相关的优化不能使用 4.6.2 使用向导创建新的组件 在ADF Business Components目录中的New Gallery中,JDeveloper为创建每种业务组件提供了 向导。每个向导允许你为新的组件指定名字、选择你准备将你的组件组织进行的包。如果包还不存 在,新的组件就成了该新包中的第一个组件。向导提供了一系列面板可以捕获创建组件类型的必要 信息。单击完成时,JDeveloper通过保存它的XML组件定义文件创建新的组件。如果你将你的Java 产生选项设置为默认值,JDeveloper还会创建初始的定制Java类文件。 4.6.3使用上下文菜单快速创建新的组件 一旦一个包在 Application Navigator 中存在,你就可以通过在 Application Navigator 中选 择它,快速的创建包中的任一种类型的其他的业务组件,并可以如图 4-8 所示,使用鼠标右键的上 下文菜单的任一项。 图4-8 包上的用以创建业务组件的上下文选项。

---------------------------------------------------------------136----------------------------------------------------------------------------


4.6.4 使用组件编辑器编辑组件 一旦一个组件存在,你可以通过在 Application Navigator 中双击组件或者选择它然后使用鼠 标右键上下文菜单的 Edit 选项,使用相关的组件编辑器编辑组件。组件编辑器为向导中提供了一 系列面板,允许你改变组件的任一个方面。单击 OK 时,JDeveloper 更新组件的 XML 组件定义文件, 并可以在必要时更新相关的定制 Java 文件。 4.6.5 使用UML图可视化、创建和编辑组件 正如第二章"Overview of Development Process with Oracle ADF and JSF",中高亮显示的一 样,JDeveloper为ADF业务组件提供了可扩展的UML图的支持。你可以创建业务组件图,可使用图创 建和修改组件,或者两者的混合。当然,你也可以删掉你已经为其创建了图的组件。图与你在编辑 器中所作的改变保持同步。 创建新的业务组件图,在JDevloper New Gallary 的ADF Business Components目录中使用Business Components Diagram项。该目录是Business Tier选择的一部分。 4.6.6 使用业务组件浏览器测试应用模块 一旦你创建了应用模块组件,你可以使用内置的Business Components Browser.进行交互的测 试。要启动Business Components Browser,在Application Navigator中选择应用模块或者业务组 件图,然后从鼠标右键的上下文菜单中选择Test。该工具提供了应用模块数据模型中的视图对象实 例,允许你使用动态产生的用户接口与之交互。该工具在你创建页面或者Swing 面板的视图层之前 和之后,都不能用来测试和调试你的业务服务。 4.6.7 refactor组件 任何时间,你都可以在Application Navigator 中选择一个组件,然后从鼠标右键的上下文菜 单中选择Refactor > Rename 对组件进行重新命名。当你使用鼠标单击及进行选择时,你也可以在 导航页面中通过按下[Ctrl]健选择一个或者多个组件,然后选择Refactor > Move 将这些组件移动 到一个新的包中。对当前工程中的旧的组件名或者包的引用会自动调整。

---------------------------------------------------------------137----------------------------------------------------------------------------


---------------------------------------------------------------138----------------------------------------------------------------------------


本章描述如何使用由ADF视图对象封装的SQL查询从数据库中查询数据。 本章包含如下几部分: ■ 5.1节, 视图对象概述 ■ 5.2节, 创建一个简单的、只读的视图对象 ■ 5.3节,在应用模块数据模型中使用视图对象 ■ 5.4节, 定义属性控制链 ■ 5.5节, 使用业务组件测试视图对象 ■ 5.6节, 程序中使用视图对象查询结果 ■ 5.7节, 如何创建一个命令行java测试客户端 ■ 5.8节,使用案例依例查询视图标准筛选结果 ■ 5.9节, 使用命名的绑定变量 ■ 5.10节,使用Master/Detail数据 ■ 5.11节, 为视图对象产生定制java类

5.1 视图对象概述 视图对象是封装SQL查询、简化使用其结果的Oracle ADF组件。在本章最后,你会明白在图5-1中列 出的所有的概念。 ■ ■ ■ ■ ■ ■ ■

你通过提供一个SQL查询定义一个视图对象 你使用提供数据库查询交易的应用模块的视图对象实例 你可以将一个视图对象连接到一个或多个其他的视图对象上以创建master/detail层次 运行时,视图对象执行你的查询并且产生一个行集 每行使用一个相应的行的主键进行标记 你使用行集遍历器遍历行 你可以通过应用一套按例查询标准对视图对象产生的行集进行过滤

---------------------------------------------------------------139----------------------------------------------------------------------------


图5-1 一个对象视图定义一个查询并产生一个行集的

注:要使用本章的例子的版本进行试验,从http://otn.oracle.com/documentation/ jdev/b25947_01/的Example Downloads页下载DevGuideExamples 工作区,然后可以了解 QueryingDataWithViewObjects项目。 5.2 创建一个简单的只读的视图对象 视图对象可以用于读数据和更新数据。本章主要介绍如何使用视图对象操作只读数据。第7 章"Building an Updatable Data Model With Entity-Based View Objects", 你将可以学习如何 创建可以更新数据的视图对象。 5.2.1如何创建一个只读的视图对象 要创建一个视图对象,使���Create View Object wizard. 该向导可以在Business Tier > ADF Business Components category 目录的New Gallery 中使用. If如果这是你在项目中创建的第一 个组件,将会出现Initialize Business Components Project 对话框提示你选择一个数据库连接。 这些例子假定你使用一个SRDemo模式下的一个SRDemo连接。如图5-2所示,提供了一个包名,一个 视图对象名,并表明你准备使用视图对象通过只读访问对数据进行管理。该图演示了在 devguide.examples包中创建名为Users的视图对象。

---------------------------------------------------------------140----------------------------------------------------------------------------


图5-2 为一个新的视图定义包和组件名。

在步骤2中(SQL Statement 页),在Query Statement 框中粘贴一个有效的SQL语句或者单击 Query Builder就可使使用交互的查询。图5-3展示了一个从USERS表中按照EMAIL排序的用户的一些 信息列的查询。 图 5-3 为一个只读视图对象定义 SQL 查询

注:如果你你看到的是实体对象页而不是SQL Statement页,请回到第一步检查是否你 经选择的Read-only Acess。


---------------------------------------------------------------141----------------------------------------------------------------------------

由于该查询没有引用任何绑定变量,你现在可以跳过第三步 Bind Variables 页。在5.9节 Using Named Bind Variables",,你可以增加一个绑定变量去了解绑定变量在查询中如何使用。 除了SQL查询信息外,视图对象可以捕获旗其查询列表中的每个表达式的名字和数据类型。这 点你可以在5.6节"Working Programmatically with View Object Query Results",中看到,你可 以使用这些视图对象属性名通过名字访问视图对象的结果集中的任一行。你可以直接使用SQL列名 作为java中的属性名,但是SQL名都是大写的,并且有多个、下划线分割单词组成。视图对象向导 将这些SQL名字转化为Java友好的名字。 在第四步中的Attribute Mapping页中,如图5-4所示,你可以看到SELECT列表列名如何与更多 的向导默认创建的java友好的视图对象属性名相对应。下划线分割的列名的每一部分,如 SOME-COLUMN_NAME 在属性名中被转化成一个首写字母大写的单词。虽然视图对象属性名潜在的与 SELECT列表中的查询列相关,视图对象级的属性名不一定匹配。你以后可以不用改变潜在的 查询,就将视图对象的属性重命名成任何一个更准确的名字。 注:你可以从ADF业务组件向导和编辑器中了解到,缺省的转化使用“CamelCapped” 属性名,以一个字母开头,当名字由多个单词组成时,在名字的中间使用大写字母以提高可 读性。 图5-4 向导为Select列表中的每列创建默认的java友好的属性名。

此时单击Finish以创建视图对象


---------------------------------------------------------------142----------------------------------------------------------------------------

5.2.2 当你创建一个只读视图对象时会发生什么 当你创建一个视图对象时,JDveloper首先描述该查询以从SELECT列表中的列推断如下信息: ■ Java友好的视图属性名(如 USER_ID -> UserId) ■ SQL 和每个属性的Java数据类型 JDeveloper 然后创建代表视图对象的声明性设置的XML组件定义文件,然后保存在与包的名字 对应的目录中。在上边的例子中,视图对象在devguide.examples 包中命名为Users,因此创建的 XML文件在工程的源路径中的该XML文件包含你键入的SQL查询及每个属性的名字、数据类型和其他 的属性。如果你想查看它的内容,你可以在Application Navigator 通过选择视图对象查看其XML 文件,并可以在Structure窗口中查看相应的Sources文件夹。双击Users.xml 节点可以在编辑器中 打开该Xml文件方便查看。 注: 如果你的IDE的Business Components Java generation引用明确的话,向导还 以创建一个可选的定制视图对象类UsersImpl.java,并且/或者 一个定制视图行 UsersRowImpl.java

可 类

5.2.3 你可能需要了解视图对象的什么内容 典型的情况下,你为你的应用要执行的每个SQL查询创建一个视图对象。 5.2.3.1 编辑现有的视图对象的定义 在你创建了视图对象之后,你可以通过View Object Editor编辑它的任一个设置。在 ApplicationNavigator中的上下文菜单中选择Edit 菜单选项,或者双击视图对象启用对话框。通 过打开编辑器的的不同面板,你可以调整SQL查询、修改属性名、增加命名的绑定变量、增加UI控 制链、控制JAVA产生选项和其他的在后续的章节中将要描述的设置。 5.2.3.2 使用包含SQL表达式的查询 如果你的SQL查询包含一个可计算的表达式如: select USER_ID, EMAIL, SUBSTR(FIRST_NAME,1,1)||'. '||LAST_NAME from USERS order by EMAIL 在Create View Object向导中使用Java友好名字命名列时,可以使用SQL 别名: select USER_ID, EMAIL, SUBSTR(FIRST_NAME,1,1)||'. '||LAST_NAME AS USER_SHORT_NAME from USERS order by EMAIL

---------------------------------------------------------------143----------------------------------------------------------------------------


5.2.3.3 控制视图对象属性的长度、精确度和范围 如图5-5所示,通过在视图对象中选择一个特殊的属性名,你可以查看和修改控制其运行时行 为的声明性设置值。一个重要的属性是Query Column 部分的Type.。该属性记录列的SQL类型,包 括VARCHAR2列的长度信息、Number列的精度和范围信息。JDeveloper编辑器视图自动的推断列的数 据类型,但是对一些SQL表达式来说,推断的值可能是VARCHAR2(255).如果你知道具体的信息,你 可以为这种属性更新Type 值以映射正确的长度。例如,图5-5中FirstName的type VARCHAR2(30) 意味着最多有30个字符。对于一个NUMBER列,你如果要指定一个有7为数字,两位小 数的数字类型可以用Type NUMBER(7,2)来表示。 Figure 5–5为一个视图对象属性编辑设置

5.3 在应用模块数据模型中使用视图对象 你创建的任何一个视图对象都是一个可重用组件,它们可以用在一个或者多个应用模块中,执 行封装在组件内的该应用模块的事务的查询。由应用模块定义的视图对象集第一它的数据模型,也 就是客户端可以通过用户接口显示和操作的数据集。要启动、创建应用模块,使用你在上面应用模 块的数据模型中创建的单个视图对象

---------------------------------------------------------------144----------------------------------------------------------------------------


5.3.1 如何创建一个应用模块 要创建应用模块,可以使用Create Application Module wizard. 该向导可以从Business Tier > ADF Business Components 目录中的New Gallery打开。如图5-6所示,提供一个包名和一个应用 模块名便可以创建一个应用模块。该图展示了如何在 devguide.examples 包中创建一个名为 UserService的应用模块。 图5-6 为一个新的应用模块定义包名和组件名

在Data Model页的第二步中,图5-7演示了最初的数据模型是空的。也就是说,目前它不包含 任何视图对象。Available View Objects 列表显示了你工程中由包组织的所有可用的视图对象。 图5-7 为新的应用模块定义数据模型

---------------------------------------------------------------145----------------------------------------------------------------------------


要为数据模型新增一个视图对象的实例,首先在“Available”列表中选择它。列表下的名称栏 显示的名称将被用来确定下一个添加到数据模型中的视图对象实例。在点击“>”按钮前,可以键 入任何名称。最后,点击“>”按钮,将选中的视图对象实例以显示出来的名称加入到数据模型中。 假设你决定实例的名称为userlist,图5-8显示了Data Model页在添加实例后的样子。数据模型列表中, 选中的视图对象下的实例名字段可以按需修改。定义栏显示了被用来在运行时创造视图对象实例的 视图对象组件的fully-qualified名。如预期中,该定义将使用的是devguide.examples.users View对象。 Figure 5–8 Data Model With One Instance Named UserList of the Users View Object

5.3.1.1 理解视图对象组件和视图对象实例之间的差别 理解视图对象组件和视图对象实例之间的差别是很重要的。最简单的方式是首先考虑可视的例 子。想象需要建立一个有两个按钮的 Java 用户界面。利用 JDeveloper 的可视化编辑器,可以用 Component Palette,选择 JButton 组件,在面板上点击添加一个 JButton 组件(如图 5-9 所示) 。重 复同样的步骤,可以将另一个按钮拖到面板。用两个 JButton 组件设计一个自定义 jpanel 组件。面 板不属于 JButton 类,只是使用它的两个实例。

----------------------------------------------------------146-------------------------------------------------------------


Figure 5–9 Designing a Panel That Contains Two Instances of the JButton Component

如果查看正设计的新面板的 Java 代码,注意,有两个 JButton 类型的成员被使用。为了区分 的两个实例的代码,一个命名为 myButton,另一个为 anotherButton。 Example 5–1 Two Instances of a JButton Component public class Panel1 extends JPanel implements JUPanel { private JButton myButton = new JButton(); // First instance private JButton anotherButton = new JButton(); // Second instance // etc. }

即使应用模块是一个非可视化组件,仍然对组件和实例套用相同的方法,不同的成员名称,可 以更好的帮助理解的这些概念更好。虽然设计的是应用模块,但也可以用视图对象组件的实例去确 定其数据模型。图5-10显示了UserService应用模块的业务组件图。以图5-1的面板为例,包含了两 个JButton实例,用不同的名字myButton和anotherButton加以区分。应用模块包含了UserList 和AnotherUserList两个成员。在运行时,两个jbutton的实例都是基于相同的定义- 这也就解释了 为什么他们都有相同的一组属性以及展示出类似的行为。不过,位置和文字属性值是不同的,Users 和UserService视图对象也是一样。在运行时,两个实例共享同一个Users视图对象组件定义—确保 他们有相同的属性结构和视图对象行为—不过,每个可能被独立的用来为不同的用户检索数据。举 例来说,类似于额外的WHERE子句过滤或者绑定变量值这样一些运行时属性,不同的实例可能也 有所不同。

----------------------------------------------------------147------------------------------------------------------------------


Figure 5–10 Designing an Application Module That Contains Two Instances of the Users View Object Component

除了明显的事实,即其中一个例子是可视化面板而另一个是非可视化数据模型组件,唯一合乎 逻辑的差异这些实例和成员名是如何被定义的。在图 5-1 的的可视化面板中,可以看到代码中声明 了两个拥有不同 JButton 实例的成员,在此相反,userservice 应用模块在 XML 组件定义文件其定义 中定义了视图对象实例。 Example 5–2 Application Modules Define Member View Objects in XML <AppModule Name="UserService"> <ViewUsage Name="UserList" ViewObjectName="devguide.examples.Users"/> <ViewUsage Name="AnotherUserList" ViewObjectName="devguide.examples.Users"/> </AppModule>

5.3.2

在创建一个应用模块的时候会发生什么

当创建一个应用程序模块的时候,JDeveloper创建了XML组件定义文件,保存了已有设置并存 储在对应包的目录下。以图5-6为例,命名为userservice的应用模块,属于devguide.examples包,所 以XML文件将创建为当前工程下目录下的./devguide/examples/UserService.xml文件。此 XML文件包含了运行时在应用模块的数据模型中创建视图对象所需要的信息。要查看其内容,可以 通过在Application Navigator中选择可视化对象并在结构窗口中查看相应源代码。双击 usersservice.xml节点将打开XML在编辑器,即可以检查它。

Note: 如果IDE级别的业务组件Java生成器有相应说明,向导也可以创建一个可选的定制应用软件模

块类usersserviceimpl.java.

--------------------------------------------------------------148---------------------------------------------------------


5.3.3 对于应用模块需要知道些什么

创建应用模块后,可使用应用模块编辑器来编辑其任何设置,选择 Application Navigator 中的 EDIT 菜单来运行应用模块,通过打开编辑器的不同面板,可以在数据模型中调整视图对象实例、中,

控制 Java 生成选项,和将在以后章节中学习到的其他设置。

5.3.3.1 Editing an Application Module's Runtime Configuration Properties 因为可以方便的定义和使用多套运行时配置属性,每个应用模块支持多种为运行时配置。当创 建一个应用程序模块时,JDeveloper 创建一个默认运行时配置。一个名为 yourservice 的应用模块, 其默认配置属性名为 yourservicelocal,这些设置储存在应用模块的 XML 组件所在目录的 common 子目录下,名为 bc4j.xcfg 的 XML 文件中,例如,当在 devguide.examples 包上创建 userservice 应 用模块时,JDeveloper 创建在当前工程路径下的./devguide/examples/common 目录下,创建 bc4j.xcfg 文件。 可以使用的应用模块配置管理器来编辑现有配置或建立新的模块。在 Application Navigator 中选择 所需的应用模块并且从菜单中选择 Configurations。如图 5-11,将出现 Configuration Manager 配置 管理器对话框,可以看到 userservice 应用模块的默认 userservicelocal 配置。创建任何额外的配置, 或编辑现有属性并保存在同一 bc4j.xcfg 文件中。 Figure 5–11 Application Module Configuration Manager

单击配置管理器的编辑按钮以编辑特定的配置。如图 5-12,编辑器允许配置数据库连接信息, 一些 pooling and scalability 设置,不能被头两个选项卡覆盖的保留属性的最后选项卡,其余的属性不 包括首两年的标签。所有的运行时属性和及其含义在 JDeveloper 联机帮助中都能找到。

-------------------------------------------------------149----------------------------------------------------------------


Figure 5–12 Oracle Business Component Configuration Editor

Note: 当构建的 Web 应用程序时,设置 jbo.locking.mode 特性为乐观,默认值是对于 Web 应用程序

来说不正确的悲观。可以在属性选项卡的配置编辑器中,按照字母顺序排列的属性列表中找到。

5.4 定义控制提示属性

ADF业务组件的许多强大的内置功能中,其中一个是在属性中定义控制提示。控制提示是额外 的属性设置,可以让视图层能给用户一种前后一致的,区域敏感的方式来自动显示疑问信息。 JDeveloper管理存放提示的方式,可以很容易将多种语言的应用本地化。

5.4.1 如何添加属性提示 为UserList视图对象添加属性控制提示,打开视图对象编辑器并在左边的树形结构中展开属 性节点。如图5-13,选择某一特定的属性名称,如UserID,并选择控制提示选项卡,在Label Text 栏中输入ID,也可以为数字设定格式类型,输入格式掩码00000。可以选择其他属性来改变Label Text 提示,如为电子邮件定义“电子邮件地址”,“姓名”,“姓”,以及各自的名字,姓氏属性。

Java 为数字和日期定义了一套标准的格式掩码, 但对于 Oracle 数据库的 SQL 和 PL / SQL 语言是不同的,请参阅 java.text.decimalformat 和 java.text.simpledateformat 类的 javadoc Note:

------------------------------150--------------------------------------


Figure 5–13 Setting UI Control Hints for View Object Attributes

5.4.2 增加属性控制提示属性时会发生什么 当为视图对象定义属性控制提示时,JDeveloper创建一个标准的Java消息包文件来存储它们。该 文件对视图对象是相关且明确的,并且有相应的名字,对devguide.examples包的userlist视图对象, 消息包创建文件将被命名为userlistrowimplmsgbundle.java ,并创建在devguide.examples.common subpackage里。在Application Navigator中选择userlist组件,可以在结构窗口中看到这个新文件添加 到了源文件夹中,结构窗口为每一个业务组件显示了执行文件,例5-3显示了在信息包文件中如何显 示控制提示信息。每个字符串数组的第一项是一个信息KEY,第二项该信息KEY的locale-specific值。 Example 5–3 View Object Component Message Bundle Class Stores Locale-Sensitive Control Hints

package devguide.examples.common; import oracle.jbo.common.JboResourceBundle; // --------------------------------------------------------------------// --- File generated by Oracle ADF Business Components Design Time. // --------------------------------------------------------------------public class UsersRowImplMsgBundle extends JboResourceBundle { static final Object[][] sMessageStrings = { { "UserId_LABEL", "Id" }, { "UserId_FMT_FORMATTER", "oracle.jbo.format.DefaultNumberFormatter" }, { "UserId_FMT_FORMAT", "00000" }, { "Email_LABEL", "Email Address" }, { "FirstName_LABEL", "Given Name" }, { "LastName_LABEL", "Surname" } };

--------------------------------------------------------151--------------------------------------------------------------


5.4.3

关于信息包需要知道些什么

国际化应用的模块层,建立使用ADF应用模块继承每一个组件信息包文件生成的翻译版本。 例如,意大利语的usersrowimplmsgbundle消息包,其包名为usersrowimplmsgbundle_it,特别的瑞士 的意大利语版本名为usersrowimplmsgbundle_it_ch,这些类有代表性的扩展了基础信息包类,并且包 含了需要本地化的信息KEYS的入口,把本地化翻译集中在一起。例如,假设不想翻译数字格式为 意大利语区域,那意大利语版本的UserList视图对象将如例5-4所示。注意重载的getContents() 方法,它返回一个具体的翻译字符串与没有重载的字符串合并在一起的消息数组。在运行时,基于 当前用户区域设定,自动使用属性信息包。

Example 5–4 Localized View Object Component Message Bundle for Italian package devguide.examples.common; import oracle.jbo.common.JboResourceBundle; public class UsersRowImplMsgBundle_it extends UsersRowImplMsgBundle { static final Object[][] sMessageStrings = { { "UserId_LABEL", "Codice Utente" }, { "Email_LABEL", "Indirizzo Email" }, { "FirstName_LABEL", "Nome" }, { "LastName_LABEL", "Cognome" } }; // merge this message bundles messages with those in superclass bundle public Object[][] getContents() { return super.getMergedArray(sMessageStrings, super.getContents()); } }

5.5

用业务组件浏览器测试视图对象

JDeveloper 包括互动式应用模块的测试工具,可以在不必使用应用程序的用户界面,或写一个 测试客户端程序的情况下,测试数据模型的各方面。在开发过程中,它是业务服务的最快的训练数 据功能性的方法。

5.5.1

如何用业务组件浏览器测试视图对象

要测试一个应用模块,在 Application Navigator 中选中它,并选择在菜单中选 Test。如图 5-14 显示的 Business Component Browser Connect 对话框,在右上角的配置名单列表允许为当前运行的测 试工具选择任何应用模块。单击 Connect 用选定的配置启用应用模块。

------------------------------------------------------152----------------------------------------------------------------


Figure 5–14 Business Component Browser

5.5.2

当使用业务浏览器的时候会发生什么

当启动该业务组件的浏览器,JDeveloper 在一个单独的进程启动测试工具并出现业务组件浏览 器。对话框左侧的树显示所有在应用模块的数据模型中的视图对象实例。在图 5 月 15 日中只有一 个名为 UserList 的实例,如果到目前为止它没有在测试任务中被执行,在树中双击 UserList 视图对 象实例执行该视图对象,显示出一个面板来观察查询结果。其他的在视图对象节点菜单项,可在有 需要时重新执行查询,删除测试面板和执行其他任务。 Figure 5–15 Testing the Data Model in the Business Component Browser

------------------------------------------------------------153----------------------------------------------------------


应为UserList视图对象是只读的,所以其域在测试面板中是禁止的。在7.5节中,可以看到通过 允许在视图对象中做插入、更新和删除实验,测试工具变得更加有价值。但即使是只读的视图对象, 该工具也提供了一些有用的功能。首先,您可以验证用户界面的提示和格式掩码的定义是正确的。 属性显示与他们所界定的标签文字控制提示作为提示,由于在5.4.1节中定义的00000格式掩码,用 户名栏显示为00303,其次,可以使用工具栏按钮滚动数据。 最后,可以输入query-by-example准则,以找到想要检查的一个特定行的数据。通过点击工具栏上的 Specify View Criteria按钮,View Criteria对话框显示如图5-16,在姓氏的属性栏输入一个类似“H %” 查询标准,点击找到缩小搜索只有姓氏开头字母为H用户(Hunold, Himuro, Hartstein, and Higgins)

Figure 5–16 Exercising Built-in Query-by-Example Functionality

5.5.3

关于业务逻辑浏览器可能需要些什么

当使用业务组件的浏览器,可以为当前运行自定义配置选项,也可以使 ADF 业务组件的调试 诊断输出信息到控制台。这两项功能可以测试应用程序的不同选项或发现问题。

5.5.3.1 为当前运行字定义配置选项 如图5-14描述,在业务组件浏览器的连接对话框中,可以选择一个预定义的配置,以运行该工 具,使用该命名的一套运行时配置属性。连接对话框还具有属性选项卡,允许查看所选的配置设置, 并为浏览器的当前运行重新设置任何组配置设定。例如,通过打开属性选项卡并设定以下两个属性, 可以为一个单独的业务组件浏览器测试用户界面控制提示的意大利语言翻译功能。 ■

jbo.default.country = IT

jbo.default.language = it

----------------------------------------------------------154-----------------------------------------------------------


如果你想经常的改变,你可使用配置管理器复制当前的 UserServiceLocal 配置文件或者新建一 个 UserServiceLocalItalian 这两个附加的属性设置。这样,你不管任何时候想在 Italian 中测试,你都 可以很容易的使用 UserServiceLocalItalian 配置来替换默认的 UserServiceLocal。 5.5.3.2 使用 ADF 的业务组件调试诊断 在 打 开 业 务 组 件 浏 览 器 时 , 如 果 你 目 前 运 行 的 工 程 的 配 置 中 设 置 了 JAVA 系 统 参 数 jbo.debugoutput=console,你就可以使用 ADF 业务组件调试工具把消息定向到控制台。这些消息将 会在 Jdeveloper 的日志窗口显示。 Note: Despite the similar name, the JDeveloper project's run

configurations are different from the ADF application module's configurations. The former are part of the project properties, the latter are defined along with your application module component in its bc4j.xcfg file and edited using the configuration editor.

在你的模型工程中打开“Project Properties”对话框中的“Run/Debug”项,并按照上面的描述设置 系统属性。点击“Edit”按钮编辑选择运行配置并在左边面板中的“JavaOptions”文本框中添加字 符串: -Djbo.debugoutput=console

这样你下次再运行业务组件浏览器并且双击工程的“UserList”视图,你将看到控件台输出的详细 诊断信息。 Example 5–5 Diagnostic Output of Business Component Browser

------------------------------------------------155------------------------------------------------------------------------


使用诊断方法,你可以看到这个框架的组件在你应用中所执行的任何操作。 这属性的其它作用是 silent(默认,未指定的)和 file。如果你选择 file 选项,诊断信息将会被写 到系统的临时目录下。最好的做法应该是建立多个 Jdeveloper 运行配置,一个可以设置打开 ADF 的业务组件调试诊断,另一个不设置。这样你就可以选择适当的运行配置,很容易的调节是否在工 程中使用调试。

5.6

关于视图对象查询结果的工作情况

现在,你有一个工作的应用模型中有一个叫 UserList 的实例,你可以创建一个简单的客户端测 试程序来举例说明视图对象实例 UserList 关于数据的工作情况。

5.6.1

关于视图对象默认的行集合的常用方法

在包 oracle.jbo 中的 ViewObject 接口提供了一些用于快速检索数据的方法的任务,一些方法的使用 如下例子包括: ■ executeQuery(),执行视图对象的查询操作,并在结果集中封装了行集合。 ■ setWhereClause(),添中动态的条件语句来实现条件查询。 ■ setNamedWhereClauseParam(),给指定的变量赋值。 ■ hasNext(),用于测试是否连续设置迭代已达到最后一行结果。 ■ next(),用于迭代下移到下一行. ■ getEstimatedRowCount(),将返回一个视图对象查询得到的总行数。 Chapter 27, "高级视图对象技术"

将介绍这种情况;可是,在大部分的时候,你的工作在一时刻只使用一个单例的结果集。同样后 面的章节,还将介绍关于一个行集合中创建多个连续不同的行集合迭代。然而,大部分的时间你又 仅仅只需要一个单一的迭代。正如您会看到在下面的示例中,这视图对象包含了一个默认的RowSet, 包含一个默认的RowSetIterator。你从下面例子可以看出,默认的RowSetIterator也可以直接的调用上 面ViewObject组件的吧有方法。他们将应用于默认连续的行集合。 Figure 5–17 ViewObject Contains a Default RowSet and RowSetIterator

-------------------------------------------------156----------------------------------------------------------------------


Note: Throughout this guide, whenever you encounter the phrase

"working with the rows in a view object," what this means more precisely is working with the rows in the view object’s default row set. Similarly, when you read "iterate over the rows in a view object," what this means more precisely is that you'll use the default row set iterator of the view object's default row set to loop over its rows.

关于在某部分的概念,你可以创建一个客户测试程序,加以实现。

5.6.2 计算行集合里的行数 The getEstimatedRowCount()方法用于判断一个行集合里有多少行: long numReqs = reqs.getEstimatedRowCount();

刚开始执行 getEstimatedRowCount()方法时是发送 select count(*)查询语句来计算行数,然后返回。 这查询简述为“wapping”,如下声明了完整的视图对象查询: SELECT COUNT(*) FROM ( ... your view object's SQL query here ... )

你可以通过这个步骤得到视图对象的行数,而不用重新查询,这对于返回查询行数多的操作或在测 试时对结果集操作之前返回行数是最优化的。 一旦行数被算出,以后对于并发调用这方法的操作,都不用重新执行 Count(*)查询。这个值将 被存在缓冲里,直到下次这个视图对象被再次执行。以后数据库里获得的行会与最后一次的查询比 较,若有不同则再运行,以致返回最新的行结果集。在计算行数的时候,会自动的解决当前事务中 未更新的数据,并往里添加有关的新行,去掉已删除的行再返回。

5.7

怎样创建JAVA客户端测试命令行

创建一个客户端测试程序,使用 Create Java Class wizard 创建一个新的 Java 类。这用到的是“New Gallery”窗口下的“General”项。输入一个类名,如 TestClient,包名如 devguide.examples.client, 并确保“Extends”文本框内的值是 java.lang.Object。在“Optional Attributes”项处取消“Generate Default Constructor”前的勾,并选中“Generate Main Method”。然后单击 OK 按钮创建 TestClient.java 文件。 这文件将会在代码编辑器中显示出一些基本代码。

Example 5–6 Skeleton Code for TestClient.java package devguide.examples.client; public class TestClient { public static void main(String[] args) {} }


----------------------------157--------------------------------------------

把鼠标放在 main()方法里面的空行处,然后使用 bc4jclient 代码模板生成一些必要的代码。使 用这些预先定义的模板时,你可以按[Ctrl]+[Enter]组合键,并在显示出来的窗体里选择 bc4jclient。 你就可以看见如下图的类: Example 5–7 Expanded Skeleton Code for TestClient.java package devguide.examples.client; import oracle.jbo.client.Configuration; import oracle.jbo.*; import oracle.jbo.domain.Number; import oracle.jbo.domain.*; public class TestClient { public static void main(String[] args) { String amDef = "test.TestModule"; String config = "TestModuleLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); ViewObject vo = am.findViewObject("TestView"); // Work with your appmodule and view object here Configuration.releaseRootApplicationModule(am,true); } }

调整变量 amDef 和 config 的值来反应出与这些变量所对应名称的应用模型的定义和配置。例如 例 5-7,你可以更改这两行的值: String amDef = "devguide.examples.UserService"; String config = "UserServiceLocal";

最后,在 findViewObject()主方法中更改你想要使用的视图对象实例的名称。这样指定的名称就 会出现在应用模型编辑器中的“Data Model”面板中的“Data Model”的树中。这里,有一个名叫 UserList 的视图对象实例,你需要更改的行是: ViewObject vo = am.findViewObject("UserList");

在这里,对于 UserService 应用模型你就有一个测试框架,它的源代码就像例 5-8 你所看到的。

Note: Section 8.5, "Working Programmatically with an Application

Module's Client Interface" expands this test client sample code to illustrate calling custom application module service methods, too.


-------------------------------------------158-----------------------------------------------------------------------Example 5–8 Working Skeleton Code for an Application Module Test Client Program package devguide.examples.client; import oracle.jbo.client.Configuration; import oracle.jbo.*; import oracle.jbo.domain.Number; import oracle.jbo.domain.*; public class TestClient { public static void main(String[] args) { String amDef = "devguide.examples.UserService"; String config = "UserServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); ViewObject vo = am.findViewObject("UserList"); // Work with your appmodule and view object here Configuration.releaseRootApplicationModule(am,true); } }

执行视图对象查询操作,并在控制台上打印出返回的行数和循环结果集里的数据,并打印出来。在 这里替换你的应用模型和视图对象,代码如 5-9: Example 5–9 Looping Over a View Object and Printing the Results to the Console System.out.println("Query will return "+ vo.getEstimatedRowCount()+" rows..."); vo.executeQuery(); while (vo.hasNext()) { Row curUser = vo.next(); System.out.println(vo.getCurrentRowIndex()+". "+ curUser.getAttribute("UserId")+" "+ curUser.getAttribute("Email")); }

第一行是调用getEstimatedRowCount()显示重新查询得到的行数。下一行是调用executeQuery() 方法来执行视图对象查询。你可能通过while的循环直到视图对象的hasNext()方法返回false来得到结 果集,可能得到一行或0行或者更多的行。在循环的内部,代码把当前行(ROW)赋给变量curUser, 然后调用getAttribute()方法来获取当前ROW对象的UserId和Email属性的值,并在控制台打印出来。

5.7.1

你运行客户端测试程序产生的效果

当你在代码编辑器选择右击出来的菜单上的“RUN”来运行 TestClient 类时,你可以从日起窗 口看到你测试的结果。注意在例 5-10 中显示使用的 getCurrentRowIndex()方法的行索引号是从 0 开始的。


----------------------------159------------------------------------Example 5–10 Log Output from Running a Test Client

Query will return 27 rows... 0. 303 ahunold 1. 315 akhoo : 25. 306 vpatabal 26. 326 wgietz

调用 Configuration 对象的 createRootApplicationModule()方法返回一个现在工作的 UserService 应用 模型实例。正如你在调试诊断的输出中所注意到的,ADF 业务组件运行类加载 XML 组件的定义作 为应用模型和你在设计时期定义的数据模型的视图对象组件所必需的实例。findViewObject()方法是 从应用模板的数据模型中通过名字查找应用模型的视图对象实例。后面的例 5-9 将描述的是调用 Configuration 对象上的 releaseRootApplicationModule()来通知你可以使用应用模型和框架清理资源, 如被应用模块使用的数据库连接。

5.7.2 运行客户端测试程序你所要了解的事项 在访问应用模块组件时,createRootApplicationModule()和 releaseRootApplicationModule()这两个 是非常有用的方法,但是,在若 ADF WEB 应用和 Swing 应用时你就不能任何时候都写这两个方法。 在这种情景下,ADF 数据邦定模型层会与 ADF 业务组件层自动配合来获得和释放应用模块组件。

5.8 使用QBE(Query-By-Example)的视图条件来过滤结果集 倘若在运行时,用户想对一个视图对象进行条件查询,你需要过滤这个视图对象的结果集,则 你可以在这个视图对象上通过ViewCriteria类来实现。这种视图条件是由一个或多个的视图条件行组 成的一个行集合,他们的属性就是视图对象里的属性。一个普通的查询行和视图条件行的主要区别 是在条件查询行中每个属性的数据类型都是String,并允许QBE开发人员输入如“>304”。

5.8.1 怎样使用视图条件过滤视图对象结果集 使用一个视图条件,可以在 TestClientViewCriteria 类中按以下几个步骤操作,如例 5-11: 1. createViewCriteria():用于创建一个空的视图条件行集合过滤器。 2. createViewCriteriaRow():创建一个或多个空的视图条件行集合。 3. setAttribute():用于设置过滤的属性。 4. add():用于在视图条件行集合中添加视图条件行。

------------------------------------------------160----------------------------------------------------------------------------


5. applyViewCriteria():用于在视图对象上使用视图条件。 6. executeQuery():用于执行已应用的过滤条件的查询语句。

最后一步执行的查询是最重要的,以后应用的视图条件只仅仅将用于视图对象的SQL查询,按下面 执行。 Example 5–11 Creating and Applying a View Criteria

package devguide.examples.client; import oracle.jbo.ApplicationModule; import oracle.jbo.Row; import oracle.jbo.ViewCriteria; import oracle.jbo.ViewCriteriaRow; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; public class TestClientViewCriteria { public static void main(String[] args) { String amDef = "devguide.examples.UserService"; String config = "UserServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef, config); ViewObject vo = am.findViewObject("UserList"); // 1. Create a view criteria rowset for this view object ViewCriteria vc = vo.createViewCriteria(); // 2. Use the view criteria to create one or more view criteria rows ViewCriteriaRow vcr1 = vc.createViewCriteriaRow(); ViewCriteriaRow vcr2 = vc.createViewCriteriaRow(); // 3. Set attribute values to filter on in appropriate view criteria rows vcr1.setAttribute("UserId","> 304"); vcr1.setAttribute("Email","d%"); vcr1.setAttribute("UserRole","technician"); vcr2.setAttribute("UserId","IN (324,326)"); vcr2.setAttribute("LastName","Baer"); // 4. Add the view criteria rows to the view critera rowset vc.add(vcr1); vc.add(vcr2); // 5. Apply the view criteria to the view object vo.applyViewCriteria(vc); // 6. Execute the query vo.executeQuery(); while (vo.hasNext()) { Row curUser = vo.next(); System.out.println(curUser.getAttribute("UserId") + " " + curUser.getAttribute("Email")); } Configuration.releaseRootApplicationModule(am, true); }


} Running the TestClientViewCriteria example in Example 5–11 produces the output: 305 daustin 307 dlorentz 324 hbaer

5.8.2 使用视图条件来过滤视图对象结果集所将发生的 当在一个对象视图中应用一个包含一个或多个视图条件行的视图条件时,下次执行时它会在相 应的位置增加一个额外的 Where 子句,QBE 的条件将添加在视图条件行里。如图 5-18 所示,当 应用一个包含多个视图条件行的视图条件时,视图对象增加 Where 子句通过添加一个额外的运行时 Where 子句,不过它是基于每个条件行的条件属性不为空。 Figure 5–18 View Object Automatically Translates View Criteria Rows into Additional Runtime WHERE Filter

5.8.3 你可能需要了解一下QBE 你可能需要知道 QBE 的几个事项。包括在业务组件浏览器怎样测试视图条件,使用多个视图 条件行怎样修改复合型的搜索条件;一个行的属性值为 NULL 的查询,清除结果中的视图条件,和 怎样应用重新解析的视图条件。

5.8.3.1 在视图条件中使用属性名,在 Where 子名中使用列名 在 5.6.1 的“视图对象的默认 RowSet 中的常用方法” 部分,你可以看到 setWhereClause()方法允许 你在一个视图对象中添加动态的 Where 子句。通过这章节的下面例子你可以看到,当你使用 setWhereClause()方法时你得通过一个字符串,它包含了数据库的列名,像下面这样: vo.setWhereClause("LAST_NAME LIKE UPPER(:NameToFind)");

相反,当你使用视图条件查询机制,你可以看到像上面的例 5―11 那样,所涉及到列名都将被视图 对象的属性替代,如下面这样: criteriaRow.setAttribute("LastName","B%");

正如上面解释的,视图条件能够把视图用到的列名都转换成相应的视图对象中的属性的 where 子句。

-------------------------------------------------162----------------------------------------------------------------------------

5.8.3.2 视图条件在业务组件浏览器中的测试


如图 5-19 所示,选择任何一个视图对象实例,然后点击工具栏的“Specify View Criteria”图 标,则显示“Business Component View Criteria”对话框。在这对话中可以创建一个由一个或者多个 视图条件行组成的视图条件。应用一个单一的视图条件行中的属性,在相应的文本框中输入 QBE 条件,并按“find”按钮查找。添加其他视图条件行,点击“OR”,并使用附加的标签实现页面转 换。每个都代表一个不同的视图条件行。当你点击“find”按钮时,业务组件浏览器使用与上面相 同的 APIs 来创建和应用过滤结果集的视图条件。 Figure 5–19 Creating a View Criteria with One or More Rows in the Business Component Browser

5.8.3.3 使用多个视图条件行来修改复合查询条件 当你要添加多个视图条件行,你可以调用视图条件行上的 setConjunction()方法来修改行相应的 声明和另一行上一级的视图条件的连接。规定的常量是如下面: ■ ViewCriteriaRow.VCROW_CONJ_AND ■ ViewCriteriaRow.VCROW_CONJ_NOT ■

ViewCriteriaRow.VCROW_CONJ_OR (default)

“NOT”可以与“AND”和“OR”配合使用,创建的过滤规则如下: ( PredicateForViewCriteriaRow1) AND ( NOT( PredicateForViewCriteriaRow2 ) ) 或者 ( PredicateForViewCriteriaRow1) OR (NOT( PredicateForViewCriteriaRow2 ) ) 语法要达到这些要求,则应该使用 Java 的按位或,如下面所示: vcr2.setConjunction(ViewCriteriaRow.VCROW_CONJ_AND | ViewCriteriaRow.VCROW_CONJ_ NOT);

-----------------------------163-------------------------------------

5.8.3.4 搜索一个ROW的属性值为NULL 若查询一行中有空值的列,则就会用“IS NULL”值作为视图条件行中相应的属性的值。


5.8.3.5 查询大小写敏感 查询大小写敏感,可以调用视图条件行的setUpperColumns(true)方法实现。在一个视图对象中 使用upper(column_name)来替换column_name,这将影响where子句String-valued属性的产生。注意为 视图条件行提供的属性的值中的String-valued属性必须转换成大写否则声明不匹配。

5.8.3.6 在结果中清除视图条件 要在一个结果中清除视图条件,您可以调用视图对象上的getViewCriteria()方法,然后再调用 remove()方法来删除所有的视图条件行,你所删除的条件行的索引是从0开始的。如果你不想再添加 其他的的视图条件行,你也可以调用视图对象上简化过的applyViewCriteria(null)来从结果中删除视 图条件。

5.8.3.7 应用视图条件会引起重新解析的查询 以上所描述的视图条件功能在每次创建视图条件(或者删除一个存在条件)时,视图对象的SQL 查询是有效改变的。改变的SQL查询语句会引起数据库重新解析声明,并在下次使用时生效。如果 你打算使用过滤功能为每次规则属性基本上相同的属性提供不同的值,那么使用视图对象的Where 子句中的命名邦定变量将获得更好的性能,这在5.9节的“命名邦定变量”中介绍。与视图条件的过 滤功能相比,使用指定的变量可以在每次不改变视图对象的SQL声明时改变查询条件的值。

5.9

命名邦定变量

你的查询中where子句包含的值可能从一个执行到另一个执行时会随时改变,则你可以使用命 名邦定变量。这些只能用在持有SQL字符串的地方,在运行时你可以轻易的更改,而不用更改SQL 字符串本身的文本。由于查询不改变,则数据库可以有效的重新利用一样的解析声明,这样在多次 执行时可以使你的应用在运行时能获得更高的性能。

5.9.1

添加命名邦定变量

在一个视图对象中添加命名邦定变量,使用“Create View Object wizard”或者“View Object Editoer”中的“Bind Variables”标签。你可以添加所有你需要的命名邦定变量。如图5-20所示,为 每个邦定变量指定名称、数据类型和默认值。你可以为这此变量指定你喜欢的名称,但是以后他们 将用相同的命名空间,因此你所选择的名称不能与现有的属性名称冲突。作为视图对象的属性,按 照习惯命名邦定变量的首字母应大写。

--------------------------------------------164--------------------------------------------------------------------------在“Control Hits”选项上,你也可以指定UI提示,如“Label Text”“ , Format Type”, “Foramt mask” 和其他的,你可以像上面设置视图对象的属性那样操作。当你创建用户界面的视图层,这些邦定变


量的控制提示将自动使用,像查询页面那样,允许用户给命名邦定变量输入值。这“Updatetable” 选项框是控制是否允许用户通过用户界面更改邦定变量的值。如果一个邦定变量是不能更改的,则 它的值只能通过开发者来更改。 Figure 5–20 为一个视图对象定义一个命名邦定变量

定义邦定变量后,下一步就是在SQL声明中关联它们。SQL语法允许邦定变量出现在select列表 和where子句中,你就可以在后面的文中使用它们表示作为where子句的部分。你可以按上面的做法 创建和编辑UserList视图对象,并打开“SQL Statement”页引入你的命名邦定变量,如下所示: select USER_ID, EMAIL, FIRST_NAME, LAST_NAME from USERS where (upper(FIRST_NAME) like upper(:TheName)||'%' or upper(LAST_NAME) like upper(:TheName)||'%') and USER_ID between :LowUserId and :HighUserId order by EMAIL

你可以注意到,你在SQL声明中引用的邦定变量都加上前缀“:”,如:TheName或:LowUserId。 你可以在SQL声明内部中需要的位置任意顺序和多次的重复引用邦定变量。

5.9.2 添加命名邦定变量时将发生的事 一旦你在一个视图对象中添加一个或者多个命名邦定变量,你可以在运行时很容易的看到和设 置这些变量的值。每个邦定变量的名称,类型和默认值等信息都被保存在视图对象的XML组件定义 文件中。如果你为邦定变量了定义了UI控制提示,那么这些信息会连同这个视图对象的其它控制提 示信息一起保存到视图对象的组件信息包文件中。

------------------------------------------------------165---------------------------------------------------------------业务组件浏览器允许你交互的检查和改变一些视图对象中的邦定变量,它能真正的简化你应用 模块中与命名变量参数有关的数据模型。在测试器中第一次执行一个视图对象时,将会出现“Bing


Variables”对话框,如图5-21。通过在列表里选择一个邦定变量,你可以看到它的名称,也可以看 到它的默认值和当前值。要改变任何一个变量的值,可以在点击“OK”按钮之前修改“Value”文 本框中的值来设置邦定变量的值和执行查询。使用工具栏中的“Edit Bind Parameters”按钮-它的 图标看起来如:“:Id”-你可在当前面板中检查和设置视图对象的邦定变量。 Figure 5–21 在测试器上设置邦定变量

如果你要定义“Label Text” 、 “Format Type“或者“Format”控制提示,这“Bind Variables”对 话框能够帮你检查是否已正确设置,若设置成功了你就可以在“Bind Variables”列表中看到,并且 会使用各自的格式模板来格式化“Value”属性中的值。你可以从图5-21中看到,在这个列表中, 有三个邦定变量的标签文本提示。

5.9.3 对于命名邦定变量你所需要知道的事 关于命名邦定变量你需要知道的几件事,包括当邦定变量的名称不匹配和默认值不对时就会抛 出运行时错误。在运行时怎样设置已存在的邦定变量的值,怎样在运行时添加一个新的命邦定变量。

5.9.3.1 与邦定变量有关的所有错误 你要保证列表中的命名邦定变量与你的SQL声明所引用的列表中的命名邦名变量要一致。如果 与上面的观点不同,则在运行时将导致产生以下两种错误。 如果在你的SQL声明中使用命名邦定变量,但又不去定义它,你将得到像下面一样的错误: (oracle.jbo.SQLStmtException) JBO-27122: SQL error during statement preparation. ## Detail 0 ## (java.sql.SQLException) Missing IN or OUT parameter at index:: 1

--------------------------------------------------------166-------------------------------------------------------------另一方面,如果你定义了一个命名邦定变量,但在你的SQL中没有使用它或者对应的类型不对, 你将会看到下面的错误:


oracle.jbo.SQLStmtException: JBO-27122: SQL error during statement preparation. ## Detail 0 ## java.sql.SQLException: Attempt to set a parameter name that does not occur in the SQL: LowUserId

应用仔细的检查以保证SQL列表中的命名邦定变量与在“Bind Variables”页面中列表的命 名邦定变量一致。

5.9.3.2 如果邦定变量没有默认值则默认为NULL值 如果你没有为一个命名邦定变量提供默认值,它会在运行时默认为 NULL 值。这意味着如果你有像: USER_ID = :TheUserId

这样的 Where 子句,并且没有为命名邦定变量 TherUserId 设置默认值,则它将默认为 NULL 值,这样将导 致没有行返回。为了使应用的操作有意义,你可以使用 SQL 函数如 NVL(),CASE,DECODE()或者其他的来处理 这种情况。实际上,视图对象 UserList 使用的 Where 子句的代码片断应该像:

upper(FIRST_NAME) like upper(:TheName)||'%'

一样,以致查询能够匹配任何一个邦定变量的值为空的名称,即TheName is null。

5.9.3.3 在运行时为已存在的邦定变量赋值 在运行时设置命名邦定变量,可以使用ViewObject接口上 setNamedWhereClauseParam()方法。你可以使用Jdeveloper的“Refactor”> “Duplicate…”的功能来创建一个继承TestClient的TestClientBindVars类, TestClient类在5.7节的“怎样创建一个JAVA客户端测试类”中讲过。在这个新的测试类中, 你可以使用几行代码来设置邦定变量HighUserId和TheName的值,代码如例5-12所示。 Example 5–12 为命名邦定变量赋值 // changed lines in TestClient class ViewObject vo = am.findViewObject("UserList"); vo.setNamedWhereClauseParam("TheName","alex%"); vo.setNamedWhereClauseParam("HighUserId", new Number(315)); vo.executeQuery(); // etc.

运行TestClientBindVars类将显示经过你设置的邦定变量过滤过的数据,结果行仅仅匹配 Alexander Hunold和 Alexander Khoo中的一个: 303 ahunold 315 akhoo ---------------------------------167-------------------------------------------

只要这个视图对象的执行查询,运行时调试就会把邦定变量的真实值显示出来,像这样: [256] Bind params for ViewObject: UserList


[257] Binding param "LowUserId": 0 [258] Binding param "HighUserId": 315 [259] Binding param "TheName": alex%

在应用程序中,这些信息在处理一些问题时是非常有用的。注意,在之前代码并没有为邦定变 量LowUserId赋值,这是它在开发时给定了一个默认值0。还应当注意,在Where子句中使用 Upper()函数时,应保证邦定变量TheName的值小写格式。例子中的代码设置了一个邦定变量的 值为“alex%”,用的是一个小写的“a”,但结果显示出来的却是匹配“Alexander”。

5.9.3.4 在运行时添加一个邦定变量 使用视图对象的setWhereClause()方法,在运行时,你可以添加一个附加的过滤子句。运 行时添加的where子句不能替代开发时的where子句,但能套用在开发时的where子句上,从而更 进一步的缩小查询的范围。每当动态的为子句添另一个值时,它在应用程序的周期内可能改变,你 应该使用邦定变量来代替Where子句中的值。 例如,假如你想在运行时对视图对象UserList在已有USER_ROLE的基础上进一步的过滤。再 假如你有时打算查询满足USER_ROLE = 'technician'的行,有时相查询满足USER_ROLE = 'User'的行。那么使用下面的代码是很差的,不符合实际开发,因为下面的代码在对于相同的 USER_ROLE例的两个不同值执行了两次查询。 // Don't use literal strings if you plan to change the value! vo.setWhereClause("user_role = 'technician'"); // execute the query and process the results, and then later... vo.setWhereClause("user_role = 'user'");

相反,你可以添加一个引用邦定变量的的Where子句,在运行时定义,如: vo.setWhereClause("user_role = :TheUserRole"); vo.defineNamedWhereClauseParam("TheUserRole", null, null); vo.setNamedWhereClauseParam("TheUserRole","technician"); // execute the query and process the results, and then later... vo.setNamedWhereClauseParam("TheUserRole","user");

这样就可以使SQL声明的文本保存不变,而不管你需要user_role的值。当通过多次的执行查询内 容一样的文本时,数据库不用重新解析查询就可以返回结果。 如果以后你想删除动态添加的where子句和邦定变量,你可以使用下面的代码: vo.setWhereClause(null); vo.removeNamedWhereClauseParam("TheUserRole");

--------------------------------168-----------------------------------

TestClientBindVars类将举例说明这些技术,在5-13中可以看到。既然这样,就可以使用 executeAndShowResults()方法的功能对结果集做循环,就可以把里面的元素提取出来。这个 程序首先是添加一个附加的where子句即user_id = :TheUserId,之后就用第二个where子句


user_role = :TheUserRole来替换第一个。 Example 5–13 TestClient Program Exercising Named Bind Variable Techniques package devguide.examples.client; import oracle.jbo.ApplicationModule; import oracle.jbo.Row; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; import oracle.jbo.domain.Number; public class TestClient { public static void main(String[] args) { String amDef = "devguide.examples.UserService"; String config = "UserServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); ViewObject vo = am.findViewObject("UserList"); // Set the two design time named bind variables vo.setNamedWhereClauseParam("TheName","alex%"); vo.setNamedWhereClauseParam("HighUserId", new Number(315)); executeAndShowResults(vo); // Add an extra where clause with a new named bind variable vo.setWhereClause("user_id = :TheUserId"); vo.defineNamedWhereClauseParam("TheUserId", null, null); vo.setNamedWhereClauseParam("TheUserId",new Number(303)); executeAndShowResults(vo); vo.removeNamedWhereClauseParam("TheUserId"); // Add an extra where clause with a new named bind variable vo.setWhereClause("user_role = :TheUserRole"); vo.defineNamedWhereClauseParam("TheUserRole", null, null); vo.setNamedWhereClauseParam("TheUserRole","user"); // Show results when :TheUserRole = 'user' executeAndShowResults(vo); vo.setNamedWhereClauseParam("TheUserRole","technician"); // Show results when :TheUserRole = 'technician' executeAndShowResults(vo); Configuration.releaseRootApplicationModule(am,true); } private static void executeAndShowResults(ViewObject vo) { System.out.println("---"); vo.executeQuery(); while (vo.hasNext()) { Row curUser = vo.next(); System.out.println(curUser.getAttribute("UserId")+" "+ curUser.getAttribute("Email")); }


} }

不过,如果你运行这个程序,你实际会得到一个像这样的运行错误: oracle.jbo.SQLStmtException: JBO-27122: SQL error during statement preparation. Statement: SELECT * FROM (select USER_ID, EMAIL, FIRST_NAME, LAST_NAME from USERS where (upper(FIRST_NAME) like upper(:TheName)||'%' or upper(LAST_NAME) like upper(:TheName)||'%') and USER_ID between :LowUserId and :HighUserId order by EMAIL) QRSLT WHERE (user_role = :TheUserRole) ## Detail 0 ## java.sql.SQLException: ORA-00904: "USER_ROLE": invalid identifier

出现在“##” 到 “0##” 的堆栈跟踪根本的原因,是一个 SQL 解析时,数据库报告“USER ROLE ” 列不能被加载。 这非常奇特,这个用户表明明有一个“USER_ROLE”列

5.9.3.5 理解缺省使用只读的内部查询的疑问 如果你灵活的在一个运行的只读的语句中追加一个“WHERE”语句,在应用“WHERE”语句之前 它的查询语句会增加一行语句。 例如:推测你的疑问被定义为: select USER_ID, EMAIL, FIRST_NAME, LAST_NAME from USERS where (upper(FIRST_NAME) like upper(:TheName)||'%' or upper(LAST_NAME) like upper(:TheName)||'%') and USER_ID between :LowUserId and :HighUserId order by EMAIL

在运行时,当你追加一个WHERE 语句“user_role =:TheUserRole”这个测试程序在例子“5-13”, 这个查询语句追加到一行语句像这样: SELECT * FROM( select USER_ID, EMAIL, FIRST_NAME, LAST_NAME from USERS where (upper(FIRST_NAME) like upper(:TheName)||'%' or upper(LAST_NAME) like upper(:TheName)||'%') and USER_ID between :LowUserId and :HighUserId order by EMAIL) QRSLT

然后加上一个动态的“WHERE”语句加到最后,最终的查询语句变成: SELECT * FROM( select USER_ID, EMAIL, FIRST_NAME, LAST_NAME from USERS where (upper(FIRST_NAME) like upper(:TheName)||'%' or upper(LAST_NAME) like upper(:TheName)||'%') and USER_ID between :LowUserId and :HighUserId order by EMAIL) QRSLT WHERE user_role = :TheUserRole


这个查询的“wrapping”包含 SQL 的合并,相交,MINUS 或者其他的一些复杂的操作放到一 个简单的语句,构成一个任意复杂的操作。在那些语句里,在查询语句之后追加简单的“gluing” 会产生出乎意料的结果,例如:它可以仅仅在几个合并的语句应用。在逐字逐句构造这条查询语句 时,尽管语句很复杂,但得保证新追加的 SQL 语句被正确的用于过滤查询的结果。错误 ORA-904 的意思是在新加的 where 字句段仅仅在查询语句用作查询。 在 27.3.3.7, "Disabling the Use of Inline View Wrapping at Runtime"解释了在查询时如何构造在你 不需要这个查询条件时的情况,但是最简单的方法是编辑 UserList 对象,在查询的 SELECT 的 list 的 SQL 语句最后加上 USR_ROLE 列。当然在 SELECT list 后面加入新列后,得加入一个逗号,这 样就可以了:编译器将自动在保证你的对象属性的同时编译新的查询语句。 修改的代码在Example 5–13有详细的叙述。 --303 ahunold 315 akhoo --303 ahunold --315 akhoo --303 ahunold

5.10 关于ter/Ddtail 迄今为止你只是查询单个表。在实践中,我们经常查询多个有关联的主外键关系的表。有两种情况 你可能常遇到: ■

很多查询的信息都是查询结果的补充。 创建没有联系的关系,就是一个相关的信息链接一个“source”,一个或多个“target“形成 master/detail 的层次。

Figure 5–22 图解中展示了两个任意选择产生不同的“shape” 。这种结合就是一个“flattened”的结 果,这 master/detail 连接语句产生一个多级的结果。

----------------------------------------------------------171-----------------------------------------------------------Figure 5–22 Difference Between Join Query Result and Linked Master/Detail Queries


5.10.1 怎样创建一个只读的对象连接表。 创建一个只读的对象连接两个表,作为一个例子来分析。一个简单的例子,创建一个名字叫 OpenOrPendingServiceRequests 连接表 SERVICE_REQUEST 和 USER 。以下是创建的步骤。 第一步先写一条比较接近的简单的语句,第二步在 SQL 语句上加上其他你要查询的表。 如果你想交互补充编译他们,你可以逐渐增加正确的 SQL 语句,点击查询按钮,进行单步的调试。

5.10.1.1

用查询的编译器简化创建连接。

如同用图形 5–23 被表示的那样,在查询语句创建的对话框 Quich-pick 中,你可以在图解里看 到许多包含主外键关系关联的表。查询语句包含的列,单击那些可以利用的来回穿梭的线。图形 5 –23 显示查询表 SERVICE_REQUESTS 的列 SVR_ID, PROBLEM_DESCRIPTION, ASSIGNED_TO, 和 users 表的 EMAIL 列。在表 SERVICE_REQUESTS,外键 SVR_CREATED_BY_USR_FK foreign, select the EMAIL column from the USERS table 查询语句将会自动的连接分句。

-------------------------------------------------------172--------------------------------------------------------------


Figure 5–23 Using the View Object Query Builder to Define a Join

在查询编译器的 WHERE 字句中,穿梭在 where 字句的包里列,全都加上(‘Open’, ‘Pending’) 。 点击编译器的 OK 按钮,创建下列查询语句。 Example 5–14 Creating a Query Using SQL Builder SELECT SERVICE_REQUESTS.SVR_ID SVR_ID, SERVICE_REQUESTS.PROBLEM_DESCRIPTION PROBLEM_DESCRIPTION, SERVICE_REQUESTS.ASSIGNED_TO ASSIGNED_TO, USERS.EMAIL EMAIL FROM SERVICE_REQUESTS INNER JOIN USERS ON SERVICE_REQUESTS.CREATED_BY = USERS.USER_ID WHERE SERVICE_REQUESTS.STATUS IN ('Open', 'Pending')

在查询语句里创造 EMAIL 列。它将表现创建这个请求的每个人的 email。但是它的列名不能同 时被描述。在 5.2.3.2, "Working with Queries That Include SQL Expressions"中你可以了解到一个默认 的缺省的列名的描述。这里你可以采用一种方法。你可以用接下来的 Create View Object wizard 面板 修改目录的属性中修改名字。对象的命名又一次保留了编辑的对象的所有属性。 点击下一步会得到一个属性的设置页面。在这页的上面显示了dropdown下的Email的属性,和 改变名字文件的信息用作CreatedByEmail.。点击完成创建OpenOrPendingServiceRequests对象。创建 一个OpenOrPendingServiceRequests.xml组成定义文件保存了对象的共有的属性。

--------------------------------------------------173---------------------------------------------------------------------

5.10.1.2 测试连接视图


测试一个新的视图对象,编辑用户服务应用模块和数据模型页,添加一个来自于数据模块中 OpenOrPendingServiceRequests 的实例。替换监听 OpenOrPendingServiceRequests 的默认实例名称。 改变 OpenOrPendingServiceRequest1 的实例名。在做这些之后,你将登录业务组件浏览器验证工作 平台中的联合查询

5.10.2 如何运用视图链接创建 Master/Detail Hierarchies 你的需求将显示用户的一组主要数据行,同时遍历主要数据行中一组代码详细行,你创建视图 链接去定义你想要的主要和详细视图对象间的关系。假想你想要链接 UserList 视图对象 去 OpenOrPendingServiceRequests 视图对象去创建用户群中的一个主要/详细层次。

创建视图链接,用创建视图链接向导。这个向导是来自于 New Gallery 在 Business Tier>ADF Business Components .步骤 1,在视图链接的 Name page 上定义一个简短名称和包名,实现这个目标 像 RequestsAssignedTo 就是一个好名字,保持简洁明了。 步骤 2,在 View Objects page 选择来自于视图对象的“source”属性去应用。Figure5-24 显示 选中的 userId 属性在 users view object 中。下一步 选择一个 Corresponding destination attribute 来自 于视图对象 担当详细描述。你想要详细查询去显示服务请求 那是非配给当前选择用户的,选择 AssignedTo 属性在 OpenOrPendingServiceRequests 去显示这个角色。最后 单击 ADD 去添加那个 机器属性 。如果有多样化属性去定义的 master 和 detail 链接。你重复步骤去添加其他的 ource/target 属性对。例如 (UserId,AssignedTo)所有的请求对 .

-----------------------------------------------------------174---------------------------------------------------------Figure 5–24 在创建一个视图链的同时定义与其对应的目标属性


第三步,在视图链的 SQL 页面上,你可以事先查看 SQL 语句,这些语句将被用于在运行时验证目的 行的视图对象与现在所在行的源视图对象。 第四步,在视图链的属性页面,你可以控制你的视图链是单行还是双向的,请注意图 5-25,在为 OpenOrPendingServiceRequests 视图对象定义 group box 时,视图对象产生的补充:用户 Box 已 经 被 检 查 , 相 反 , 在 用 户 视 图 对 象 的 源 group box , 视 图 对 象 的 补 充 为 : OpenOrPendingServiceRequests box 没有被检查,默认的,一个视图链是一个单行关系的, 只允许现在所在行验证目的行视图对象,这个单选框的意思是你可以通过 OpenOrPendingServiceRequests 去验证现在所在的用户视图对象, 但是反过来就不是这样了,因此,默认单向的视图链是很好的,尽量不要勾选其他的单选框,

Figure 5–25 视图链的属性控制名和存取器

-------------------------------------------------175----------------------------------------------------------------------

在 目 标 group box 的 补 充 中 指 出 你 可 以 使 用 程 序 存 取 与 用 户 的 现 在 所 在 行 相 关 联 的


OpenOrPendingServiceRequests 行 的 集 合 , 默 认 的 , 存 取 器 的 名 字 是 OpenOrPendingServiceRequests,目标视图对象匹配这个名字,Service requests 是一个用户已 经赋值了的 requests 的集合,就象你在图 5-25 中看到的一样,你可以���过改变 requests 的名字 来是 Service requests 更加清晰,创建视图链,点击 Finish。

5.10.3当你创建一个带有层次划分的视图链是,会发生什么? 当你创建一个视图链时,Jdeveloper 创建一个 XML 组件定义文件,这个 xml 文件将会声明所有 的设置并被保存在相应名字的包中,在段落 5.10.2 中,那个视图链在 devguide 被命名为 RequestsAssignedTo , examples 包 中 , 因 此 , 这 个 xml 文 件 将 被 创 建 在 工 程 根 目 录 的./devguide/examples/RequestsAssignedTo .xml,这个 xml 文件包括了关于成对的源和目标属性的声明信息,另外还会保存视图链组件的自身 定义,当视图链存取器中你已经定义一栏增加内容时,Jdeveloper 也会相应的修改 xml 文件的内容。 如果你想验证一下,你可以在应用程序中选择用户视图,就想图 5-26 中一样,你可以在视图链存 取器目录中看到一个新的 AssignedRequests 存取器。 Figure 5–26 边上树状图的样子

5.10.4 关于视图链,你需要知道什么 为了更加有效的与视图链合作,有些东西你需要了解,包括:视图链补充属性返回的行集合, 如何通过视图链补充来存取一个集合的内容,如何实现同步。

-------------------------------------------------176----------------------------------------------------------------

5.10.4.1 视图链存取器返回一个rowset


在运行 getAttribute()方法是,你可以通过名字读取视图对象结果集中某行的一些属性, 视图链的存取行为,就像一个源视图对象的附件属性,因此你可以使用同样的 getAttribute ()方法来重新找到他的值,唯一不同的是,你通过视图链存取器得到的属性的数据类型与标 准视图的属性的数据类型不同,标准视图属性是一个固定的标量值,而视图链存取其所得到的 属性是一个由 0 或更多相关联内容的行集合,你可以通过书写下列代码来发送请求,并找到行 集合, RowSet reqs = (RowSet)curUser.getAttribute("AssignedRequests");

注意:如果你在你的视图行中产生了一个定制的 java 类 ,视图链存取器的雷丁就是 RowIterator,运行时,返 回值一般是一个 rowset,它非常安全的将视图链的 属性值映射到 rowset 中,

5.10.4.2 如何用驶入链存取器存取一个集合 如果你已经通过视图链存取器取回了一个 rowset,你就可以通过循环将他包含的内容, while (reqs.hasNext()) { Row curReq = reqs.next(); System.out.println("--> (" + curReq.getAttribute("SvrId") + ") " + curReq.getAttribute("ProblemDescription")); }

如果你在已经存在的 TestClient.java 这个类使用 Jdeveloper 的 Refactor > Duplicate... functionality,你可以非常轻松的克隆一个 TestClient2.java 类,你可以按照例子 5-15 去修改, 尝试这个新的技术,注意在 main()方法行的左侧,设置了一个动态的 where 语句来限制 UserList 值显示叫做 USER_ROLE 的用户,第二个变化是增加了 executeAndShowResults()方法的视图链存取 器属性,并将请求数和 ProblemDescription 属性打印出来,

-------------------------------------------------177----------------------------------------------------------------

例 5-15 编码实现使用视图链存取器存取集合,


package devguide.examples.client; import oracle.jbo.ApplicationModule; import oracle.jbo.Row; import oracle.jbo.RowSet; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; public class TestClient2 { public static void main(String[] args) { String amDef = "devguide.examples.UserService"; String config = "UserServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef, config); ViewObject vo = am.findViewObject("UserList"); // Add an extra where clause with a new named bind variable vo.setWhereClause("user_role = :TheUserRole"); vo.defineNamedWhereClauseParam("TheUserRole", null, null); vo.setNamedWhereClauseParam("TheUserRole", "technician"); // Show results when :TheUserRole = 'technician' executeAndShowResults(vo); Configuration.releaseRootApplicationModule(am, true); } private static void executeAndShowResults(ViewObject vo) { System.out.println("---"); vo.executeQuery(); while (vo.hasNext()) { Row curUser = vo.next(); // Access the row set of details using the view link accessor attribute RowSet reqs = (RowSet)curUser.getAttribute("AssignedRequests"); long numReqs = reqs.getEstimatedRowCount(); System.out.println(curUser.getAttribute("UserId") + " " + curUser.getAttribute("Email")+" ["+ numReqs+" requests]"); while (reqs.hasNext()) { Row curReq = reqs.next(); System.out.println("--> (" + curReq.getAttribute("SvrId") + ") " + curReq.getAttribute("ProblemDescription")); } } } }

运行 TestClient2 显示了结果, 结果被列出来,每个结果都有一些已经开启的或没开启的service requests,这些信息被列在他们 的名字之下 ---


303 ahunold [0 requests] 304 bernst [2 requests] --> (102) Washing Machine does not turn on --> (108) Freezer full of frost 305 daustin [1 requests] --> (104) Spin cycle not draining 307 dlorentz [0 requests] 306 vpatabal [2 requests] --> (107) Fridge is leaking --> (112) My Dryer does not seem to be getting hot 如果你在debug模式下运行TestClient2你会看到,在视图链的where子句被service request自 动完成了过滤,

5.10.4.3 如何在数据模型中进行有效的动态控制或细节调整 你已经看到了定义一个视图链并引入一个视图链属性到源视图对象的过程,在这个环节,视图链 扮演了一个被动的角色,简单的定义必要的信息,当你的代码请求他时,他将以此来得到同等的行 集合,视图链存取器的属性是实时的通过编程从视图链实例中得到的一些结果行,换言之,编程途 径不能请求修改UserService应用模块的数据模型,然而,master/detail用户接口在企业应用中十 分频繁,视图链可以通过频繁变换方式来避免调整master/detail层的编程,你想选择这种活动的 master/detail调整,需要添加一个视图链实例到你的应用模块数据模型中去,完成这个,编辑 UserService应用模块并开启数据模型页面,就像图5-27中一样,你将看到一个可用的视图对象集 合显示了OpenOrPendingServiceRequests视图对象两次:一次是它自己,一次是一个通过 RequestsAssignedTo视图链连接的细节视图对象 Figure 5–27增加一个详细实例到视图对象中去

1.在右边数据模型列表中,选择你想动态调整的数据模型的用户视图对象的实例, 2.在可用的视图对象列表中,选择在用户视图对象中参差排列的 OpenOrPendingServiceRequests 节点 3. 为一 个 详细 实 例 输 入一 个 名 字 ,以 此 在 可 用的 视 图 对 象列 表 中 创 建一 个 名 字 区域 。 叫 做 RequestsAssigned 实例。 4.点击添加实例按钮为数据模型中的现在选择的主实例添加一个详细实例,并命名为你所已经选择 的名字。

-------------------------------------------------179-------------------------------------------------------------------

按照下列步骤,您的数据模型列表看起来就像图5-28 。


图5-28userservice数据模型与视图链视图对象

看到现行的主/详细信息最简单的方式,就是通过开启UserService的Business Components Browser来选择Test从它的上下文菜单中选择Application Navigator。图 5-29 显示了您将会看到 的浏览器窗口。数据模型树显示了视图链,显示的是活动且对等的userlist 视图对象实例与 requestsassigned检视对象实例。它的默认视图链,实例名称requestsassignedto1 。双击这个视 图链,树中的实例节点打开主/详细面板,面板就像你看到的图 5-29 。您会看到,当您使用工具栏 按钮来浏览,在主视图对象-改变视图对象的当前行作为一个结果-明细是自动刷新与用户界面保持 同步的。 图 5-29 活动的数据模型主/明细协调试验

如果您双击该allopenorpendingservicerequests 视图对象,这个实例是你之前添加的,第二 标签将被开启,以显示其数据。注意,这是另一个与devguide.examples.users视图对象相同 的实例;不过,因为它不是正在积极协调,以期连接,其查询是没有userlist当前行约束的 。

---------------------------------------------------------180------------------------------------------------------------------

到目前为止,你见过的视图链它定义了一个基本的主/明细的关系,都是关于两个视图对象的。请


记住,创造更多的视图链,您可以实现任何复杂的主/明细层次,其中包括: ■ 多层次的主/详细/详细资料 ■ 一主多详细 ■ 多主一详细 这些更复杂的层次一样涵盖在这里,您只需要偶尔创建一个视图链。

5.11

产生自定义Java类为视图对象Skip Headers 跳过标题

正如您所看到的,所有视图对象的基本查询功能,才能实现,而不是使用自定义 Java 代码。客户 端可以通过数据的任何 SQL 语句检索和迭代查询,而在视图对象开发过程中不需要写任何自定义代 码。简而言之,对于许多只读查看对象,一旦您定义的 SQL 语句,你就大功告成了。不过,它的重 要的是要了解,当您需要时,如何让自定义的 Java 根据您的需求产生一个 View 对象时。附录 D “ , 最常用的 ADF 的业务组件方法”提供了一个快速参考,提供了您通常会写,使用的常用代码,并重 写您的自定义视图对象和视图行类中。之后的章节,将会讨论具体的例子,在 srdemo 应用程序中, 如何使用自定义代码很好的加入到这些类中去。

5.11.1

如何生成自定义类

使用 Java 页的视图对象编辑器可以生成定制 Java 类为视图对象。就像显示在图 5-30 上的一样, 有 3 个选项的 Java 类可以与一个视图对象。清单中的前两个最常用: 视图对象类 ,它代表组件执行查询 ■ 视图行阶级 ,它代表每一行作为一个查询结果。 ■

图5-30 视图对象自定义的Java产生选项

--------------------------------------------------181--------------------------------------------------------------------

5.11.1.1产生绑定变量存取器


当您启用产生的自定义视图对象类,如果您选择绑定变量存取复选框,然后的 JDeveloper 在您的视图对象类产生 getter 和 setter 方法。由于用户浏览的对象有三个命名绑定变量 ( thename , lowuserid , highuserid ) ,自定义 usersimpl.java 视图对象类将有相应的方 法: public Number getLowUserId() {...} public void setLowUserId(Number value) {...} public Number getHighUserId(){...} public void setHighUserId(Number value) {...} public String getTheName() {...} public void setTheName(String value){...}

这些方法可让您设定一个绑定变量与编译时的类型检查,以确保您设置的值为适当的类型。也就是 说,将设置wUserId的值转变成写一排代码 : vo.setNamedWhereClauseParam("LowUserId",new Number(150));

你也可以这样写程序: vo.setLowUserId(new Number(150));

你可以看到,与后者的做法, Java 编译器将遇到了个错误, 原因是您不小心输入了sEtlowusername而不是setlowuserid : // spelling name wrong gives compile error vo.setLowUserName(new Number(150));

或者,如果您输入了一项不正确的数据类型,如“abc” ,而不是值 // passing String where number expected gives compile error vo.setLowUserId("ABC");

没有产生的绑定变量存取器,以下不能被编译: // Both variable name and value wrong, but compiler cannot catch it vo.setNamedWhereClauseParam("LowUserName","ABC");

这既包含一个不正确的拼写绑定变量的名称,以及作为一个绑定变量的值,错误的数据。如果您使 用 viewobject 接口的通用的 API,这种类型的错误会提高的 exceptions 在在编译运行时不能被捕 获。

---------------------------------------------------------182------------------------------------------------------------------

5.11.1.2 产生视图行属性访问器


当您启用自定义视图类时,如果您还选择存取复选框,然后的 JDeveloper 为每一个属性的视图行 产生 getter 和 setter 方法,为了用户浏览视图对象,相应的自定义 usersrowimpl.java 层将有方 法:

public Number getUserId() {...} public void setUserId(Number value) {...} public String getEmail() {...} public void setEmail(String value) {...} public String getFirstName() {...} public void setFirstName(String value) {...} public String getLastName() {...} public void setLastName(String value) {...} public String getUserRole() {...} public void setUserRole(String value) {...}

这些方法可让您的工作与行数据在编译时的检查正确的数据的使用。这便是通过一行代码,得到价 值的 UserId 属性值: Number userId = (Number)row.getAttribute("UserId");

你也可以撰写的程式码,例如: Number userId = row.getUserId();

你可以看到,与后者的做法, Java编译器将遇到了一个编译错误,因为您不小心输入

useridentifier而不是UserId : // spelling name wrong gives compile error Number userId = row.getUserIdentifier();

未经视图所产生的存取器存取方法,与不正确的代码行一样,以下不能被编译: // Both attribute name and type cast are wrong, but compiler cannot catch it String userId = (String)row.getAttribute("UserIdentifier");

这既包含一个不正确的拼写的属性名称,以及一个不正确的类型映射的getattribute ( )的返 回值。 使用行接口通用的API,这类错误将提高exceptions在在编译运行时不能被捕获。 5.11.1.3 导出视图行存取器到客户端 当启用生成自定义视图类,如果您选择产生的视图行属性的存取器,您也可以选择性地选择导 出存取器到客户端的复选框。这将导致额外的自定义行接口生成,其中应用程序客户端可以用来访 问定制方法对连续的依靠直接导向到实现类。正如你在第4章中学到的那样, “重写ADF的业务组 件”后,客户端代码与业务服务层接口,具体的类不是最佳做法,确保客户端代码不改变时,您的 伺服器端实现。 ----------------------------------------------------183----------------------------------------------------------------在该示例的 Users 视图,导出存取器到客户端将生成一个自定义行接口命名为 usersrow 。此接 口是建立在共同的视图对象包 Subpackage 里面的。行接口允许客户在一个强类型的方式撰写程


式码存取属性的查询结果。 例如 5-16,显示了 testclient3 客户端程序产生的结果, next() 方法到 usersrow 接口,以便它能够调用 getuserid ( )和 getemail ( ) 。 例 5-16 通过存取器使用客户端行接口的简单例子 package devguide.examples.client; import devguide.examples.common.UsersRow; import oracle.jbo.ApplicationModule; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; import oracle.jbo.domain.Number; public class TestClient3 { public static void main(String[] args) { String amDef = "devguide.examples.UserService"; String config = "UserServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef, config); ViewObject vo = am.findViewObject("UserList"); vo.executeQuery(); while (vo.hasNext()) { // Cast next() to a strongly-typed UsersRow interface UsersRow curUser = (UsersRow)vo.next(); Number userId = curUser.getUserId(); String email = curUser.getEmail(); System.out.println(userId+ " " + email); } Configuration.releaseRootApplicationModule(am, true); } }

5.11.1.4配置产生Java默认参数 你已经看到,当你需要自定义运行行为时,如何为您的视图对象生成自定义Java类,或者如果 您只是喜欢有强烈输入获得绑定变量或视图行属性。 配置默���设置ADF的业务组件产生自定义Java 的,选择Tools | Preferences打开Business Components,设定您的偏好设定将用于业务组件创建。 甲骨文建议开发商没有自定义Java类默认情况下,根据他们的喜好来设置产生ADF的业务组件。正 如你遇到的具体需要,您可以启用,只是有点自定义的Java。随着时间的推移,您会发现哪一种设 置最适合您。 5.11.2 产生自定义类时,会发生什么 当您选择去创建一个或多个的自定义 Java 类, JDeveloper 创建的你表示的 Java 文件。将视 图对象命名为 devguide.examples.users ,为自定义的 Java 文件中,视图对象类的默认名称为 usersimpl.java,视图行类的默认名称为 usersrowimpl.java。这两个文件创造在相同的目录下 / devguide /,他们作为 XML 组件定义文件的组成部分。 -----------------------------------------------------184------------------------------------------------------------------


Java 的产生选项的视图对象继续反映对 Java 的网页并访问视图对象编辑器。正如 XML 的定义文 件一样,在您在编辑器里所做的任何变更之上,Jdeveloper 不断的您的自定义 Java 类中生成代码。 如果后来您决定并不需要一个自定义的 Java 文件,取消有关的选项,自定义 java 文件将被清除掉。 5.11.2.1 看到并导航的自定义java文件 跟所有的 ADF 的组成部分相同,当您在应用导航中选择了一个视同对象,结构窗口将显示所有 的执行文件。唯一所需的文件是 XML 的组件定义文件。当转换的 UI 控制提示定义了一个组件,它 将有一个组件消息包文件。显示在图 5-31 ,当您启用生成定制 Java 类,他们为视图对象出现在源 文件夹中。当您需要为自定义的 Java 文件看到或操作源代码,在源编辑器有两种方法可以打开该 文件:

象图 5-31 中显示的那样,选择有关的 Go to 选项 双击结构窗口中的某个源文件夹中的某个文件。 图 5-31,看到并导航视图对象的自定义 Java 类

5.11.3 您需要了解自定义类的什么东西 见以下各节的其他信息,以帮助您使用自定义Java类。 5.11.3.1 视图对象框架内的基础类 当您使用“XML-only”视图对象时,在运行时,其功能是提供默认的 ADF 的业务组件执行类。 每个自定义的 Java 类产生,将自动继承适当的 ADF 的业务组件的基类,以便于您的代码默认继承,


并可以轻松添加或自定义它。1 视图对象类将继承 viewobjectimpl ,视图行类将继承 viewrowimpl (两个都在 oracle.jbo. server 包中) 。5.11.3.2 您可以放心地以自定义组件文件添加代码 也许是基于以往的经验教训,一些开发商犹豫要添加自己的代码生成的 Java 源文件。每个 自定义 Java 源代码文件,JDeveloper 为您创建并保持包括以下注释在文件的顶部,以澄清它是 安全的添加自己的自定义代码到本文件:当您在组件编辑器中按一下 OK 或 Apply 按 钮,.JDeveloper 并不盲目再生文件。相反,它执行的智能更新的方法,以使自己的自定义代码 不变。 5.11.3.3 属性指标和 invokeaccessor 生成的代码 正如您所看到的那样, 视图对象被设计成无论是在一个 XML-only 模式或结合使用的 XML 组件 定义和自定义的 Java 类都可以使用。由于属性值是不会储存在 private 区域的或是一个视图行类 中的,储存在 XML 中是唯一办法。相反,在另外一个名称,属性也指派一个数值索引中的视图对象 的 XML 组件定义,在 zero-based 中,顺序 ViewAttribute 和关联 ViewLinkAccessor 标签在该文件 中。在运行时,属性值在一视图行被存储在一个结构中,这种结构是由基础 viewrowimpl 类管理的, 索引属性值在视图对象属性列表中的位置。 对于大部分,这项私人的执行细节是不重要的。然而,当您启用一个自定义的 Java 类为您的 视图行,便会执行详细的相关的一些生成的代码,JDeveloper 自动保持你的视图行类,您可能会想 要了解什么样的代码被使用了。举例来说,在自定义的 Java 类,为 Users 视图行, 例 5-17 表明, 每一个属性或视图链存取属性有一个相应的产生的整数常数。确保在 XML 的组件定义的这些常量的 值能正确反映属性。 例 5-17 属性常数自动保持在自定义的视图行的 Java 类 public class UsersRowImpl extends ViewRowImpl implements UsersRow { public static final int USERID = 0; public static final int EMAIL = 1; public static final int FIRSTNAME = 2; public static final int LASTNAME = 3; public static final int USERROLE = 4; public static final int ASSIGNEDREQUESTS = 5; // etc. 您还可以看到,自动保持,强类型getter和setter方法,在视图行类使用这些属性常数是这样的: // In devguide.examples.UsersRowImpl class public String getEmail() { return (String) getAttributeInternal(EMAIL); // <-- Attribute constant } public void setEmail(String value) { setAttributeInternal(EMAIL, value);// <-- Attribute constant } -----------------------------------------------------186------------------------------------------------------------------


在过去的两个方面,自动保持相关的代码,以查看列属性常数是 getattrinvokeaccessor ( ) 和 setattrinvokeaccessor ( )方法。这些方法的优化性能的属性进入指数的数值,在 viewrowimpl 基类访问属性值,这是非常通用的代码,。该 getattrinvokeaccessor ( )方法看起来像从 servicerequestimpl.java 类中得出的。配套 setattrinvokeaccessor ( )方法看起来类似。 // In devguide.examples.UsersRowImpl class protected Object getAttrInvokeAccessor(int index,AttributeDefImpl attrDef) throws Exception { switch (index) { case USERID: return getUserId(); case EMAIL: return getEmail(); case FIRSTNAME: return getFirstName(); case LASTNAME: return getLastName(); case USERROLE: return getUserRole(); case ASSIGNEDREQUESTS: return getAssignedRequests(); default: return super.getAttrInvokeAccessor(index, attrDef); } } 请记住产生属性索引相关的代码应遵循一下原则。 该做的 添加自定义代码,如果需要内强类型属性 getter 和 setter 方法 使用视图对象编辑器来更改视图对象属性的顺序或类型。 JDeveloper 将改变 Java 的 getter 和 setter 方法,以及相关的 XML 的组件定义。 该注意的事项 不要修改 getattrinvokeaccessor ( )和 setattrinvokeaccessor ( )方法 不手动改变属性索引的值

注意:如果您需要手动编辑所产生的属性常数,也许是因为源控制合并冲突,你必须 确保 zero-based 映射了连续的 viewattribute 和 viewlinkaccessor 标记在相应的 View 对象的 XML 组件定义中。

-----------------------------------------------------187------------------------------------------------------------------


-----------------------------------------------------189------------------------------------------------------------------


本章节描述在你的 J2EE 项目怎样用实体对象创建业务层。 本章包括下列部分: ■ Section 6.1, "Introduction to Entity Objects" ■ Section 6.2, "Creating Entity Objects and Associations" ■ Section 6.3, "Creating and Configuring Associations" ■ Section 6.4, "Creating an Entity Diagram for Your Business Layer" ■ Section 6.5, "Defining Attribute Control Hints" ■ Section 6.6, "Configuring Declarative Runtime Behavior" ■ Section 6.7, "Using Declarative Validation Rules" ■ Section 6.8, "Working Programmatically with Entity Objects and Associations" ■ Section 6.9, "Generating Custom Java Classes for an Entity Object" ■ Section 6.10, "Adding Transient and Calculated Attributes to an Entity Object"

6.1 介绍实体对象

一个 ADF 的业务组件的实体对象,可以表现一个数据库表,并简化修改其数据。 重要的是, 它可以让您概括域的行的业务逻辑,以确保您的业务策略和规则是始终如一的。到这一章最后,你 就会明白图 6-1 中的概念 : • • • • •

您定义的一个实体对象指定数据库表找到描述的行。 您可以联系的一个实体对象,来反映基本数据库表之间的关系。 在运行时,实体行被相关实体的定义对象管理。 每个实体行是确定一个相关的连续的 key。 您检索和修改实体行语境中的一个应用模块,提供数据库的转换。

-----------------------------------------------------189------------------------------------------------------------------


Figure 6–1 Entity Object Encapsulates Business Logic for a Table

当您的应用模块创建,修改,或删除的实体对象,并提交转换,改变会自动保存。当您需要与 创造了 servicerequest 和 User 一起工作,或 servicehistory 实体,然后连个实体,简化任务。 实体对象支持众多的声明性的业务逻辑功能,您的数据执行的有效性, 正如您会看到更详细的在 之后的章节里,你通常会配合的声明性验证与额外的自定义应用程序逻辑和业务规则,以封装的最 高数量的域业务逻辑到每一个实体对象。您的相关设置的实体的对象,形成了一个可重复使用的业 务域层,您可以用于多种应用。 注意: 一个 working version 的例子,在这一章中,下载 devguideexamples workspace 从例如下 载页面在 http://otn.oracle.com/documentation/jdev/b25947_01/看 businesslayerwithentityobjects 工程。

6.2 建立实体对象和联合 最简单的方法,创建实体对象和联合是从现有的表中使用反向引擎。因为往往你会已经有一个 数据库模式,您将使用在实践中的最简单的方法,也是最常见的做法。有需要时,倒不如创建一个 实体对象从头开始,然后生成一个表。 6.2.1 如何创建实体对象和连接从现有的表 创建一个实体对象,使用 Business Components from Tables wizard 。该向导可从 New Gallery 在 Business Tier > ADF Business Components。 如果在您创造该项目的第一部分, Initialize Business Components Project 对话框出现,让您可以选择一个数据库连接之前,wizard 就会出现。 这些例子,假设您工作与一连接命名 srdemo 为 srdemo 架构。 在第 1 步中对 Entity Objects 页面 上,您选择了一个清单表,从 Available 的名单为您想要建立的实体对象。字段在顶部可让显示包 中所有的实体对象。如果选中 Auto-Query 复选框,然后名单表可立即出现,否则,你需要按一下 Query 按钮,检索名单后,提供一个名称筛选。 一旦从现有的名单您选择了一个表,该表的实体 对象的名称将出现在与相关资料表的选定的名单名称括号中。在选定的名单点击一个实体对象名 称,您可以使用实体名称下方的栏位更改默认的实体对象的名称。由于每个实体的对象实例代表一 个某一特定表中的单列,最好是给实体对象命名为单数如 User, Product 和 servicehistory , 代替他们的复数配对对象。图 6-2 显示了,选择所有五个表,在 srdemo 架构,设置了一系列的名称 devguide.model.


entities ,和重新命名的每一个实体对象为一个单数名字。

注意:由于一个实体对象代表一个数据库连续,自然称之为一个实体列。另外,由于在运行时的 实体行的一个实例是一个 Java 对象封装业务逻辑的数据库列,更面向对象的实体,例如此亦是适 当的。因此,这两个名词是可以互换的。

图 6-2 创造的实体对象为现有的表

单击完成以创建实体对象。进度对话框出现,而这些组件创造了,然后你可以看到由此产生的 组件在应用 Navigator 中显示,在图 6-3 。您可以尝试 Flat Level 控制和 Sort by Type 按钮, 看看他们在显示器上效果。

---------------------------------------------------------------191----------------------------------------------------------------------------


图 6-3 新的实体对象在应用导航使用 Flat Level 1 和排序类型

6.2.2 当您创建实体对象和协会从现有的表会发生什么事 当您从现有的资料表创建一个实体对象,首先 JDeveloper interrogates 数据字典来推断下列资料: • • • • • •

Java 的友好实体的属性名称,从名称表的列(如 user_id -> “用户名) 在 SQL 和 Java 的数据类型的每个属性的基础上的优先列 长度和精度的每个属性 主要的和独特的关键属性 强制性标志的属性的建立在 NOT NULL 的限制 新的实体对象和其他实体的外键约束基础上,之间的关系

JDeveloper 然后创建的 XML 组件定义文件,代表其声明的设置,并将它存储在目录中对应的 名称。其中的实体创造了上述被命名为 User 在该 devguide.model.entities 包,所以 XML 文件将 创建在 / devguide /模型/实体/ user.xml 下,该项目的源路径。此 XML 文件包含表的名称,姓名 和数据类型,每个实体的属性,以及列的名称,每个属性。如果您感到好奇,看看它的内容,您可 以看到 XML 文件为实体对象的选择,它在 Application Navigator 和在相应的源文件夹在结构窗口 中 。双击 user.xml 节点将在编辑器打开 XML,使您可以检查它。

注意:如果您的 IDE 级别的业务组件的 Java 产生了需要的设定,以便表明,该向导还可以创建一 个可选的自定义实体对象类

---------------------------------------------------------------192----------------------------------------------------------------------------


此外,该实体对象的姓名由您决定,向导还生成名为关联组件,捕捉关系实体对象之间的信息。 快速浏览在数据库图在图 6-4,默认关联名称,如 svrassignedtousrfkassoc 和 svrcreatedbyusrfkassoc 导出转换外键约束的名称,以 Java-friendly 名称并加入的 Assoc 后缀。 为每个关联的创建,JDeveloper 创建一个适当的 XML 组件定义文件,并将它存储在他的包的目录中 对应的名称。默认情况下,关联的的 reverse-engineered 从外键获得创造了在相同的包,作为实 体,举例来说,一,该关联的 XML 文件将被命名为。 / devguide /模型/实体/ svrassignedtousrfkassoc.xml 。 图 6-4 USERS 和 service_requests 表是由相关的外键

6.2.2.1 表没有主键时会发生什么事 如果一个表没有主键约束,那么的JDeveloper不能推断,主键为实体对象。由于每一个实体对象 必须至少有一个属性标记为一个主键,该向导将创建一个属性命名为rowid和使用数据库rowid值 作为首要的关键的实体。 如果适当,您可以编辑实体对象稍后标记不同的属性,作为一个主键并 删除rowid属性。 如果您使用Create Entity Object wizard ,系统会提示您使用rowid作为首要的 关键,如果您没有设定任何其他属性作为主键。

---------------------------------------------------------------193----------------------------------------------------------------------------


6.2.3 建立实体对象使用创造实体向导 创造一个单一的实体对象,您可以使用创建实体对象向导 。该向导可从New Gallery in the Business Tier > ADF Business Components category。选择的名称,现有的资料表在第1步,对Name 的页面,JDeveloper将所有的相同的信息,判断为新的实体,它使用Business Components from Tables wizard 这样做。如果你输入一个名称,一个不存在的表,您将需要界定每个属性一个又一个关于 属性页的向导。以后创建表您可以手动,或产生,描述在第6.2.6 , “创建数据库表从实体 对象” 。 6.2.4 通过一个别名或一个视图创造一个实体对象 当创建一个实体对象使用 Business Components 从 Tables wizard 或 Create Entity Object wizard,一个实体对象可以代表一个基本表,同义字,或���图。您看到在第 6.2.2.1 , “事表没 有主键时会发生什么” ,该框架可以通过检查数据库推断出主键和相关关联,在数据字典主键和 外键约束。但是,如果架构对象您选择的是一个资料库视图,然后既不是主键,也不关联任何推断, 则数据库的限制。在这种情况下,如果您使用 Business Components from Tables wizard,主键 默认为 rowid 。如果您使用 Create Entity Object wizard ,您需要指定主键,手动标记,至少 有一个它的属性作为一个主键。 如果您选择的架构对象是一个代名词,那么有两种可能的结果。如果同义词是一个表别名,如 果您已指定了一个表,然后向导和编辑器会执行。 如果用数据库视图来替代锁定提交,那么,他 们就会表现为如果你曾指明的视图。 6.2.5 编辑现有的实体对象或关联 在您建立一个新的实体对象,您可以编辑任何其设置使用实体对象编辑器。选择编辑菜单上的 选项,在上下文菜单中的应用 Navigator 中,或双击该实体对象,启动编辑器。通过打开不同的面 板的编辑器,您可以调整设定,定义实体和管治其运行时的行为。.稍后章节这一章涵盖许多这些 设置。 6.2.6 从实体对象建立数据库表 创建数据库表是建立在实体对象的基础上,选择的方案在 Application Navigator 中包含的实 体对象,并选择 Create Database Objects...。 出现一个对话框让你选择您想要建立实体的表。 这个工具可以为一个实体对象用来生成表,你重新建立一个现有的表。

注意事项:脚本运行后,此功能不产生 ddl,它的行动直接对数据库,并会删除现有的表。出现一 个对话框,以确认您要这么做,再进行操作。为实体在现有的表格的基础上,审慎使用。

---------------------------------------------------------------194----------------------------------------------------------------------------


6.2.6.1 使用数据库约束集 在约束集编辑器中, 数据库外键限制是否产生在相应的实体上。 当为实体创造表的时候,被产生。选择这个选项没有任何的 runtime 含意。 6.2.7 用对它的数据库的变化同步化一个实体 不可避免你 (或你的 DBA) 可能改变一张表 因为这个实体。 你的现有实体将不会扰乱另外 的实体表中的属性; 然而, 如果你想要存取 J2ee工程 表 中新的列你将会需要同步化数据 库中相应的实例。 为了要自动地运行这个同步, 选择实体物体并且选择数据库同步化例如, 你在 SQL*Plus 中 欠佳一个新的列SECURITY_QUESTION 到USERS表中 ALTER TABLE USERS ADD (security_question VARCHAR2(60)); 在你在实体上使用同步化特征之后, 那数据库同步会话会出现如图 6-5 所示 Figure 6–5 Synchronizing an Entity Object with Its Underlying Table

6.2.8 你可能需要知道如何创造实体 商业表容器生成向导 会更快更简单的生成很多商业表 在练习中, 不是指你应给立刻为每 张表创建实体,而是可以这么做 如果你的应用程序使用了许多表,当时很适当,但是你能使用向 导每当在你需要的时候,Oracle 推荐你创建实体的时候 要考虑那些将会被调用 第 8.9 节, “决 定应用程序模型的粒度”,为你的商务服务在使用上粗略的说明一些想法, 使用case-driven设计 你的商业服务能,帮助你理解实体方面所必须的商务逻辑。 你总是能在最后增加一些必要的实体。

-------------------------------------------------195-------------------------------------------------------------------


6.3 建立和配置关系 如果您的数据库表有没有外键的定义,JDeveloper将无法自动推断创建他们之间的实体对象。 现在看几个有趣的运行时功能,您将会学习创建实体间的依赖关系,甲骨文建议您手动创建。 6.3.1 如何建立一个关系 建立一个关系,使用创建新的关系向导 。 假设该关系之间的servicerequest和servicehistory实体已经不存在,您可以手动创建,也可 以按照下列步骤进行: 建立一个关系: 1. 打开Create New Association wizard从New Gallery in the Business Tier > ADF Business Components类别。 2. 在第1步的Name标签页,提供一个名称为关系的组成部分和一篮子遏制。 3. 在第2步,对实体对象的标签页,从一个实体对象选择一个“源”属性,将关系设定为如图6-6 显示,从来源实体servicerequest实体对象中选择svrid属性。 Figure 6–6 Manually Defining the Attribute Pairs That Relate Two Entity Objects

4. 下一步,从其他实体对象所涉及的关系选择相应的目的属性。表servicehistory的行包含涉及到

一个具体的servicerequest行服务请求的ID,在servicehistory实体对象中选择此svrid外键属 性。

-------------------------------------------------196-------------------------------------------------------------------


5. 接下来,单击添加以匹配添加的属性对,在表的来源及目的地的属性的下面。如果有多个属性, 对所需的界定关系,你可以重复这些步骤以添加额外的源/目标属性对。在此示例中,必须提供 一个( svrid , svrid )键值对。 6. 最 后 , 确 保 基 数 的 下 拉 式 正 确 地 反 映 了 该 基 数 的 关 系 。 自 servicerequest 及 其 相 关 servicehistory 行关系的之间是一对多,您可以保留默认设置。 7. 在第 3 步,对关系的 SQL 标签页上,您可以预览该关系的 SQL 上游将用于在运行时获取相关的 servicehistory 实体对象,例如某一特定的 servicerequest 实体对象。 8. 第 4 步,在 Association Properties 页,您确定该关系是否代表一个单向的关系,或设置其他 的属性界定运行时的行为关系。如图 6-7 ,该 Expose Accessor 在双方的来源及目的地复选框 中选择。默认情况下,关系是一个双向的定向关系,让对方有需要时访问相关的实体列。在这 个例子中,这意味着如果您正在使用的一个实例一 servicerequest 实体对象,您可以很容易地 获取收集及其相关 servicehistory 行。您也可以轻易地访问 servicerequest 到它所属的,与 任何例如一个 servicehistory 实体对象。双向导航是更方便的书面业务逻辑的验证,因此在实 践中,你通常会离开这些默认的复选框,设置。 Figure 6–7关系性质的控制运行时行为

-------------------------------------------------197-------------------------------------------------------------------


6.3.1.1 改变实体关系存取名称 您应该考虑的默认设置存取的名称,该关系的属性页,并决定是否改变名称,以更直观的做法 是恰当的。这些界定的名称存取属性,您将使用在运行时以编程方式访问实体对其他方面的关系。 默认情况下,存取的名称,该实体对象在另一边。用于存取的名称,一个实体必须是独一无二的, 其中实体对象属性和其他存取,如果一个实体是相关的另一个实体以多种方式则预设的存取名称修 饰一个数字后缀,使专属的名称。举例来说, servicerequest实体是相关的,一旦向用户实体所 代表的用户谁创造了请求,并在第二个时间,以反映技术员向谁的服务请求指派。默认存取的名称 就是servicerequest实体将用户和用户1的 。开放各自的Association Properties页为关系的问题, 您可以重新命名这些存取,和名称一样更直观, createdbyuser和technicianassigned 。 6.3.1.2 重命名和移动关系,用于不同的包 由于名称的预设关系是不容易了解,首要任务之一,您可能想要执行后,创造的实体对象从表 的时候,重新命名他们则更有意义。此外,由于关系的一个组成部分,你通常配置在开始您的项目 和不经常访问的,此后,您可能会想要将它们移到一个不同的方案,使您的实体对象比较容易看到。 双方重新命名组件和将它们移至不同的方案,则是直接使用的JDeveloper的重构功能。提出了一套 业务组件,以不同的包装,选择一个或多个组件在Application Navigator和选择Refactor > Move.. 从上下文菜单中。要重命名的一个组成部分,从上下文菜单中选择refactor > Rename。当你refactor ADF的业务组件,自动完成JDeveloper任何XML或java文件相关的组件,以及更新的任何其他组件, 可能会参考他们。 图6-8显示的Application Navigator,改名为所有关系和它们移至 devguide.model.entities.associations包。虽然您可以refactor关系到任何包装,这样可以使他 们在逻辑上相关的实体,允许折叠式的包关系,当您不需要看到他们时。

-------------------------------------------------198-------------------------------------------------------------------


Figure 6–8 Application Navigator,关系的重构

6.3.2 当您创建了一个关系会发生什么事 当您创建一个关系 JDeveloper创建一个适当的XML组件定义文件,并将它存储在目录中对应 的名称。如果您创建了一个关系命名为servicehistoriesforservicerequest ,在 devguide.model.entities.associations方案,然后关系的XML文件将建立在。 / devguide /模型 /实体/关系目录与名称servicehistoriesforservicerequest.xml 。在运行时,该实体对象使用该 关系的自动化实体。 6.3.3 您可能需要知道的组成关系 当您创建的组成关系,它是有用的了解种的关系,您可以代表,以及各种选项。 关系之间的实体对象可以代表两个风格的关系,取决于来源的实体: 参考目的地实体 包含目的地的实体作为一个合乎逻辑的,嵌套的一部分 显示在图6-9 ,在您的srdemo应用业务层,你有一个servicerequest引用的产品 ,要求用户 指定。 这些关系所代表的第一类关系,反映了一个用户或一个产品的实体对象独立存在servicerequest 。 此外,删除一个servicerequest ,并不意味移除产品 ,它只是针对相关的用户servicerequest及 servicehistory之间的关系细节是个简单的参考。该servicehistory实体包括一个逻辑部分 servicerequest 。换言之, servicerequest组成servicehistory实体。一个servicehistory独立 存在的实体是没有意义的,和当一个servicerequest被删除-假设这是允许的-其所有的组成部分, 应予删除。

-------------------------------------------------199-------------------------------------------------------------------


这种类型的逻辑,代表了第二种关系,所谓的组成 。UML的图在图6-9显示了更强的组成关系。

图6-9 servicerequest组成servicehistory实体和参考双方的Product和User

从表向导创建的组成关系预设任何外键,默认就有删除级联选项。使用创建向导编辑器,以表 明一个关系是一个组成关系,检查组成关系的复选框。 一个实体对象提供了附加的运行时存在的 一个组成。您将了解具体情况和设置的控制,它在第6.6.3.12 , “理解和配置组成的行为” 。 6.4 为您的业务层创建一个实体图 由于您的业务层对象对于你的团队是一个重要可重复使用的资源,往往可以方便的可视化编辑 UML模型。JDeveloper很容易为您的业务层创造一个实体图,您和您的同事可以使用以供参考。 6.4.1 如何建立一个实体图 创建一个你的实体对象图,使用创建的业务组件图的对话框。你使用它的New Gallery在 Business Tie> ADF Business Components类别。对话框提示您输入一个图表的名称,和一个该图 所属的包。进入图名称,比如“Business Domain Objects ”的名称,包为, devguide.model.design , 并单击确定以创建空图。 给图新增一个现有的实体对象,选取他们在所有的Application Navigator和拖放他们到图里面。 使用属性检查器隐藏软件包名称,变更字型,关闭网格和分页符,并显示名称,看起来可能好些。 图现在应该像这样:图6-10 :

--------------------------------------------------200--------------------------------------------------------------------


图 6-10 业务层的 UML 图表:

6.4.1.1 发布业务实体图 发布图如 PNG , JPG , SVG,或压缩的 SVG 格式,选择 Publish Diagram....从上下文菜单中。 6.4.2 当您创建了一个实体图会发生什么事 当您创建一个业务组件图, Jdeveloper会在子目录中创建一个XML文件代表该图,该项目标路 径会匹配软件包的名称。创建业务层对象图,如图 6-10 ,它将创造一个*. oxd_bc4j文件。 该模 型的路径在./devguide/model/design子目录中。默认情况下, Application Navigator统一显示 该项目的内容,使ADF的组件和Java文件在源路径出现在该项目模型的路径相同的包中。不过,正 如所显示的图6-11 ,使用Toggle Directories工具栏上的按钮Navigator,你可以看到不同的项目 内容路径根目录。

-------------------------------------------------------201---------------------------------------------------------------


图 6-11

切换显示内容文件夹的路径

6.4.3 您可能需要知道如何建立一个实体图 6.4.3.1 UML图与业务组件是同步的 业务组件的UML图,不只是静态图片,当你拖放对象到图上的时候,它可以反映时间点。它是 一个基于UML的的组件,所以,它会永远地反映当前的状态。更重要的是, UML图是一个可视化的 视觉导航和编辑工具您可以选择实体对象编辑器中的任何一个图中的实体对象,选择属性...从上 下文菜单(或双击) 。您也可以直接对图执行一些实体对象的编辑任务,重新命名实体和实体的 属性,以及添加或删除属性。 6.4.3.2 UML图表增加了额外的元数据以XML的组件描述 当您包括一个业务组件,如一个实体对象 的UML图, Jdeveloper会增加额外的元数据,如例 子6-1 。额外的信息只是适用于设计时。 例:6-1

额外的UML元数据添加到一个实体对象的XML描述

<Entity Name="ServiceRequest" ... > <Data> <Property Name ="COMPLETE_LIBRARY" Value ="FALSE" /> <Property Name ="ID" Value ="ff16fca0-0109-1000-80f2-8d9081ce706f::::EntityObject" /> <Property Name ="IS_ABSTRACT" Value ="FALSE" /> <Property Name ="IS_ACTIVE" Value ="FALSE" /> <Property Name ="IS_LEAF" Value ="FALSE" /> <Property Name ="IS_ROOT" Value ="FALSE" /> <Property Name ="VISIBILITY" Value ="PUBLIC" /> </Data> : </Entity>

----------------------------------------------------202-----------------------------------------------------------------


6.5 属性控制提示 在您的业务层的实体对象,根据属性控制提示可以立即改变它的值,以确保您的数据可以让您 的最终用户满意。JDeveloper管理存放的提示方式,很容易本地化,国际化。本节探讨如何界定标 签文字,工具提示,和实体对象属性的提示格式。正如您看到的在第7章, “建设一个以实体为基 础的可更新的数据模型” ,用户界面会提示您自动继承您的业务层实体对象。 6.5.1 如何添加属性控制提示 为一个实体对象添加属性控制提示,在左侧的侧边面板打开实体对象编辑器和属性节点。 如 图6-12所示,它显示的为servicerequest实体对象。 选择一个属性名如requestdate,选择控制提 示选项卡,您可以设定它: 标签文字提示“Requested On” ■ 工具提示文本提示“The date on which the service request was created” ■ 格式类型 为简单日期类型 ■ 格式为MM/dd/yyyy HH:mm ■

您可以选择其他属性,从而为他们确定特定的控制提示。 注意:与Java定义的日期格式不同,这里从所用的是Oracle数据库的SQL和PL / SQL的格式。

图6-12为实体对象属性标签设置UI控制提示

----------------------------------------------------203-------------------------------------------------------------------


6.5.2 当您添加属性控制提示时会发生什么 当您定义属性控制提示为一个实体对象,Jdeveloper会创建一个标准的Java包文件存储他们。 servicerequest实体,在devguide.model.entities包中,并且在devguide.model .entities.common 子包中创建的文件将被命名为servicerequestimplmsgbundle.java 。 选择 servicerequest的Application Navigator ,您就可以看到新文件添加的结构窗口 ,它显示执行 文件的每个组件。如例子6-11,如何控制提示信息。第一实体定义每个字符串数组是一个消息Key, 第二实体定义特定的字串值是相应的Key。 例子6-3是本地化实体对象的组成信息为意大利语 package devguide.model.entities.common; import oracle.jbo.common.JboResourceBundle; // --------------------------------------------------------------------// --- File generated by Oracle ADF Business Components Design Time. // --------------------------------------------------------------------public class ServiceRequestImplMsgBundle extends JboResourceBundle { static final Object[][] sMessageStrings = { { "AssignedDate_FMT_FORMAT", "MM/dd/yyyy HH:mm" }, { "AssignedDate_FMT_FORMATTER", "oracle.jbo.format.DefaultDateFormatter" }, { "AssignedDate_LABEL", "Assigned On" }, { "AssignedTo_LABEL", "Assigned To" }, { "CreatedBy_LABEL", "Requested By" }, { "ProblemDescription_DISPLAYWIDTH", "60" }, { "ProblemDescription_LABEL", "Problem" }, { "RequestDate_FMT_FORMAT", "MM/dd/yyyy HH:mm" }, { "RequestDate_FMT_FORMATTER", "oracle.jbo.format.DefaultDateFormatter" }, { "RequestDate_LABEL", "Requested On" }, { "RequestDate_TOOLTIP", "The date on which the service request was created" }, { "Status_LABEL", "Status" }, { "SvrId_LABEL", "Request" } }; // etc.

6.5.3 日期格式国际化 使用ADF业务组件的国际化模型层为每个组件提供消息绑定文件。例如,意大利版的 servicerequestimplmsgbundle类命名为servicerequestimplmsgbundle_it,瑞士意大利语的版本 将命名为servicerequestimplmsgbundle_it_ch 。 这些类通常会延长资源类的名称,并必须在局 部范围内包含消息键和其本地化翻译。 例如,意大利版的 servicerequestimplmsgbundle 绑定文件, servicerequestimplmsg bundle_it 和一个瑞士意大利语的版本将有名称 servicerequestimplmsgbundle_it_ch 。这些类通 常会延长资源类的名称,并必须在局部范围内包含消息键和其本地化翻译。 例 6–3 本地化实体对象组件为意大利语


package devguide.model.entities.common; import oracle.jbo.common.JboResourceBundle; public class ServiceRequestImplMsgBundle_it extends ServiceRequestImplMsgBundle { static final Object[][] sMessageStrings = { { "AssignedDate_FMT_FORMAT", "dd/MM/yyyy HH:mm" }, { "AssignedDate_LABEL", "Assegnato il" }, { "AssignedTo_LABEL", "Assegnato a" }, { "CreatedBy_LABEL", "Aperto da" }, { "ProblemDescription_LABEL", "Problema" }, { "RequestDate_FMT_FORMAT", "dd/MM/yyyy HH:mm" }, { "RequestDate_LABEL", "Aperto il" }, { "RequestDate_TOOLTIP", "La data in cui il ticket è stato aperto" }, { "Status_LABEL", "Stato" }, { "SvrId_LABEL", "Ticket" } }; public Object[][] getContents() { return super.getMergedArray(sMessageStrings, super.getContents()); } } 6.6 配置的声明性运行时的行为 实体对象具有可以多次声明的特点,以简化企业业务应用。 根据您的任务,有时多次声明可 满足您的需求。这样可以让您为您的企业层声明更复杂的业务逻辑或验证规则。 这一章的重点是 给你介绍声明的特点。在第 9 章, “对实体对象实现业务规则” ,您将研究的一些最典型的实体 对象自定义方式。 6.6.1 如何配置运行时声明 用实体对象编辑器配置运行时声明的一个实体对象。 您访问编辑选择的一个实体的应用掉航 再从上下文菜单中选择编辑菜单。图 6-13,编辑器像 servicerequest 实体对象一样。 在名称的选项卡,您可以看到实体对象的名称和配置数据库表,并包含它自身的关系。在属性 页中,您创建或删除的属性代表相关的数据实体对象。通过展开这个节点,您可以使用每一个实体 对象的属性。

-----------------------------------------------------205------------------------------------------------------------------


在调节选项卡,您可以设定选项,使数据库操作更有效率,您可以在一个单独的事务中创建, 修改或删除多个实体类型。在发布选项卡上,您的实体对象可以通过使用您定义的事件通知其他实 体对象,可以选择部分或全部的实体对象的事件。在 Subscribe 选项卡上,您选择实体对象的事件 关联到其他实体对象。在授权的选项卡上,您定义基于角色权限的所有属性。 Figure 6–13 Use the Entity Object Editor to Configure Its Declarative Features

如果您的实体已有了很多的属性名称,有个快速的方法来找到一个想要的结果。 属性节点展开的时候,您就可以开始输入英文字母的属性名称,JDeveloper 会自动定 位到相应位置。 6.6.2 当您设定运行时行为的时候会发生什么 所有实体对象的运行时的描述和控制 都 储存在 XML 的组件定义文件中。当您使用编辑器修 改设置您的实体,点击 ok 更新组件的 XML 定义文件和可选的自定义 java 文件。如果您需要立即应 用更改并继续在编辑器中工作,请点击应用按钮。应用更改的同时,编辑通常还需要创建一个自定 义的 Java 文件来编辑,在 JDeveloper 生成这些文件之前请先在编辑器中打开另一个页面。

-----------------------------------------------------206------------------------------------------------------------------


6.6.3 关于声明实体对象的特点 由于大部分的实体对象的声明都和它自己的属性有关,本节涵盖的重要内容如图 6-13 所示 6.6.3.1 合法的数据库和Java数据类型创建一个实体对象的属性 在持久化的属性中设置这个属性是对应一张表的还是临时的一个值,如果属性是持久性, 数据库 列 可让您的名称更改为对应列的属性,并说明其类型与精度和长度(如varchar2 ( 40 )或NUMBER ( 4,2 ) ) 。在此基础信息之上,确定在运行时的实体对象的最大长度和精度/等级的属性值, 如果一个值不符合要求则抛出一个异常。 业务组件会根据它自身的关系自动从表向导和创建实体对象向导推断的 Java 类型。任何类型的 Java 类型的实体属性都可以让您变更属性类型字段。数据库列类型字段反映的 SQL 类型的属性映 射。 数据库列名称字段的值映射其中的属性。 您的实体对象可以处理表的列类型,如表 6-1 。关于 java.lang.string 的异常,默认的 Java 属性类型都是在 oracle.jbo.domain 和 oracle.ord.im 包支持的 Oracle 数据库的数据类型。下拉 列表中为 Java 的类型字段同时也支持包括一些其他常见的类型。

----------------------------------------------------------207------------------------------------------------------------------


注意:这里所说的类型,您可以使用任何 Java 对象类型作为一个实体对象属性的类型, 他必须实现 java.io.serializable 接口。

6.6.3.2 显示数据的长度,精度和范围

工作时支持定义类型的最大长度如 varchar2 ( n ) , 数据库列类型包括最大属性长度作 为参数。因此,举例来说,一个基于 varchar2 ( 10 )的属性列,它将在数据库中存储最大长度 为 10 个字符的字符串。如果由于某种原因,您想要限制的字符串的最大长度属性,只要改变数据 库列类型的最大长度值。 举例来说,用户表中的电子邮件栏在是 varchar2 ( 50 )类型的 ,因 此默认情况下, 在用户实体对象中电子邮件的属性默认相同。 如果您知道实际的电子邮件地址始 终是 8 个字符或更少,您可以更新的数据库列类型为电子邮件的属性必须为 varchar2 ( 8 ),最 大长度为 8 个字符。数据库列类型的属性支持精确度和范围,如 NUMBER(p[ ,s ]) .因此,举例来 说, NUMBER(7,2)列在数据库中限制一个属性为精度 5 和范围 1 ,而是更新的此数据库列键入要符 合 NUMBER(5,1) 。 6.6.3.3 控制一个可更新的属性 当某一特定属性可更新时,设置它的控制参数。 如果设置为: • • •

Always,属性是始终更新 Never ,属性是只读的 While New ,属性可以设置在创建实体的时候,但成功后,它的属性就是只读的了

6.6.3.4 创建一个托管的属性 设置一个托管的属性前要确定该字段是否是必需的。

---------------------------------------------------------------208----------------------------------------------------------------------------


6.6.3.5 为实体定义主键 主键属性标示了一个唯一标识的实体。通常情况下,您将使用一个单一的主键,但多属性的主 键也是完全被支持的。 在运行时,您可以使用 getkey ( )方法 获取任何实体的关系,key 主要包含对象主键的值。 如果您的实体对象有多个主键的属性,主键对象们包含了各自的值。要明白,在实体对象中定义的 相应主键的值出现的相对顺序很重要。 举例来说, servicehistory 实体对象有多个主键属性 svrid 和 lineno 。在属性页的实体对 象编辑器, svrid 是第一,lineno 是第二;数组封装的 servicehistory 将有这两个属性,也正是 这种顺序排列。 认识到这一点是是至关重要的,如果您尝试使用 findbyprimarykey ( )找到一 个实体与多个属性的关系,只要你的对象已建立多重主键的属性,实体的行不会被发现。 6.6.3.6 定义一个静态的默认值 为默认字段的属性指定一个静态的默认值。例如,您可以设定实体对象的 servicerequest 属性 的预设值,或设定的预设值的用户实体的 UserRole 属性。 6.6.3.7 同步与触发指定值 如果您知道在插入或更新操作的时候那个列值将更新,您可以选中刷新后,插入或刷新后更新 的复选框用来自动保持框架中实体对象和数据库的连续同步。实体对象使用甲骨文的 SQL RETURNING INTO 特性,当插入或更新的时候,返回改良的数据到您的应用程序在一个数据库请求 中。

注意:如果您创建了一个实体对象的别名,并且连接的远程表超过一个数据库连接, 使用此功能将在运行时抛出一个错误,例如:JBO-26041: Failed to post data to database during "Update" ## Detail 0 ## ORA-22816: unsupported feature with RETURNING clause jbo - 26041 26.5 节, "根据一个实体对象创建视图或远程数据 库连接"描述了一种技术,来规避这个数据库的限制。

---------------------------------------------------------------209----------------------------------------------------------------------------


6.6.3.8通过一个数据库序列触发分派 一个主键一个通用的例子,在插入后刷新主键的值通过BEFORE INSERT FOR EACH ROW触发 器。触发器常常用PL/SQL分派主键通过数据库序列 CREATE OR REPLACE TRIGGER ASSIGN_SVR_ID BEFORE INSERT ON SERVICE_REQUESTS FOR EACH ROW BEGIN IF :NEW.SVR_ID IS NULL OR :NEW.SVR_ID < 0 THEN SELECT SERVICE_REQUESTS_SEQ.NEXTVAL INTO :NEW.SVR_ID FROM DUAL; END IF; END; 设置名为DBSequence的属性类型如图6-14所示,并且让主键根据数据库序列自动生成。设置当 前数据类型在插入之后触发属性. 当你创建一个实体主键为DBSequence.一个唯一的数字会被分派根据一个临时值。这个主键值是 它在被创建的时候被赋值,如果你在一个事务里创建一批相关的实体,你能通过外键指派这个临时 值在事务提交的时候,实体在插入的时候会插入相应的主键值任何新关系的实体的外键会根据主键 自动更新。

注意:请看如图 6-14, 你设置 Updatable DBSequence 值的属性为 Never. 实体对象会指 派一个临时 ID,最后在插入之后更新相应的值。用户最终不必更新它。

Figure 6–14 Setting Primary Key Attribute to DBSequence Type AutomatesTrigger-Assigned Key Handling

--------------------------------------------------------------210----------------------------------------------------------------------------


Note:注意:只有设计时当您使用创建数据库表的时候,序列名称所显示的 序列标签才起作用。功能描述在 6.2.6 小节 , “从实体对象创建数据库表” 。 以该实体对象为基础可以创建一个序列。

6.6.3.9 失去更新保护 在运行时的框架内,提供自动"lost update"检测实体对象,以确保用户无法在不知不觉中修 改另一个用户在此期间已经更新的数据。这通常检查是比较原始的值,每个持久实体属性对相应的 列的值在数据库中被连续锁定。如果一个实体对象的检测,将是更新一排,就是现在不相符的现状 数据库,它会触发 rowinconsistentexception 异常。 您可以根据这个检测更有效地确定您的实体的一个属性,每当实体得到修改 ,你将得到更新。 典型的选择包括一个在该行版本号码栏或更新的日期栏,变化的指标属性值可能会指派一个数据 库,触发您的写入和刷新,在实体对象使用刷新后插入 ,和刷新后更新属性。另外,您也可以表 明,该实体对象管理更新的值的历史值,属性特征描述在下一节中。检测是否列已被修改,它是最 有效的方式,选择 Change Indicator 来比较,这里只显示改变指标属性的值。 6.6.3.10 历史属性 经常,您需要跟踪实体对象的历史资料,例如: 谁创造了这个实体? 何时他们创建它呢? 谁上次修改这个实体? 何时他们修改它呢? 多少次此行被修改? 实体对象的存储信息,在 History Column 属性,正如图 6-15 中所示 。 如果一个属性的数据是 number , String ,或 Date ,而不是部分的主键,那么你就可以启 用此属性使您的实体自动保存属性值的历史记录。属性如何得到处理,取决于对历史的属性类型。 如果您选择的版本号码类型,ADF 每一次将自动增并更新对象的值。 如果您选择 Created By , Created On , Modified By 或 Modified On ,其值将得到更新根据当前用户的用户名或当前日期。

--------------------------------------------------------------211----------------------------------------------------------------------------


Figure 6–15 Defaulting a Date tor the Current Database Time Using a History Attribute

6.6.3.11 设置鉴别属性的实体对象的继承层次结构 有时一个单一的数据库表信息存储约几个不同的种逻辑上相关的对象。举例来说,一个薪金应 用程序可能与工作时,领薪,和合同雇员都存储在一个单一的雇员表与一 employee_type 列。 EMPLOYEE_TYPE 列的值像 H , S , or C ,是小时工,受薪工,还是合约的雇员。虽然许多雇员的 属性和行为都和所有雇员的属性一样,某些特性和业务逻辑依赖于雇员类型。在这种情况下,可以 方便不同类型的员工使用继承。所有雇员共同的属性和方法,就是基雇员的实体对象,而像子实体 对象 hourlyemployee , salariedemployee , contractemployee 继承基雇员,新增额外的属性 和行为。 鉴别器的属性是说明哪些属性值区分行类型,第 6-26 节, "继承您的业务域层" ,说明 如何设置和使用继承。 6.6.3.12 理解和配置组成的行为 当一个实体对象的构成和其他实体,作为一个逻辑容器其他嵌套实体对象的部分正确发挥自己 的作用。具有以下特点都是启用组成的实体对象:

--------------------------------------------------------------212----------------------------------------------------------------------------


6.6.3.12.1 保护新组成的实体的孤行 当创造组成一个实体,它会检查其外键值,以确保它确定了一个现有的实体作为其母实体。在 创造时间未能提供外键的值,这并不确定一个现有的实体对象,在没有母实体的情况下会抛出

invalidownerexception而不是让一个子实体建立。

注意:如果有必要会在现有的在数据库中发现新的实体存在的约束。

6.6.3.12.2 订购的变化保存到数据库 此功能可确保 DML 操作的顺序,在以正确的顺序组成的实体对象。举例来说,一个 INSERT 语句 为一个新组成的实体对象将要执行的前 DML 操作有关的任何组成的子类。 6.6.3.12.3 在主键Refresh-On-Insert级联组成细节的更新, 当一个新的实体行有一个 Refresh On Insert 主键时,在主键赋值触发器得到主键值时,任何 组成的实体将自动拥有其外键属性值的更新,以反映新的主键值。 有一些额外的组成相关的功能,您可以通过控制设置在该 Association Properties 页创建联合 向导或联合编辑器。 图 6-16 显示,此页为 servicehistoriesforservicerequest 关联了 servicerequest 和 servicehistory 的实体对象。这些设置是默认这一结果从 reverse-engineering 产生,由 ON DELETE CASCADE 外键约束。 图 6-16 设置 servicehistoriesforservicerequest 关联

额外的功能,性能,影响他们的行为,包括下列各项:

--------------------------------------------------------------213----------------------------------------------------------------------------


6.6.3.12.4 级联删除支持 您可以启用或防止删除一个组成父节点,而子节点组成的实体存在。没有被选中,其目的是阻 止删除父节点,如果它包含任何子节点。当选中了,这个选项可以让组成的实体对象被删除掉,不 管其有无子节点。选项没有被选中,然后组成的实体对象履行其正常的 DELETE 语句,并永久性删 除。 如果选项被勾选,那么组成的实体不履行 DELETE 的声明,即数据库的 ON DELETE CASCADE 约 束将处理删除相应行。 6.6.3.12.5 级联更新外键属性时,主键的变化 通过检查 Cascade Update Key Attributes 选项,您可以启用自动更新该外键属性值,在组成 的实体时,主键的值组成的实体会改变。 6.6.3.12.6 锁定复合父实体 使用 Lock Top-Level Container 选项,您可以控制是否加入,删除,或修改一个实体组成的 详细列,但是不应该企图锁定组成的实体,只有这样才准许更改被保存。 6.6.3.12.7 更新Composing Parent History属性 使用Update Top-Level History Columns选项,您可以控制是否加入,删除,或修改一个细节组 成的实体对象,该对象应该更新Modified By和Modified On历史的属性组成的父实体。

6.7 使用声明的验证规则

实体对象编辑器中值得特别注意的是验证页,在这里您可以看到和管理实体声明的验证规则 或其它属性。本框架强制执行实体一级的验证规则,当用户试图改变浏览的行。当用户改变了相关 属性的值,属性级的验证规则的执行。当您添加一个验证规则,你应该提供一个适当的错误讯息, 并且可以轻松地翻译成其他语言,如果需要的话。 甲骨文公司 ADF 默认会装载一些内置的验证规 则,您会看到在本小节。 第 9.3 节, “使用方法校验器”说明如何使用该方法校验自定义验证码, 并在第 26.9 节, “实施自订验证规则“您将了解如何使用一套基本的约束与惯例的约束来约束自 己。 6.7.1 如何添加验证规则 新增一个验证规则,以一个实体对象,使用验证页的实体对象编辑器中,显示在 Figure 6-17 。 要添加一个属性级别的验证规则,选取属性,在声明的验证规则树,点击新建… 。 界定一个实体 一级的验证规则是很简单的,只需您选择根实体对象节点的树,然后点击新建… … 。

--------------------------------------------------------------214----------------------------------------------------------------------------


当添加新的确认规则时, “Add Validation Rule”对话框就会出现。使用“Rule”下拉菜单来 选择想要的确认规则的类型,并且使用另外的控制页来配置它的设置。控制器跟随着选择的确认规 则类型而改变。图6-18举例说明了当为ServiceRequest视图体的ProdId属性定义确认规则的“Add Validation Rule”对话框是什么样子的。这个确认规则被选择来强制使值在100和999之间。当添 加一个确认规则时,如果确认规则失败仍然会显示一个错误信息。

6.7.2 当添加一个确认规则时会怎么 当为一个实体添加确认规则时,JDeveloper 更新它的XML组件定义来包含已经使用和已经进 入的规则属性的条目描述。例如,如果在ProdId属性之上添加确认规则,这一结果在XML文件的 RangeValidationBean条目里。

--------------------------------------------------------------215----------------------------------------------------------------------------


<Entity Name="ServiceRequest" <!-- : --> <Attribute Name="ProdId" IsNotNull="true" Precision="8" Scale="0" ColumnName="PROD_ID" Type="oracle.jbo.domain.Number" ColumnType="NUMBER" SQLType="NUMERIC" TableName="SERVICE_REQUESTS" > <RangeValidationBean xmlns="http://xmlns.oracle.com/adfm/validation" ResId="ProdId_Rule_0" OnAttribute="ProdId" OperandType="LITERAL" MinValue="100" MaxValue="999" > </RangeValidationBean> </Attribute> <!-- : --> </Entity> 在运行环境下,规则被实体在公共信息上自动执行。错误提示信息是一个可说明字符串,它在 实体对象信息绑定类中可作为一个界面控制提示来管理。XML组件属性ResId定义了验证通信中信息 绑定的字符串主键的入口。例6-4显示ServiceRequest实体对象信息绑定的少部分代码,代码的 ProdId_Rule_0关键字显示了错误信息的默认位置。验证得到的错误信息用和上面同样的机制。 例 6-4 包含验证错误信息的实体对象信息绑定 package devguide.model.entities.common; import oracle.jbo.common.JboResourceBundle; // --------------------------------------------------------------------// --- File generated by Oracle ADF Business Components Design Time. // --------------------------------------------------------------------public class ServiceRequestImplMsgBundle extends JboResourceBundle { static final Object[][] sMessageStrings = { // other strings here { "ProdId_Rule_0", "Valid product codes are between 100 and 999" }, // other strings here }; // etc. } 6.7.3 关于验证规则你都需要注意些什么? 了解到一些能被用作实体级别的验证是非常重要的,有时也在属性级别中用到,你会意识到集 合验证被计划在用在小的集合相关的工作中。

--------------------------------------------------------------216----------------------------------------------------------------------------


6.7.3.1 了解内建的实体级别的验证器 你可以使用如下内建的实体级别的验证器: 唯一主键验证器 验证每个实体的主键是唯一的 方法验证器 在实体对象的定制java类中调用一个方法进行程序的相关验证I 6.7.3.2 了解内建的属性级别的验证器 你可以使用如下内建的实体属性级别的验证器 比较验证器 验证一个属性值是否是: ■ 一个文本值 ■ 一个视图对象查询结果的第一行的选中的属性,或者 ■ 一个SQL查询结果的第一行的第一列 列表验证器 验证一个属性是否在内存中的如下值的集合中: ■ 静态列表, ■ 一个视图对象的缺省行集中一个选中的属性,或者 ■ 一个SQL查询结果的行中的第一列的值 范围验证器 验证一个属性是否包括在一个最小值和最大值之间 长度验证器 检验一个属性值的字符串的长度是否小于、等于或者大于一个固定的字符数 正则表达式验证器 检查一个属性值是否与一个正则表达式相匹配 方法验证器 调用一个实体对象的定制Java类进行程序的检查 列表验证器用于验证一个属性是否与一个相关的小的属性集合相匹配,如图6-19所示,如果你 选择 Query Result 或者 View ObjectAttribute 样式的列表验证器,记住,验证器在执行内存扫 描之前或获取所有的查询行以验证属性值是否与列表中的属性值相匹配。由验证器的SQL或视图对 象查询执行的查询不引用在查询的WHERE子句中的正在验证的值。 换句话说,如果你想验证一个用户输入的产品码是否在一个众多产品列表中存在,这点就不起 作用。Section 9.6, "Using View Objects for Validation",解释了你可以通过一个视图对象去 执行一个针对数据库的目标验证查询进行的有效的执行基于SQL的验证的技术。

---------------------------------------------------------------217----------------------------------------------------------------------------


图6-19 列表验证器用于匹配相关的值的小的集合

6.8 使用实体对象和关联 虽然外部的客户端程序可以访问应用模块,并使用其数据模型中的视图对象,但是,设计上, 基于界面的或者程序的客户端都不能直接使用实体对象。Chapter 7, "Building an Updatable Data ModelWith Entity-Based View Objects", 你会学到如何轻易的将视图对象的复杂的SQL查询与业 务逻辑的实施和自动的实体对象的数据库交互柔和在一起,形成强大的应用程序构建的组合。应用 模块数据模型用于满足现有的终端用户任务的需求,该组合启用了一个完全可更新的应用模块数据 模型,使你在你的可重用的业务对象层共享集中的业务逻辑。 当然,在学习使用将实体对象和视图对象组合在一起发挥其巨大威力之前,弄懂如何分别使用 它们尤为重要。通过从更细节方面学习使用这些对象,你可以更加明白在你的应用程序中什么时候 单独使用它们,什么时候混合使用更合适。 由于客户端不能直接使用实体对象,你写的任何使用实体对象的代码都是在一个定制的应用模 块类中或者另外一个实体对象的定制类中的定制代码。本节演示了在devguide.model包中名为 SRService的应用模块的定制方法使用实体对象和关联的例子,以及对在之前的章节中学到的创建 的SRDemo实体进行操作。 6.8.1 通过主键查找实体对象 你可以使用称为实体定义的相关的对象访问一个实体行。运行时,每个实体对象具有一个相应 的实体定义对象,用于描述实体的结构以及管理它描述的实体对象的实例。在devguide.model包中 创建了SRService应用模块并为其启用定制java类之后,假设你想编写一个方法返回一个具体的服 务请求的当前状态,可能看起来与SRServiceImpl.java文件中的retrieveServiceRequestStatus() 方法类似,参见例子6-5。

---------------------------------------------------------------218----------------------------------------------------------------------------


该例子可以分为如下几步: 1. 查找实体定义. 可以通过将实体的名字传递给EntityDefImpl类的静态的方法findDefObject(),获取 devguide.model.entities.ServiceRequest 实体的实体定义对象,oracle.jbo.server包中的 EntityDefImpl类为每个实体对象实现了一个实体定义。 2. 构造一个键 构建一个包含你想查找的主键属性的Key 对象。这种情况下,你创建一个包含单个requestId 值的键,该值可以作为参数传递给方法。 3. 使用键查找实体对象 使用实体定义的findByPrimaryKey() 方法可以通过在当前的事物对象传送的键查找实体,这 可以通过使用getDBTransaction()方法从应用模块中获取。代表实体对象行的具体的类是 oracle.jbo.server.EntityImpl类. 4. 返回它的某些数据给调用者 使用EntityImpl 的getAttribute()方法返回状态属性值给调用者 Example 6–5 通过键值查找ServiceRequest的实体对象 // Custom method in SRServiceImpl.java public String findServiceRequestStatus(long requestId) { String entityName = "devguide.model.entities.ServiceRequest"; // 1. Find the entity definition for the ServiceRequest entity EntityDefImpl svcReqDef = EntityDefImpl.findDefObject(entityName); // 2. Create the key Key svcReqKey = new Key(new Object[]{requestId}); // 3. Find the entity object instance using the key EntityImpl svcReq = svcReqDef.findByPrimaryKey(getDBTransaction(),svcReqKey); if (svcReq != null) { // 4. Return the Status attribute of the ServiceRequest return (String)svcReq.getAttribute("Status"); } else { return null; } } 注: 除了典型的单属性值类型的键外,oracle.jbo.Key 对象构造器使用对象数组以支持多键 的创建。 6.8.2 使用访问器属性访问相关联的实体 在6.3节, "Creating and Configuring Associations",中,你看到关联使得从一个实体对象 到另一个的访问变得非常容易。这是一个简单的可以帮助演示实际上这意味着什么的方法。你可以 增加一个findServiceRequestTechnician()方法,可以查找一个服务请求,然后访问相关联的代表 分配给请求的技术员User实体对象

---------------------------------------------------------------219----------------------------------------------------------------------------


然后,由于这是应用模块中通过ID找到ServiceRequest实体对象的第二个方法,你可能想将该 功能重构进retrieveServiceRequestById() 帮助方法中,该防范你可以在需要通过ID查找服务请 求的应用模块的任何地方重用 // Helper method in SRServiceImpl.java private EntityImpl retrieveServiceRequestById(long requestId) { String entityName = "devguide.model.entities.ServiceRequest"; EntityDefImpl svcReqDef = EntityDefImpl.findDefObject(entityName); Key svcReqKey = new Key(new Object[]{requestId}); return svcReqDef.findByPrimaryKey(getDBTransaction(),svcReqKey); } 实例 6–6 展示了findServiceRequestTechnician()的代码。该例由三个基本步骤组成: 1. 通过ID查找ServiceRequest 使用retrieveServiceRequestById() 可以通过ID获得ServiceRequest实体 2. 使用访问器属性访问相关联的实体 在6.3.1.1, "Changing Entity Association Accessor Names" 中, 你可以为 ServiceRequestsAssignedToUser关联的关联访问器进行重命名,从而ServiceRequest实体能 够访问它的两个相关的具有TechnicianAssigned 访问器名的User实体对象中的一个。使用与 用于获取任一个实体属性值相同的getAttribute()方法,你可以使用关联访问器的名字传送, 并从关系的另一边获取实体对象。 3. 向调用者返回数据Return some of its data to the caller. 对返回的的User实体使用 getAttribute()方法,可以返回通过连接他的姓和名而得到的技术 员的名字。 注意,你不必写任何SQL来访问相关的User实体。ServiceRequest 和User实体对象之间的ADF 关联中获取的关联信息足以使数据的导航自动完成。

---------------------------------------------------------------220----------------------------------------------------------------------------


Example 6–6 Accessing an Associated Entity Using the Accessor Attribute // SRServiceImpl.java中的定制方法 public String findServiceRequestTechnician(long requestId) { // 1. 查找服务的请求实体 EntityImpl svcReq = retrieveServiceRequestById(requestId); if (svcReq != null) { // 2. 使用关联访问器属性访问User实体对象 EntityImpl tech = (EntityImpl)svcReq.getAttribute("TechnicianAssigned"); if (tech != null) { // 3. 返回实体对象的属性给调用者 return tech.getAttribute("FirstName")+" "+tech.getAttribute("LastName"); } else { return "Unassigned"; } } else { return null; } } 6.8.3 更新或者删除现有的实体行 一旦你获得一个实体行,更新或者删除它就很简单。你可以增加像例6-7中的updateRequestStatus 方法用于处理这些工作。该例分为如下三步: 1. 通过ID查找ServiceRequest 使用 retrieveServiceRequestById() 方法根据ID获取ServiceRequest 实体 2. 为一个或者多个属性设置新值 使用EntityImpl 类的 setAttribute() 方法是利用传入的新值更新Status属性值 3. 提交事物 使用应用模块的getDBTransaction() 方法访问现有的事物对象并调用它的commit()方法提交 事物。

---------------------------------------------------------------221----------------------------------------------------------------------------


例 6–7 更新现有实体行 // SRServiceImpl.java中的定制方法 public void updateRequestStatus(long requestId, String newStatus) { // 1. 查找服务请求实体 EntityImpl svcReq = retrieveServiceRequestById(requestId); if (svcReq != null) { // 2. 为状态属性设置新值 svcReq.setAttribute("Status",newStatus); try { // 3. 提交事物 getDBTransaction().commit(); } catch (JboException ex) { getDBTransaction().rollback(); throw ex; } } } 删除实体行的例子与次类似,除了找到现有的实体后,在提交事务之前要使用下边的行将实体 删除: // 删除实体 svcReq.remove(); 6.8.4 创建一个新的实体行 除了可以使用实体定义查找实体行外,你也可以使用它创建新的。把侧重点从服务请求转到产 品,你可以写如例6-8中的createProduct()一样的方法,接受一个新的产品的的名字和描述作为输 入,返回分配给它的产品ID。假设Product 实体对象的ProdId属性已经更新为6.6.3.8, "Trigger-Assigned Primary Key Values from a Database Sequence",中讨论的DBSequence类型, 那么它的值会自动更新映射在SRDemo 应用模式下的PRODUCTS表上的ASSIGN_PRODUCT_ID触发器从 PRODUCTS_SEQ序列中分配给它的值。 例子分为以下几步: 1. 查找实体定义 使用 EntityDefImpl.findDefObject() 为Product 实体查找实体定义 2. 创建一个新的实例. 使用实体定义中的createInstance2()方法为实体对象创建新的实例

---------------------------------------------------------------222----------------------------------------------------------------------------


======================================================================== 注: 真正的方法名在末尾有a 2 。正规的createInstance() 方法有保护的方法,用于有开发者根 据D.2.5节 "EntityDefImpl Class" of Appendix D, "Most Commonly Used ADF Business Components Methods". 进行定制。类型AttributeList的第二个参数用于提供在创建时 必须提供的属性值,它不能用于对列表中发现的所有的值进行初始化。例如,当使用该API为一个 复合子实体创建一个新的实例是,你必须为作为第二个参数传递的AttributeList提供复合父实体 的外键属性值,不过不提供则会引发InvalidOwnerException.异常。 ======================================================================== 3. 设置属性值. 使用实体对象的setAttribute() 方法为新的实体行指定名字和描述. 4. 提交事务 调用当前事物对象的commit()方法提交事物. 5. 返回触发器分配的产品ID给调用者 使用getAttribute() 获取一个ProdId 属性作为DBSequence,然后调用 getSequenceNumber().longValue() 返回给调用者一个long型的序列号。 例 6–8 创建一个新的实体行 // SRServiceImpl.java中的定制方法 public long createProduct(String name, String description) { String entityName = "devguide.model.entities.Product"; // 1. 为产品实体查找实体定义 EntityDefImpl productDef = EntityDefImpl.findDefObject(entityName); // 2.为产品实体创建一个新的实例 EntityImpl newProduct = productDef.createInstance2(getDBTransaction(),null); // 3. 设置属性值 newProduct.setAttribute("Name",name); newProduct.setAttribute("Description",description); try { // 4. 提交事务 getDBTransaction().commit(); } catch (JboException ex) { getDBTransaction().rollback(); throw ex; } // 5. 访问数据库触发器分配的ProdId值并返回 DBSequence newIdAssigned = (DBSequence)newProduct.getAttribute("ProdId"); return newIdAssigned.getSequenceNumber().longValue(); }

---------------------------------------------------------------223----------------------------------------------------------------------------


6.8.5 使用静态的Main方法测试 此时,你一定想测试你的定制应用模块方法。将测试代码构建成一个对象的常用的技术是将该 代码包含在static main() 方法中。例6–9 演示了一个可以加到SRServiceImpl.java 定制应用模 块类的一个main()方法,用于测试你上边写的例子。你会用到5.7, "How to Create a Command-Line Java Test Client"中提到的Configuration对象初始化并使用应用模块进行测试。 ======================================================================== 注: Configuration对象在oracle.jbo.client包中暗含了它可以作为一个应用Client 访问应用模 块,一个 main()方法是一中程序的、命令行的客户端,因此这是可以的。另外,尽管直接将 createRootApplicationModule()的返回值返回给应用模块的实现类,在这种情况下由于它是一个 应用模块的客户端,而且main()方法的代码驻留在应用模块实现类本身中,因此这么做也是合法的。 ======================================================================== 简单看一下代码,可以发先它是在练习使用上面创建的四个方法: 1. 获取服务请求101的状态 2. 获取分配给服务请求101的技术员的名字 3. 将服务请求101的状态设置为非法值"Reopened" 4. 创建一个提供空产品名的新产品 5. 创建一个新产品、演示它的新分配的产品ID

---------------------------------------------------------------224----------------------------------------------------------------------------


例 6–9 从内部使用Main方法测试SRService 应用模块 // SRServiceImpl.java中的main方法 public static void main(String[] args) { String amDef = "devguide.model.SRService"; String config = "SRServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); /* * NOTE: This cast to use the SRServiceImpl class is OK since this * ---- code is inside a business tier *Impl.java file and not in a * client class that is accessing the business tier from "outside". */ SRServiceImpl service = (SRServiceImpl)am; // 1. 获取服务请求101的状态 String status = service.findServiceRequestStatus(101); System.out.println("Status of SR# 101 = " + status); // 2. 获取分配给服务请求101的技术员的名字 String techName = service.findServiceRequestTechnician(101); System.out.println("Technician for SR# 101 = " + techName); try { // 3. 将服务器请求101的状态设置为非法值"Reopened" service.updateRequestStatus(101,"Reopened"); } catch (JboException ex) { System.out.println("ERROR: "+ex.getMessage()); } long id = 0; try { // 4. 创建一个提供空产品名的新产品 id = service.createProduct(null,"Makes Blended Fruit Drinks"); } catch (JboException ex) { System.out.println("ERROR: "+ex.getMessage()); } // 5. 创建一个新产品、演示它的新分配的产品ID id = service.createProduct("Smoothie Maker","Makes Blended Fruit Drinks"); System.out.println("New product created successfully with id = "+id); Configuration.releaseRootApplicationModule(am,true); } 运行SRServiceImpl.java 类,调用例子6-9中的main()方法有如下输出: Status of SR# 101 = Closed Technician for SR# 101 = Bruce Ernst ERROR: The status must be Open, Pending, or Closed ERROR: JBO-27014: Attribute Name in Product is required


New product created successfully with id = 209 注意,如果6-17所示,试图将服务请求状态设置为"Reopened"失败了,因为ServiceRequest 实 体对象的状态属性的List Validator出现错误.该验证器配置为仅允许静态列表Open, Pending, 或 者Closed中的值.我们还可以注意到 起初试图调用没有产品名的createProduct()方法会由于内建 的对Product实体对象的名字属性的强制验证而引发一个异常. ---------------------------------------------------------------225----------------------------------------------------------------------------

======================================================================== 注: 你可能会自己发问:,一个客户端应用如何调用我在SRService应用模块中创建的定制服务,而 不是有同一个类中的main()函数调用? 在 8.4节, "Publishing Custom Service Methods to Clients"你会学到启用它的简单步骤。. 你会看到这是Application Module Editor 的Client Interface 页的一个直接的配置选项。 ======================================================================== 6.9 为一个实体对象产生一个定制java类 本章中你已经看到,所有的数据库交互和实体对象的许多声明式运行时功能都可以在没有定 制Java代码的情况下完成。当你想不使用声明式特征就可以为你的实体实现定制业务逻辑时,你需 要为需要定制代码的实体启用定制Java产生项。附录D, "Most Commonly Used ADF Business Components Methods", 提供了一个你编写、使用和重载你的定制实体对象和实体定义类的快速方 法.后续的章节也讨论了SRDemo应用如何在它的实体类中使用定制代码的例子。 6.9.1如何产生定制类 要为实体对象启用定制Java类的产生选项,使用Entity Object Editor 的Java页。如图6-20 所示,有三个可选的与实体对象相关的Java类。虽然实际上实体集合类很少被定制,但是,实体对 象类的定制还是经常使用,Entity Definition Class的使用要更少些: ■ 实体集合类 — 很少定制. ■ 实体对象类 — 最常定制,它代表数据库表的一行 ■ 实体定义类 —更少定制,它代表管理实体行的定义其结构的类. 图 6–20 实体对象定制Java产生选项

---------------------------------------------------------------226----------------------------------------------------------------------------


6.9.1.1 选择产生实体属性的访问器 当你启用定制实体对象类的产生选项时,如果你还选择了Accessors复选框,那么JDeveloper 为实体对象的每个属性产生getter和setter方法. 对于ServiceRequest 实体对象,相应的定制 ServiceRequestImpl.java类会在内部产生如下的方法: public Number getSvrId() {...} public void setSvrId(Number value) {...} public String getStatus() {...} public void setStatus(String value) {...} public Date getRequestDate() {...} public void setRequestDate(Date value) {...} public String getProblemDescription() {...} public void setProblemDescription(String value) {...} public Number getProdId() {...} public void setProdId(Number value) {...} public Number getCreatedBy() {...} public void setCreatedBy(Number value) {...} public Number getAssignedTo() {...} public void setAssignedTo(Number value) {...} public Date getAssignedDate() {...} public void setAssignedDate(Date value) {...} public ProductImpl getProduct() {...} public void setProduct(ProductImpl value) {...} public RowIterator getServiceHistories() {...} public UserImpl getTechnicianAssigned() {...} public void setTechnicianAssigned(UserImpl value) {...} public UserImpl getCreatedByUser() {...} public void setCreatedByUser(UserImpl value) {...} 这些方法允许你使用数据类型编译时检查的行数据。也就是说,不用写像下面的这行以获取ProdId 属性的值: Number prodId = (Number)svcReq.getAttribute("ProdId"); 你可以这样写代码: Number prodId = svcReq.getProdId(); 你会发现使用后面的方法,如果你不小心将ProdId写成了ProductCode,Java编译器将会捕获排版 错误: // 拼写错误导致编译错误 Number prodId = svcReq.getProductCode(); 如果没有实体对象访问器方法,如下不正确的代码行不能被编译器捕获: // 属性名和类型名都是错的,但是编译器无法捕获异常 String prodId = (String)svcReq.getAttribute("ProductCode"); 它包含一个拼写错误的属性名和getAttribute()方法返回的一个错误,当你使用Row接口上的 通用的API—基类EntityImpl的实现时,这种错误将会在运行时抛出异常,而不是在编译时捕获。

---------------------------------------------------------------227----------------------------------------------------------------------------


6.9.2 当产生定制类时会发生什么 当你选择产生一个或者多个定制Java类时,JDeveloper创建你指名的文件。对于名为 devguide.model.entities.ServiceRequest 的实体对象,实体对象类的定制java文件的缺省名是 ServiceRequestImpl.java. 两个文件在同一个目录/devguide/model/entities 中作为组件的XML 组件定义文件被创建。 实体对象的java产生选项将在后续的对View Object Editor的访问时映射到Java页上。与XML 定义文件一样,JDeveloper在你的定制java类中保持产生的代码与你在编辑器中做的改动保持一 致。如果以后处于某种原因,你不打算使用定制java文件,在java页中不进行相关选项的检查将会 删除定制java文件。

6.9.3 查看和使用定制java文件 与所有的ADF组件一样,当你在Application Navigator中选择一个实体对象时,Structure窗 口显示所有的相关的实现文件。只有一个必需的文件时XML组件定义文件。你可能也注意到,当为 一个组件定义一个可移植的UI控制链时,也会有一个组件信息绑定文件。如图6-21所示,当你起作 用了定制java类的产生选项时,它们也会出现在实体对象的Source文件夹下。当你需要查看或者使 用源代码产生定制java文件时,有两种方式可以在源代码编辑器中打开文件: ■ 在上下文菜单中选择相关的Go to选项,如图6-21所示 ■ Y你可以双击Structure窗口中Sources文件下的文件 图 6–21 查看和使用实体对象的定制java类

---------------------------------------------------------------228----------------------------------------------------------------------------


6.9.4你需要了解自定义Java类些什么 查看下面关于自定义Java类的补充说明部分 6.9.4.1关于一个实体对象的架构基类 当你使用一个“XML-only”实体对象时,在运行时的功能是提供默认ADF业务组件实现类。每 个自定义Java类产生时都将自动的继承一个适合的ADF业务组件基类,因此你的代码继承了默认的 行为并且能很容易的添加或使用它。一个实体对象将扩展EntityImpl,,然而这实体定义类也将扩 展EntityDefImpl(这些类都在oracle.jbo.server包中)。 6.9.4.2你可以安全地为自定义组件文件添加代码 基于以前多数不好的经验,一些开发者在产生的Java源文件中添加自己的代码时会犹豫不决 的。每个自定义的Java源代码文件,JDeveloper都会在文件的最上方创建和维护包含像下面那样的 注释来说明在这文件中添加你自己的代码是安全的。 // --------------------------------------------------------------------// --- File generated by Oracle ADF Business Components Design Time. // --- Custom code may be added to this class. // --- Warning: Do not modify method signatures of generated methods. // --------------------------------------------------------------------当你点击组件编辑器中的OK或Apply按钮时,JDeveloper不会盲目的更新文件。代替的是,它 对方法执行巧妙的更新,它需要维护,且保持你的自定义代码的完整性。 6.9.4.3配置默认的Java产生参数 当你需要定制他们在运行时的行为或更喜欢用强类型访问邦定变量或视图行属性时,你想看到怎样 为一个视图对象产生自定义Java类。 为ADF组件自定义Java类配置默认设置,你可以选择Tools | Preferences...菜单并打开 BusinessComponents页面来设置你的参数以便在以后创建的业务组件中使用。Oracle推荐开发者在 不是自定义Java类中使用ADF业务���件默认的设置。当你在一个自定义Java代码中加入一个特殊的 需求,你可以在这节里面学到。你可使用你需要的一个自定义java组件来举例说明。随着时间的过 去,你会发现默认的设置在你的工作中最有用。 6.9.4.4属性索引和调用访问器产生码 正如你所看到的,实体对象在被设计时的功能不是在XML-only方式中就是使用一个关联的XML 组件定义和一个自定义Java类。由于这个特点,属性值没有被存储在一个实体类的私有成员域中, 因此这样的一个类是不出现在XML-only位置中的。代替的是,除了一个名称外,属性在实体的XML 组件定义中被分配的索引从0开始,并在文件中按关联的<Attribute>和<AccessorAttribute>标签 有序的排序着。在运行时,一个实体行中的属性值通过基类EntityImpl被存储在一个稀疏的数组结 构中处理,编入的索引是通过在实体列表中属性的位置编入的。然而,当你为实体对象启用定制Java 类时,实现与JDeveloper在实体对象类中自动维护产生的代码相关。 理解这些代码的用途很有用。 例如,实体ServiceRequest 的定制Java类中, 例 6–10 显示了每个属性或者访问器属性有有个相 应产生的整型常量,JDeveloper确保这些常量值正确的映射XML组件定义中的属性的顺序。 例 6–10 在定制实体java类中属性常量自动维护 public class ServiceRequestImpl extends EntityImpl { public static final int SVRID = 0; public static final int STATUS = 1; public static final int REQUESTDATE = 2; public static final int PROBLEMDESCRIPTION = 3; public static final int PRODID = 4;


public static final int CREATEDBY = 5; public static final int ASSIGNEDTO = 6; public static final int ASSIGNEDDATE = 7; public static final int TECHNICIANASSIGNED = 8; public static final int CREATEDBYUSER = 9; public static final int PRODUCT = 10; public static final int SERVICEHISTORIES = 11; // etc. 你会注意到在实体对象类中自动维护的强类型getter and setter方法按照如下方式使用这些属性: // In devguide.model.entities.ServiceRequestImpl class public Number getAssignedTo() { return (Number)getAttributeInternal(ASSIGNEDTO); // <-- Attribute constant } public void setAssignedTo(Number value) { setAttributeInternal(ASSIGNEDTO, value); // <-- Attribute constant } 与实体属性常量相关的自动维护的代码的最后一方面是getAttrInvokeAccessor() 和 setAttrInvokeAccessor()方法。这些方法优化了通过数字索引进行访问的性能,索引是基类 EntityImpl中的通用代码如何在执行通用处理时访问属性。一个 getAttrInvokeAccessor() 方法 的例子可以参考ServiceRequestImpl.java 类. setAttrInvokeAccessor()方法与之类似。

---------------------------------------------------------------230----------------------------------------------------------------------------


// In devguide.model.entities.ServiceRequestImpl class /** getAttrInvokeAccessor: generated method. Do not modify. */ protected Object getAttrInvokeAccessor(int index,AttributeDefImpl attrDef) throws Exception { switch (index) { case SVRID: return getSvrId(); case STATUS: return getStatus(); case REQUESTDATE: return getRequestDate(); case PROBLEMDESCRIPTION: return getProblemDescription(); case PRODID: return getProdId(); case CREATEDBY: return getCreatedBy(); case ASSIGNEDTO: return getAssignedTo(); case ASSIGNEDDATE: return getAssignedDate(); case SERVICEHISTORIES: return getServiceHistories(); case TECHNICIANASSIGNED: return getTechnicianAssigned(); case CREATEDBYUSER: return getCreatedByUser(); case PRODUCT: return getProduct(); default: return super.getAttrInvokeAccessor(index, attrDef); } } 关于产生的属性索引相关的代码需要记住以下几点: 可以做的事 ■ 如果需要的话,在强类型属性的getter和setter方法内部增加定制代码: ■ 使用Entity Object Editor 修改实体对象属性的顺序或者类型 JDeveloper会替你改变getter and setter方法的Java签名以及相关的XML组件定义 不可以做的事 ■ 不要修改getAttrInvokeAccessor() 和setAttrInvokeAccessor() 方法. ■ 不要手工修改属性索引值on't change the values of the attribute index numbers by hand. ======================================================================== 注:如果由于源控制的合并冲突或者其他的原因,需要手工编辑产生的属性常量,你必须保证从零 开始的编号映射相应的实体对象XML组件定义文件中的 <Attribute> 和<AccessorAttribute> 标 签的顺序。 ========================================================================

---------------------------------------------------------------231----------------------------------------------------------------------------


6.9.5 使用自定义实体类的方案例子比较 为了更好的说明使用不同的自定义所产生的实体类在普通的 EntityImpl 类中所起的作用,例 6–11 展 示 了 一 个 SRServiceImpl.java 方 法 的 版 本 , 你 可 以 执 行 上 述 的 第 二 个 的 SRService2Impl.java 应用模型类。有一些需要注意的事项如下: „ 执行属性访问必须使用强类型属性访问。 „ 关联的访问器属性在另一个关联中返回强类型的实体类 „ 在你的自定义实体类中使用 getDefinitionObject()方法可以避免所有的实体定义名都为字符 串。 „ createPrimaryKey()方法在你的自定义实体类中只是单一的为一个实体创建 Key 对象。 Example 6–11 Programmatic Entity Examples Using Strongly Typed Custom Entity Object Classes package devguide.model; import devguide.model.entities.ProductImpl; import devguide.model.entities.ServiceRequestImpl; import devguide.model.entities.UserImpl; import oracle.jbo.ApplicationModule; import oracle.jbo.JboException; import oracle.jbo.Key; import oracle.jbo.client.Configuration; import oracle.jbo.domain.DBSequence; import oracle.jbo.domain.Number; import oracle.jbo.server.ApplicationModuleImpl; import oracle.jbo.server.EntityDefImpl; import oracle.jbo.server.EntityImpl; // --------------------------------------------------------------------// --- File generated by Oracle ADF Business Components Design Time. // --- Custom code may be added to this class. // --- Warning: Do not modify method signatures of generated methods. // --------------------------------------------------------------------/** * This custom application module class illustrates the same * example methods as SRServiceImpl.java, except that here * we're using the strongly typed custom Entity Java classes * ServiceRequestImpl, UserImpl, and ProductImpl instead of working * with all the entity objects using the base EntityImpl class. */ public class SRService2Impl extends ApplicationModuleImpl { /**This is the default constructor (do not remove) */ public SRService2Impl() { } /* * Helper method to return a ServiceRequest by Id */


private ServiceRequestImpl retrieveServiceRequestById(long requestId) { EntityDefImpl svcReqDef = ServiceRequestImpl.getDefinitionObject(); Key svcReqKey = ServiceRequestImpl.createPrimaryKey(new DBSequence(requestId)); return (ServiceRequestImpl)svcReqDef.findByPrimaryKey(getDBTransaction(), ---------------------------------------------------------------232----------------------------------------------------------------------------

} /* * Find a ServiceRequest by Id */ public String findServiceRequestStatus(long requestId) { ServiceRequestImpl svcReq = retrieveServiceRequestById(requestId); if (svcReq != null) { return svcReq.getStatus(); } return null; } /* * Create a new Product and Return its new id */ public long createProduct(String name, String description) { EntityDefImpl productDef = ProductImpl.getDefinitionObject(); ProductImpl newProduct = (ProductImpl)productDef.createInstance2( getDBTransaction(),null); newProduct.setName(name); newProduct.setDescription(description); try { getDBTransaction().commit(); } catch (JboException ex) { getDBTransaction().rollback(); throw ex; } DBSequence newIdAssigned = newProduct.getProdId(); return newIdAssigned.getSequenceNumber().longValue(); } /* * Update the status of an existing service request */ public void updateRequestStatus(long requestId, String newStatus) { ServiceRequestImpl svcReq = retrieveServiceRequestById(requestId); if (svcReq != null) { svcReq.setStatus(newStatus); try {


getDBTransaction().commit(); } catch (JboException ex) { getDBTransaction().rollback(); throw ex; } } } /* * Access an associated Used entity from the ServiceRequest entity */ public String findServiceRequestTechnician(long requestId) { ServiceRequestImpl svcReq = retrieveServiceRequestById(requestId); if (svcReq != null) { UserImpl tech = (UserImpl)svcReq.getTechnicianAssigned(); if (tech != null) { return tech.getFirstName()+" "+tech.getLastName(); } ---------------------------------------------------------------233----------------------------------------------------------------------------

else { return "Unassigned"; } } else { return null; } } // Original main() method generated by the application module editor // // /**Sample main for debugging Business Components code using the tester. // */ // public static void main(String[] args) { // launchTester("devguide.model", /* package name */ // "SRServiceLocal" /* Configuration Name */); // } /* * Testing method */ public static void main(String[] args) { String amDef = "devguide.model.SRService"; String config = "SRServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); /*


* NOTE: This cast to use the SRServiceImpl class is OK since this * ---- code is inside a business tier *Impl.java file and not in a * client class that is accessing the business tier from "outside". */ SRServiceImpl service = (SRServiceImpl)am; String status = service.findServiceRequestStatus(101); System.out.println("Status of SR# 101 = " + status); String techName = service.findServiceRequestTechnician(101); System.out.println("Technician for SR# 101 = " + techName); try { service.updateRequestStatus(101,"Reopened"); } catch (JboException ex) { System.out.println("ERROR: "+ex.getMessage()); } long id = 0; try { id = service.createProduct(null,"Makes Blended Fruit Drinks"); } catch (JboException ex) { System.out.println("ERROR: "+ex.getMessage()); } id = service.createProduct("Smoothie Maker","Makes Blended Fruit Drinks"); System.out.println("New product created successfully with id = "+id); Configuration.releaseRootApplicationModule(am,true); } }

---------------------------------------------------------------234----------------------------------------------------------------------------

6.10 给一个实体对象添加临时属性 除了表中各列所有对应的所有属性外,你的实体对象也可以包含临时属性,且它所对应的持有 者和值都能在 JAVA 中显示。这部分讲解一个简单的例子即为 User 实体对象添加一个名为 FullName 的临时属性,它的值是连接 FirstName 和 LastName 两个属性的值。 6.10.1 怎样添加一个临时属性 给一个实体对象添加一个临时属性: 1. 在实体对象编辑器中打开“Attributes”页,并点击 New…按钮。如图 6-22 所示: 2. 为属性输入一个名称,如 FullName, 3. 设置 Java 属性类型,如 String, 4. 取消选定 Persistent 复选框 5. 如果要计算值则要设置 Updateable 中的 Never 项,然后点击“OK”按钮创建属性。


---------------------------------------------------------------235----------------------------------------------------------------------------

6.10.2 当你添加临时属性将产生什么 当你添加一个临时属性和属性实体对象编辑,Jdeveloper 会修改实体对象的 XML 组件定义并显 示出新的属性。但是一个持久的实体在 XML 中的定义如下: <Attribute Name="FirstName" IsNotNull="true" Precision="30" ColumnName="FIRST_NAME" Type="java.lang.String" ColumnType="VARCHAR2" SQLType="VARCHAR" TableName="USERS" > </Attribute> 一个临时属性的<Attribute>标签如下,没有表名和列名为$none$: <Attribute Name="FullName" IsUpdateable="false" IsQueriable="false" IsPersistent="false" ColumnName="$none$" Type="java.lang.String" ColumnType="$none$" SQLType="VARCHAR" > </Attribute>


6.10.3 在一个实体类中添加 java 代码并执行计算 一个临时属性是一个数据值的占位符。如果你更改“Updateable”项中的临时属性为”While New” 或”Always”,则在最后用户就可以为属性输入一个值。如果你想临时属性显示出计算值,你则应 该将“Updateable”项中的值设为“Never”并且自写代码来计算值。 为实体属性添加一个临时属性后,使它成一个有效的属性,你需要: „ 在 Java 页的实体对象编辑器中启用自定义对象类。 „ 在访问的方法中为一个临时属性编写代码,并返回计算值。 例如,产生 UserImpl.java 视图行类后,Java 代码在 getFullName()方法中返回它的计算值,代码 如下: // Getter method for FullName calculated attribute in UserImpl.java public String getFullName() { // Commented out original line since we'll always calculate the value // return (String)getAttributeInternal(FULLNAME); return getFirstName()+" "+getLastName(); } ---------------------------------------------------------------236----------------------------------------------------------------------------

确保 FullName 计算属性是有效的,无论何时 LastName 或 FirstName 属性都可能被最终用户更 改,你可以为他们各自的设置方法添加一行来标注 FullName 作为“dirty”,任意一个值随时都可 以设置。 // Setting method for FirstName attribute in UserImpl.java public void setFirstName(String value) { setAttributeInternal(FIRSTNAME, value); // Notify any clients that the FullName attribute has changed populateAttribute(FULLNAME,null,true, /* send notification */ false, /* markAsChanged */ false);/* saveCopy */ } and public void setLastName(String value) { setAttributeInternal(LASTNAME, value); // Notify any clients that the FullName attribute has changed populateAttribute(FULLNAME,null,true, /* send notification */ false, /* markAsChanged */ false);/* saveCopy */ }

---------------------------------------------------------------237----------------------------------------------------------------------------


---------------------------------------------------------------238----------------------------------------------------------------------------


这章节描述怎样构建可更新的视图对象与实体对象自动化的合作。 这章节包括以下部分: 7.1 节,“Entity-Based 视图对象介绍” 。 7.2 节,“创建 Entity-Based 视图对象”。 7.3 节,“在结合的视图对象中包含所涉及的实体”。 7.4 节,“创建一个¡Association-Based 视图连接” 。 7.5 节,“测试 Entity-Based 视图对象交互”。 7.6 节,“给一个 Entity-Based 视图对象添加临时属性”。 7.7 节,“理解视图对象和实体对象在运行时是怎样合作”。 7.8 节,“Entity-Based 视图对象在程序中所有的作用”。 7.9 节,“Entity-Based 视图对象和 Read-Only 视图对象间的区别”。 7.1 Entity-Based 视图对象介绍 一个 Entity-Based 视图对象支持可更新行。视图对象查询即将来临的客户机任务所需要 的数据,然后在业务层中与一个或者多个的实体对象合作,并自动的验证和保存改变的视图行。 像 Read-Only 视图对象一样,一个 Entity-Based 视图对象压缩一个 SQL 查询,能连接到 master/detail 层次,并能在你的应用模型的数据模型中使用。 这章节的最后你将理解图 7-1 所示的观点: „ 你可以通过从一个或者多个的实体对象所涉及的属性来定义一个可更新的视图对象。 „ 你可以使用多重的、关联的实体对象来简单化的显示涉及信息所起的作用。 „ 你可以定义一个基于实体关联的视图链接。

---------------------------------------------------------------239----------------------------------------------------------------------------


„ „

在一个应用模型提供事务的上下文中使用你的 Entity-Based 视图对象。 在运行时,这视图行委派它的属性存储和验证。 这章节将说明了怎样获得 Entity-Based 视图对象的实例,即一个应用模块的数据模型使客户 端能够在某种程度上查询、修改、插入和删除业务层信息,

注:这章使用相同基本的SRDemo应用业务域层的ServiceRequest,ServiceHistory Product, User 和ExpertiseArea视图对象,都在第六章的"利用实体对象创建一个业务域层"。通过一个运行版本 的 例 子 来 做 实 验 , 可 以 从 http://otn.oracle.com/documentation/jdev/b25947_01 的 Example Downloads页上下载DevGuideExamples工作域和查看EntityBasedViewObjects工程。 ======================================================================== 7.2 创建一个 Entity-Based 视图对象 创建一个 Entity-Based 视图对象比创建一个 read-only 视图对象更容易,自从你在 SQL 声明 中没有类型。比起 read-only 副本,Entity-Based 视图对象也能提供更多有用的运行时功能。 7.2.1 怎样创建 Entity-Based 视图对象 创建一个 Entity-Based 视图对象应使用创建视图对象向导。向导用到的是 Business Tier > ADF Business Components 中的“New Gallery”。假如你想在 devguide.model.queries 包中创建一个 StaffList 视图对象来重新得到一个全体成员的可更新的列表。

---------------------------------------------------------------240----------------------------------------------------------------------------


如图7-2所示,在Name页上的步骤 1提供视图对象的名称的包名。使用Updatable Access through  Entity Objects项保持默认的设置来管理数据。

在Entity Objects页上的步骤2,在视图对象中选择你所要用的实体对象数据。图7-3显示选 择User实体对象并显示在Selected列表中的结果。在这个列表中的一个实体是已知道的,并作为实 体使用——因为它记录了这个实体对象,且这个视图对象将被使用。它也将被作为一个实体引用的 思想,因为这视图对象的引用的属性来自那个实体。

---------------------------------------------------------------241----------------------------------------------------------------------------


在Attributes页上的步骤3,从Available列表中选择你所要用到的属性并且把它们移到 Selected列表中。图7-4,UserId,Email,FirstName和LastName等属性被选中。

在Attribute Settings页上的步骤4里,为了改变更改他们的名称或者一些最初的设置,你可 以使用Select Attribute来转换视图对象间的属性。这个例子你可能接受默认值。 在SQL Statement页上的步骤5里,如图7-5所示,Jdeveloper将自动产生基于你把选择实体的 属性的Select声明。你可以添加一个Where和Order by子句来查询过滤和排序过的数据。由于 StaffList将仅仅在Users表中显示行。表中User_Role列的值为technician或manager中的一个。你 可以在Where文本框中包含一个适当的Where子句。按last_name和first_name进行数据排序,可以 在Order by文本框中包含适当的Order by子句。Where和Order by文本框中不能出现含有where 或 order by关键字。这视图对象执行查询时,将在运行时添加这些关键字。

---------------------------------------------------------------242----------------------------------------------------------------------------


点击 Finish 完成创建一个视图对象 7.2.1.1 创建一个视图对象含有所有属性的实体对象 当你想允许客户端对一个潜在的实体对象的所有属性起作用,你可以使用创建视图向导如 7.2.1 节“怎样创建 Entity-Based 视图对象”所描述中的那样。选择实体对象后,选择 Attributes 页上的所有属性。然而,这样频繁的操作,那样可以能更快的完成应用浏览器中的相同的任务。 创建一个新的Entity-Based视图对象: 1. 在应用浏览器中选择想要得到的实体对象。 2. 从上下文菜单中选择New Default View Object...。 3. 在Create Default View Object对话框中为新的视图对象提供一个包和组件的名称。如图7-6 所示。

---------------------------------------------------------------243----------------------------------------------------------------------------


创建新的Entity-Based视图对象也将是同样的,你可以用创建视图对象向导创建。它将有一个 单一的实体用法引用在应用浏览器中你所选中的实体对象,且包含它的所有属性。它最初既没有 where子句也没有order by子句,你可以使用视图对象编辑器: „ 移除不需要的属性 „ 用一个where子句缩小查询范围 „ 用一个order by 子句排序结果 „ 定制一些视图对象属性 7.2.2 当你创建一个Entity-Based视图对象将发生什么 当你创建一个Entity-Based视图对象,Jdeveloper创建XML组件定义文件来表现视图对象的设 置并把它保存在相应的包名的目录下。在图7-2里,视图对象在包devguide.model.queries中,且 名为StaffList。因此这XML文件被创建为工程的源路径下的./devguide/model/queries /StaffList.xml。这个XML文件包含相关的SQL声明、实体用法的名称和各属性间的性质的信息。如 果你想看它的内容,你可以通过选择应用浏览器中的视图对象的XML文件和结构窗口中查看相应的 Source文件夹。双击StaffList.xml节点将在编辑器中打开这XML,则你就可以查看它的内容。 ======================================================================== 注:如果你的IDE-Level业务构成Java产生的参数选择,这向导也可能创建一个可选的自定义视图 对象类StaffListImpl.java和/或一个视图行类StaffListRowImpl.java类。 ======================================================================== 7.2.3 编辑一个存在的Entity-Based视图对象定义 你创建一个Entity-Based视图对象后,你可以通过使用视图对象编辑器来编辑它的一些设置。 选择应用浏览器中的上下文菜单上的“Edit”菜单项,或者双击这视图对象来打开视图对象编辑器。 通过打开这编辑器的不同面板,你可以更改查询中的Where子句和Order by子句,改变名称、添加 命名邦定变量和添加UI控制提示,控制Java产生选项和配置其它设置。 7.2.4 你需要了解视图对象 每个视图对象属性都继承相应实体对象属性的特性。 7.2.4.1 视图对象属性继承潜在的实体对象的属性的特性 Entity-Based视图对象的一个有趣的方面是每个属性都引用潜在的实体对象的属性并继承属 性的特性。图7-7显示视图对象编辑器选中UserId属性。你可以看到它的特性像这Java Attribute Type和Query Colume Type所描述的,且他们的值是引用User实体对象中的UserId属性。一些特性 如属性的数据类型被继承且在视图对象级不能更改

--------------------------------------------------------------244----------------------------------------------------------------------------


其它属性如Queryable和Updateable通过继承得到,但能被重写,他们的重写设置比继承设置 有更多的限制。例如,在User实体对象中的UserId属性有一个“Updateable”设置中有Always选项。 如图7-7所示,这视图对象允许你设置相应的视图对象属性来限制设置,如”While New”或 者”Never”。然而,如果在实体对象User中的UserId属性在”updateable”中设置为”Never”, 则编辑器不允许StaffList的有关视图对象属性像设置为”Always”那样有限制。

7.3 在加入的视图对象中包含涉及到的实体 对于从一个主要的业务域对象使用次要的引用消息来帮助最终用户理解什么是外键属性的补 充信息是极为平常的。获得ServiceRequest实体对象的实例,它包含Number类型的外键属性,如: „ CreatedBy,描述了用户谁创建了请求。 „ AssignedTo,描述了请求被分配给哪个用户。 „ ProdId,描述了请求属于哪个产品。 从经验来看,你应该知道展示一些专有的"源数据"数值给最终用户不是很有帮助。实际上,所 涉及的信息应该从User和Product实体对象的显示上来改善应用的可用性。一个典型的解决方案应 包括执行加入的查询来重新得到主要和参考信息的结合。开发在每个查询行中组装"虚拟"域,和使 用基于额外的查询表的参考信息。当最终用户在编辑数据时可以更改外键值,这是附加的挑战。

---------------------------------------------------------------245----------------------------------------------------------------------------


例如,当从一个technician再指定一个服务请求到另一个technician,最终用户希望参考信息 是即时的。幸运的是entity-based视图对象很容易的支持包含的参考信息,并且参考信息总是最新 的。主要的条件来起作用,它的特点是充当视图对象的主要实体的实体对象和提供参考信息的实体 对象的关联的存在。 s这章节主要讲解的是怎样修改默认创建的ServiceRequests视图对象和从User和Product实体 对象所包含的参考信息。 7.3.1怎样在一个视图对象中包含引用实体 在一个视图对象中包含引用实体,在一个已经有单一实体用法的entity-based视图对象上打开 视图对象编辑器,打开Entity Objects页。第一个实体使用是在这个页面上的Selected列表里,它 被当作是这个视图对象的主要实体使用。这个列表并不仅限于单一的实体使用。在视图对象中使用 附加的的实体对象,在Available列表中选中他们并把他们移到Selected列表中。 7.3.1.1给视图对象添加附加的引用实体用例 图7-8所示,在已存在的视图对象ServiceRequests中添加三个引用实体用例:一是Product, 二是单独的User实体用例。当你点击Selected列表中的一个实体方法时,Reference复选框中状态 就会被选中,第二个和接下来的的实体用例都会被添加到一个被标记为参考信息的信息视图对象 中。同样的,次要的实体方默认情况下是不可更改的,他们的Updateable复选框是不被选中的。 注:当添加一个次要实体用例时,“updateable”默认标识为false,“Reference”默认标识为true。 这是目前公用的用例模板。在27.9的“使用多个可更改的实体创建一个视图对象” 节中,将会讲 解较少的公用用例模板,但仍然是非常有用的,拥有一个多个可更改的实体用例结合的视图对象。 ======================================================================== s这关联的下拉列表将显示你所选中的实体用例所涉及到的关联的名称。这Alias文本框允许你 在默认名称不能被清除时为实体用例给出一些有意义的别名。例如,把实体对象User的两个实体用 例移动在Selected列表中,最初的别名分别为User1和User2。你可以在图中看到,我们可以为他们 重新命名,分别为Technician和Customer,这样可以在这个视图对象提供的参考信息中更清楚。重 要的是,如图所阐明的那样,当你为同一个实体添加多个实体用例时,你需要使用Association下 拉列表来选择与主要实体用例关联的描述。为Technician实体选择关联的 ServiceRequestsAssignedToUser,并为Customer选择关联的ServiceRequestsCreatedByUser。

---------------------------------------------------------------246----------------------------------------------------------------------------


7.3.1.2 从引用实体方法选择附加属性 添加这些次要的实体方法后,转换到视图编辑器的Attribute页并从这些新的主法中选择你想 在这视图中包含的附加属性。如图7-9的例子,把下列附加的属性移到Select列表中: „ Name属性来自Product实体。 „ Email属性来自Technician实体。 „ Email属性来自Customer实体。 你可以注意到,即使你不打算包含他们,但JDeveloper自动的校验每个实体方法在Selected列 表中的主要属性部分。如果它在列表中不存在,则JDeveloper会自动帮你添加。

---------------------------------------------------------------247----------------------------------------------------------------------------


选择SQL Statement页,你可看到JDeveloper已经在Select声明中包含了新的列,而且在可Where 文本框中加入适当的子句: ((ServiceRequest.PROD_ID = Product.PROD_ID) AND (ServiceRequest.ASSIGNED_TO = Technician.USER_ID)) AND (ServiceRequest.CREATED_BY = Customer.USER_ID) 7.3.1.3 重命名引用实体的属性 展开视图对象编辑器的左边的树中的Attributes节点,你可以看到附加的属性已经被添加到列 表的末尾。因为默认的属性名称不能被清除,因此只有通过集资的选择每个属性,你可像下面那样 对他们进行重命名: ■ Name -> ProductName ■ ProdId1 -> PKProdId (Primary Key from Product entity usage) ■ Email -> TechnicianEmail ■ UserId -> PKTechnicianUserId (Primary Key from Technician entityusage) ■ Email1 -> CustomerEmail ■ UserId1 -> PKCustomerUserId (Primary Key from Customer entity usage) 7.3.1.4 从引用实体中移除不需要的主键属性 这视图对象属性符合主要实体标识一个视图行的关键属性。当你添加次要实体时,JDeveloper 也能标记视图对象属性符合他们的的主键属性在视图行中关键的一部分。当你的视图对象由一个单 一的,可更改的的实体和许多引用实体组成时,这个主键属性来自于主要的实体并能够唯一标识一 个视图行。这些附加的主键属性是不需要的,可以把Key Attribute设为false。按上面创建视图对 象,把下面的属性设置为false,以致于Key Attribute不再被选中:PKProdId,PKTechnicianUserId 和PKCustomerUserId。

---------------------------------------------------------------248----------------------------------------------------------------------------


7.3.1.5 隐藏引用实体中的主键属性 一般情况下由于你不希望主键属性自动被添加到视图对象中并显示,你可以设置UI Control Hints 页上的Display Hint属性的值为Hide,如图7-10所示。

7.3.2 当你在一个视图对象中引用实体将会发生什么 在一个实体对象中,当你包含次要实体时,JDeveloper会修改视图对象的XML组件定义包含关 于附加实体的信息。例如,如果你查看这ServiceRequests.xml文件,关于这个视图对象包含三个 附加的引用实体的,你将看到文件中多个<EntityUsage>元素记录的信息。例如,你将看到一个主 要实体的入口,如下: <EntityUsage Name="ServiceRequest" Entity="devguide.model.entities.ServiceRequest"/> 次要引用实体的入口将有些不同,包含关于主要实体关联所涉及到的信息: <EntityUsage Name="Product" Entity="devguide.model.entities.Product" Association= "devguide.model.entities.associations.ServiceRequestsForProduct" AssociationEnd= "devguide.model.entities.associations.ServiceRequestsForProduct.Product" SourceUsage="devguide.model.queries.ServiceRequests.ServiceRequest" ReadOnly="true" Reference="true"/>

---------------------------------------------------------------249----------------------------------------------------------------------------


在XML中每个属性入口都指示了引用哪个实体。这个入口为ProblemDescription属性显示它所 涉及的ServiceRequest实体: <ViewAttribute Name="ProblemDescription" IsNotNull="true" EntityAttrName="ProblemDescription" EntityUsage="ServiceRequest" AliasName="PROBLEM_DESCRIPTION" > </ViewAttribute> CustomerEmail属性被Customer实体方法引用。 <ViewAttribute Name="CustomerEmail" IsUpdatable="false" IsNotNull="true" EntityAttrName="Email" EntityUsage="Customer" AliasName="EMAIL1" > </ViewAttribute> 视图对象编辑���在开发时使用关联信息自动的构建视图对象的Where子句。当最终用户改变外 键属性值时,它在运行时使用这些信息来保持消息是最新的。 7.3.3 你需要了解连接视图对象什么 如果你的视图对象引用多个实体对象,这些被作为单独的实体显示在一个业务组件的图表上。 注意,在合适的时候,你可以修改默认的内连接为外连接。 7.3.3.1 在一个业务组件图表中显示视图对象 6.4节”在你的业务层创建一个实体图表”讲解了怎样在你的业务层创建一个业务组件图表。 除了支持实体对象外,JDeveloper的UML图表支持并允许你拖放视图对象到图表上,且显示他们的 结构和实体。如果在包devguide.model.design中创建一个新的名为SRServiceDataModel的业务组 件图表,并且从应用浏览器中拖放视图对象ServiceRequests到图表上,你可以从图7-11上看到。 当查看一个展开的节点时,这个图表显示一个包含视图对象的实体。

---------------------------------------------------------------250----------------------------------------------------------------------------


Figure 7–11 View Object and Its Entity Usages in a Business Components Diagram

7.3.3.2在适当的时候修改默认的连接子句为外连接 当JDeveloper为两个连接表即有主要实体和次要实体表的关联创建Where子句时,默认情况下 总是创建内连接。研究ServiceRequests视图对象接近的where子句: ((ServiceRequest.PROD_ID = Product.PROD_ID) AND (ServiceRequest.ASSIGNED_TO = Technician.USER_ID)) AND (ServiceRequest.CREATED_BY = Customer.USER_ID) 当服务请求还没分配给一个technician时,他们的AssignedTo属性将为NULL。默认的内连接以 上述的产生为条件将不重新分配服务请求。你想通过ServiceRequests视图对象不分配可看的和可 更改的服务请求,你将需要重新到视图对象编辑器的SQL Statement页来更改User表中的内连接查 询中可能为NULL的ASSIGNED_TO列的值。修改Where子句像以下显示的那样,为相关的表中允许在连 接中缺少数据的等号的一边附加(+)操作符: ((ServiceRequest.PROD_ID = Product.PROD_ID) AND (ServiceRequest.ASSIGNED_TO = Technician.USER_ID (+) )) AND (ServiceRequest.CREATED_BY = Customer.USER_ID) 7.4创建一个Association-Based视图链接 正像使用只读视图对象,你可以链接entity-based视图对象到其他的视图对象来形成一些复 杂的master/detail层次。包括这个案例在master和detail视图对象都是entity-based视图对象 且他们各自的实体用例存在关联时候,在创建的步骤上有些不同。在这种情境下,由于这关联捕 获设置的源和目标属性产生关系,你可能通过指定基于的关联创建一个视图链接。 ---------------------------------------------------------------251----------------------------------------------------------------------------

7.4.1怎样创建一个Association-Based视图链接 你可以使用创建视图链接向导来创建一个Association-Based视图链接。 创建一个Association-Based视图链接 1. 从Business Tier > ADF Business Components上的the New Gallery项上选择Create View Link wizard。 2. 在Name页的步骤1里,提供一个包和组件的名称。假如你想在包 devguide.model.queries.viewlinks里创建一个视图链接,名为 RequestsAssignedToTechnician。 3. 在View Objects页上的步骤2里,在Select Source Attribute树里展开devguide.model.queries 包里的StaffList视图对象。在Select Destination Attribute树里展开ServiceRequests视图 对象。注意,除了视图对象外,Association-Based视图对象相应的关联也将出现在列表中。


如图7-12所示,在Both和Destination树中选择相同的ServiceRequestsAssignedToUser关联, 然后点击ADD按钮添加关联到下面的表中。点击Next和Finish按钮完成新的视图对象的创建。 图7-12 从源视图对象的实体用例中选择一个关联到目标视图对象中

4. 下一步,在ServiceRequests视图对象和一个关于服务请求的历史条数的详细信息的视图对象 之间创建另外一个Association-Based视图链接。你已经有主要的ServiceRequests对象,但是 你在连接他们之前,你需要为先创建一个视图对象。你可以使用上述的方法,在应用浏览器中 选中devguide.model.entities包里的ServiceHistory实体对象和从上下文菜单中选择New Default View Object...来创建一个基于这个实体的视图对象,且在包 devguide.model.queries中,名为ServiceHistories。

---------------------------------------------------------------252----------------------------------------------------------------------------


============================================================== 注意:在创建缺省的视图体时,可以使用联合从已存在的包的列表中选择 devguide.model.queries ============================================================== 5、最后,重复使用上面的步骤来创建基础联合,在联合的基础上连接 ServiceRequests 视图体和 新 的 ServiceHistories 视 图 体 , 涉 及 到 各 自 主 要 的 实 体 用 法 。 在 devguide.model.queries.viewlinks 包中命名视图联合为 HIstoryLinesForRequest。另外的 方法来避免包名字的类型,如图 7-13 所示,可以在上下文包节点的菜单中使用“New View Link…”选项。

7.4.2 当创建基础联合视图链时会发生什么? 当你创建基础联合视图链时,JDeveloper创建XML组成的定义文件来声明在目录中的设置 和保存它,与它的包名相对应。在上面的例子中,视图链被命名为RequestsAssignedToTechnician 并且在devguide.model.queries.viewlinks包中为HistoryLinesForRequest,所以XML文件将会在 /RequestsAssignedToTechnician.xml和/HistoryLinesForRequest.xml中创建,在工程的源路径下 的/devguide/model/queries/viewlinks目录中。这个XML文件包含关于联合的声明信息,涉及到指 定的源和目标视图体。另外为了保存视图链成分定义,JDeveloper也会在视图链中更新XML源视图 体的定义来添加关于视图链属性的信息。

---------------------------------------------------------------253----------------------------------------------------------------------------


7.5 测试以实体为基础的视图对象交互 您测试的以实体为基础的视图对象交互,都为只读。首先添加的实例的视图对象,建立起来的 数据模型的一些应用模块,然后使用应用模块的业务组件的浏览器进行测试。 7.5.1 概述业务组件浏览器功能为一可更新的数据模型 您可以找到的业务组件浏览器,可以快速测试和调试您的应用程序模块。 图 7-14 给出了一 个大概的操作,所有的业务组件的浏览器工具栏按钮执行。 图 7-14 业务组件的浏览器功能,可更新的数据模型

7.5.2 添加 View 对象实例的数据模型 以下相同的步骤,在第 5.10.4.3 , “如何使活跃的主/详细信息,协调方面的数据模型” 已 经说明,添加以下 View 对象的实例到数据模型的 srservice 应用模块,如图 7-15 : •

选择现有 servicerequests View 对象,例如在数据模型树一,然后添加一个细节,例如命 名为 servicehistories 的 servicehistories 视图的对象,在可用 list 中作为一个子 servicerequests。 选择现有 stafflist View 对象,例如在数据模型树一,然后添加一个细节,例如命名为 assignedservicerequests 的 servicerequests 视图对象,在可用 list 作为一个子 stafflist。 选择新的 assignedservicerequests View 对象,例如在数据模型树一,然后添加一个细节, 例如命名为 assignedservicehistories 的 servicehistories 视图的对象,在可用 list 作 为一个子 servicerequests。

---------------------------------------------------------------254----------------------------------------------------------------------------


图 7-15 业务组件的浏览器显示修改的结果,一个以实体为基础的 View 对象

7.5.3 如何测试以实体为基础的视图对象交互 假设您已经建立了 srservice 应用模块的数据模型如图 7-15,以测试它做到以下几点: 测试实体为基础的视图对象: 1. 从上下文菜单中选择应用模块在 Application Navigator 中,并选择 Test.

2. 单击连接对业务组件的浏览器连接对话框,使用默认 srservicelocal 配置进行测试。 注意: .默认情况下,一个应用模块,只是其默认情况下,本地配置 命名为 appmodulename Local。.如果您为您的应用模块创造了额外的 配置,并要进行测试,只需选择想要的配置,从业务组件配置下拉列表 中就连接对话框,然后点击连接 。

7.5.4 当您测试的实体为基础的视图对象交互会发生什么事 当您启动该业务组件的浏览器,JDeveloper 启动测试工具用一个单独的进程和业务组件的 浏览器出现。如图 7-16,该树在左侧的显示屏显示的层次结构的视图对象实例中,并包括额 外的节点之间的高级视图对象实例和详细视图对象实例,例如连结执行活动的主/明细作为当前 行的变化。

---------------------------------------------------------------255----------------------------------------------------------------------------


图 7-16 srservice 数据模型在 Business Components Tester 中

连按两下该 historylinesforrequest2 视图连结,例如在执行该树主对象-如果在测试 session 它没有被执行, -并显示主/详细的测试面板, 图 7-1 7。在视图对象的节点额外的上下文菜单项, 让您重新执行该查询,如有需要,删除测试面板,并执行其他任务。你看到一个类似的主/详细资 料,面板当您使用的业务组件的浏览器在第 5.5 节, “测试视图对象使用的业务组件的浏览器” 。 你可以看到和翻阅查询结果。其中一个重要的差异是一个直接的结果,使用一个实体基于视图对象。 而不是看到不可用 UI 控件显示只读资料,您现在看到的编辑栏位,并免费进行试验,creating, inserting,updating,validating,committing 和 rolling back。 图 7-17 实例实体为基础的视图对象是完全可编辑的

尝试与实验多层次的高级/详细资料,层次结构,在同一时间内开放多种测试面板,并使用显示 结果在一个窗口工具栏上按钮的“ POP ”标签出来的框架内成为一个单独的窗口中看到多视图 对象的数据。 ---------------------------------------------------------------256----------------------------------------------------------------------------


7.5.5 模拟最终用户与您的应用模块的数据模型的互动 使用业务组件浏览器,你可以模拟最终用户与您的应用模块的数据模型前的互动,那些您开 始建立的任何自定义用户界面。甚至以后,您的用户界面的页面兴建,你会来欣赏使用的业务组 件的浏览器,以协助诊断问题。您可以在业务组件浏览器中重现问题,如果问题在于在视图或控 制器层的应用,或者是一个在商业服务层的应用模组本身问题。 仅使用主/详细的测试页中显示的图 7-17 ,您可以测试您申请的几个功能区。 7.5.5.1 测试主/明细协调 使用导航按钮在工具栏上,你可以看到该服务的历史行目前的服务请求是正确的统筹。 7.5.5.2 测试的用户界面控制的提示 提示显示在测试面板,帮助你看到你是否已正确界定一个用户友好的标签文字提示,控制每 个属性。举例来说,在 servicerequests View 对象的 requestdate 属性。您的鼠标悬停在编辑 栏位为 requestdate 领域,您会在短期内看到工具提示控制的提示文字出现,如果您定义它。工 具提示中显示的说, “ 该日期应视为服务的要求,已建立” ,这是你早在第 6.5.1 节, “如 何添加属性控制提示”中提到了如何建立暗示对您的实体对象。既然你没有具体界定任何新的控 制提示为 servicerequests View 对象,这说明该实体为基础的视图对象属性继承其控制的暗示, 那些在背后的实体对象的属性。 7.5.5.3 测试视图对象参考实体的用法 翻阅资料-或使用指定的视图标准工具栏按钮搜索-您可以验证是否服务的要求尚未分配的正 确显示。如果你正确地改变了查询的 WHERE 子句中使用外部联接,这些行会如预期般出现。 通过改变 assignedto 属性不同的 user ID -双击该 stafflist View 对象,例如,在业务组件 浏览器浏览为一些有效的工作人员的 user ID -您可以验证相应的参考信息自动更新,以反映新技 术员。 通过观察 ProductName 值和两个 Email Address,例如行 servicerequests ,您可以看到相应 的用户友好的 ProductName,和 Email Address,双方客户谁创造的要求,这是分配。您也可以通 知一个事件,无论是客户的 email address 和技术员的 email address,是由用户实体对象的电子 邮件地址继承的标签文字提示 。它不会明确给最终用户的 email address 是哪。

---------------------------------------------------------------257----------------------------------------------------------------------------


补救的情况下,无论是技术员的电子邮件地址和客户的电子邮件地址显示是一样的,继承的电 子邮件地址标签,编辑 servicerequests View 对象,并确定一个标签,文本控制,暗示双方在 View 对象的水平。设置标签文字提示,以技术员的电子邮件地址为 technicianemail 属性和客户的电子 邮件地址为 customeremail 属性。使用业务组件的浏览器来验证这些控制的提示定义在 View 对象 的水平之上的,它通常会从背后的实体对象继承。 7.5.5.4 测试业务域层验证 试图改变的状态属性值的一个封闭的服务要求有一个值。当您尝试域标签,您将会获得一个例 外: ( oracle.jbo.attrsetvalexception )的状态,必须公开,未完成或已关闭 的基础上,其他简 单的宣示性验证定义的规则在第 6.7 , “使用的宣示性验证规则” ,您可以尝试更新 problemdescription 值,收到错误: ( oracle.jbo.attrsetvalexception )问题描述不能包含这个词 最后,您可以尝试进入一个 prodid 值 1234 侵犯范围的验证规则: ( oracle.jbo.attrsetvalexception )有效的产品代码是介于 100 和 999 按一下回滚按钮在工具 栏上恢复数据传送到以前的状态。 当您推出的业务组件浏览器,开放的属性选项卡上连接对话框,你可以取代默认区域设置改变: • •

jbo.default.country = IT jbo.default.language = it

这些属性设置,您可以看到是否意大利语言翻译的 servicerequest 实体对象,控制的提示是正确 的位置。您会发现,Stato , Aperto Il 和 problema 标签而不是状态 。您也将看到的格式 requestdate 的变化,从一个值如 2006 年 3 月 12 日 16 时 55 分至 2006 年 12 月 3 日 16 时 55 分一 样。 7.5.5.6 测试连续的创建和预设值的产生 按一下该按钮, 创造连续在工具栏上为 servicerequests View 对象,例如建立一个新的空白 行。任何域,有一个宣示性的默认值会出现与值的空列。该 dbsequence 值 svrid 属性只读,在新 的连续与它的临时 negative 数目。后进入所有必填字段-尝试 1 00 为 p rodid,300 为所要求的域 -按一下按钮,该 commit 作出 commit 操作。实际上,引发分配的主键会出现在 svrid 域的 commit 成功后。 7.5.5.7 测试新的有正确的外键的详细列 如果您尝试添加一项新的服务,历史上连续到现有的服务请求,您会发现该视图连结自动确保 外键 svrid 属性值,在新的 servicehistories 行是设定为目前掌握的服务请求行的值。

---------------------------------------------------------------258----------------------------------------------------------------------------


7.6 添加计算和瞬态属性,以一个实体为基础的 View 对象 在除了拥有属性映射到背后的实体对象,您的视图对象可以包括计算属性,不映射任何实体对 象的属性值。两种计算属性称为: • •

SQL 的计算属性 ,当其值是文字作为表达的 SQL 查询的选择集合 Transient 属性,当他们的值是 query 的一部分查询

本节说明如何添加两种,首先说明如何添加一个 SQL 计算 lastcommafirst 属性,然后在瞬态 计算属性命名 firstdotlast 向 stafflist View 对象。最后,您会看到一个 View 对象可以包括一 个实体映射的属性,这本身就是一个 Transient 属性在实体对象的水平,以确保所有受支持的组合 是可见的。 7.6.1 如何添加一个 SQL 计算属性 要添加一个 SQL 计算属性的一个实体为基础的 View 对象: 1. 2. 3. 4. 5. 6. 7. 8.

打开属性页,在视图对象编辑器,并单击新建。 输入一个名称的属性,如 lastcommafirst 。 设置 Java 的属性类型 ,以一个适当的值,如字符串 。 检查映射到列的 SQL 复选框。 提供一个 SQL 的表达,在表达的域像是 LAST_NAME||', '||FIRST_NAME 考虑改变的 SQL 栏的别名名称匹配的属性 验证数据库查询的栏位类型 ,并调整长度(或精密/规模)作为适当的。 单击确定以创建属性。

图 7-18 增加了新的 SQL 计算属性


---------------------------------------------------------------259----------------------------------------------------------------------------

7.6.2 当您添加一个 SQL 计算属性会发生什么事 当您添加一个 SQL 计算的属性和完成视图对象编辑器,更新 JDeveloper 的 XML 组件定义为视 图对象,以反映新的属性。而与一个实体映射的属性一样,看起来像这样在 XML 的,从背后的实体 属性,继承它的大部分性能,它是映射: <ViewAttribute Name="LastName" IsNotNull="true" EntityAttrName="LastName" EntityUsage="User1" AliasName="LAST_NAME" > </ViewAttribute> 在相比之下, SQL 的计算属性的 viewattribute 标记看起来像下面的描述。正如人们预料的, 它没有 entityusage 或 entityattrname 信息包括数据资料, SQL 的表达: <ViewAttribute Name="LastCommaFirst" IsUpdatable="false" IsPersistent="false" Precision="62" Type="java.lang.String" ColumnType="VARCHAR2" AliasName="FULL_NAME" Expression="LAST_NAME||&#39;, &#39;||FIRST_NAME" SQLType="VARCHAR" > </ViewAttribute> 注 意: 是XML的性格的参考撇号,参照其数值ASCII码为39 (十进制) 。其他性状 文字,需要类似的结构在XML是less-than,greater-than,和符号字符。

7.6.3 如何添加瞬态属性 一个瞬态属性,以一个实体为基础的 View 对象: 1. 2. 3. 4. 5.

新增

打开属性页,在视图对象编辑器,并单击新建。 输入一个名称的属性,如 firstdotlast 。 设置 Java 的属性类型为 string 。 离开映射到列的 SQL 的复选框选中。 单击确定以创建属性。

---------------------------------------------------------------260----------------------------------------------------------------------------


图7-19 增加了新的瞬态属性

新增一个瞬态实体对象的属性,以一个实体为基础的View对象,首先确保你有一个实体使用的 实体对实体对象页的视图。然后转到属性页和理想的属性从现有的集合到选定的集合。使用这些步 骤,您就可以添加fullname计算属性从用户实体对象到stafflist View对象。 如果您使用的业务组件的浏览器,以测试 srservice 数据模型后,添加这三个属性向 stafflist View 对象集合中,您可以看到他们的效果显示在图 7-20 : 图 7-20 stafflist View 对象与三种计算属性

7.6.4, 当您添加了瞬态属性会发生什么事 当您添加的瞬态属性和完成视图对象编辑器,更新的 JDeveloper 的 XML 组件定义为视图对象, 以反映新的属性。瞬态属性在 XML 是类似的 SQL 计算的 viewattribute 标记,缺乏一个表达的信息。 ---------------------------------------------------------------261----------------------------------------------------------------------------


7.6.5 添加 Java 代码中的视图行类执行计算 瞬态属性是一个占位符。如果你改变了更新的信息瞬态属性,当 New 或 Always ,最终用户可 以输入一个值为属性。如果您想要的瞬态属性,以显示计算的值,那么您通常会离开更新的信息设 置,用自定义 Java 代码来计算的值。 后添加的瞬态属性的视图,对象,使之成为计算瞬态属性,你需要: • •

使自定义视图列类对 Java 的网页的视图,选择产生存取方法 写 Java 程式码内的存取方法的瞬态属性返回计算值

举例来说,新产生的 stafflistrowimpl.java 视图行类, Java 代码返回其计算值该 getlastcommafirst ( )方法是这样的: // In StaffListRowImpl.java public String getFirstDotLast() { // Commented out this original line since we're not storing the value // return (String) getAttributeInternal(FIRSTDOTLAST); return getFirstName().substring(0,1)+". "+getLastName(); }

注意: 在第 26.8, “实施自动属性重新计算” ,您将了解编码技术的计算属性在 实体行级须重新计算时,其中的属性值对它们所依赖的是修改。你可以采取一个非常 类似的策略,在视图列层级造成自动重新计算视图对象属性。

7.6.6 您可能需要了解的瞬态属性的一些东西 视图的对象包括在 SQL 表达您的 SQL 计算的属性,在 SELECT 集合及其查询在运行时。该数据 库是一个评估的表达和它返回的结果作为在查询中值列。值在您每一次执行查询时得到重新评估。 7.7 了解如何使视图对象和实体对象在运行时协作, 对于他们自己,视图对象和实体对象,简化了两项重要工作,每家企业应用开发人员需要做的: • •

与 SQL 查询结果工作 修改和验证列在数据库表

---------------------------------------------------------------262----------------------------------------------------------------------------


实体型视图对象可以查询任何选择的数据,您的最终用户需要看到或修改的数据。任何数据, 让他们改变了验证并保存您的可重复使用的业务域层。主要是那些只有你可以知道的: • •

你决定什么业务逻辑应该在您的业务域层 你决定什么疑问,说明您需要的资料放在屏幕上

这些事情使您的应用独一无二的。内置功能,您的实体为基础的视图对象处理,其余的执行细 节。您已经试行了上述与实体为基础的视图对象,在业务组件的浏览器和目睹的一些好处,但现在 的时间去了解究竟如何让他们工作。本节按部就班,透过局面检索和修改数据,通过一个实体为基 础的View对象,并指出了后台一个有趣的方面是怎么回事。但在之前,您需要一些行按键及对什么 样的作用实体缓存的背景,您将明白实体为基础的View对象。 7.7.1 每个视图行或实体行有一个相关的键 显示在图 7-21 ,当您在 oracle.jbo 方案使用该行界面工作时。它还包含一个方法,所谓的 getkey ( ) ,您可以使用存取的键对象识别任何行。通知指出,在 oracle.jbo.server 包扩展 行界面的实体界面。这种关系提供了一个具体解释为什么实体行是如此恰当。即使一个实体的行支 持的其他功能封装业务逻辑和处理数据库访问,您仍然可以处理任何实体列一排 。 .记得,双方的视图行和实体行支持任何单一属性或多属性键,所以键对象相关的任何特定的行会 概括的所有属性组成及其键。一旦你有一个键的对象,您可以使用 findbykey ( )方法在找到一 个行的基础上对任何行设置。 图7-21 从任何角度来看行或实体行支持检索其查明键

======================================================================== 注 意: 当您定义的一个实体为基础的View对象,默认情况下,首要的键属性为它的所有实体的用法 是把他们的键属性的信息设置为 TRUE 。这是最佳做法,后来停用的键属性的信息为键属性参考实 体的惯例。自视图对象属性相关的主键的可更新的实体惯例必须的一部分,综合的视图行的键,其 键属性的信息,不能被禁用。

---------------------------------------------------------------263----------------------------------------------------------------------------


7.7.2 实体缓存在 Transaction 中扮演什么角色 应用模块是一个 transaction 容器,为一个逻辑单位工作。在运行时,它从你的供应命名配置 收购了数据库连接使用信息,它代表的 transaction 管理的配套 transaction 对象。由于逻辑单 元的工作,可能涉及寻找和修改多个实体行不同类型, transaction 对象提供了一实体缓存作为 一个“工作区”举行的实体行所涉及的当前用户的 transaction 。每个实体缓存包含一个单一实体 的类型,所以 transaction 涉及双方的用户和 servicehistory 实体对象建立工作的副本,这些实 体列在两个单独的缓存里。 通过使用一个实体对象的相关实体的定义,你可以在一个应用模块写代码,以寻找和修改现有 的实体行。 图 7-22 ,调用 findbyprimarykey ( )将实体定义为 servicerequest 实体对象,您 可以检索该行与键。如果它尚未在实体缓存中,实体的对象从数据库中执行查询,检索它。此查询 从后台的表选择所有的实体对象的持久性属性,并找到对列相应的实体对象的主键属性行使用适当 的 WHERE 子句。此后,科学家们试图在缓存中通过键值在同一 transaction 将找到它找到相同的实 体行,避免了访问数据库。在一个特定的实体缓存,实体列索引他们的主键。这使得寻找实体在行 高速缓存的快速运作。 当您使用相关的实体行使用关联的存取方法,他们也来自该实体缓存,或者是来自数据库,如 果他们没有在缓存中。最后,实体的缓存,也是本地新的实体行等待被保存。换言之,当您使用 createinstance2 ( )方法对实体的定义,以创建一个新的实体行,这是添加到实体缓存。 图 7-22 当 transaction servicerequest 时 ,实体行是储存在 servicerequest 实体缓存中的

当一个实体行是创建,修改,或删除,这是自动报读到有待改变的 transaction 集合。当您对 transaction 对象调用 commit( ),其等候集合上的变化,验证新的或修改的实体行可能仍无效。 当实体行在等候集合上,都是一个数据库有效的 transaction 事件, SAVEPOINT 和坐标节省实体行 到数据库。如果一切顺利成功,它的事件在最后数据库的 commit 声明。如果失败, transaction 执 行回滚,以保存 ,以允许用户更正错误并再试一次。 ---------------------------------------------------------------264----------------------------------------------------------------------------


对象所使用的一个应用模块代表的工作集的实体行为一个单一的最终用户的 transaction 。按 照设计,它不是一个共享的,全局高速缓存。数据库引擎本身是一个非常有效率的共享,复合全局 高速缓存的。而不是试图重复 30 多年的微调认为已经到数据库的共享,全局缓存的功能, ADF 的 业务组件,自觉地拥抱它。刷新一个单一的实体对象的数据从数据库中在任何时候,您可以调用其 刷新( )方法。您可以 setclearcacheoncommit ( )或 setclearcacheonrollback ( )对 transaction 对象的控制是否实体缓存清理,在 commit 或回滚。默认值分别是 false 和 true。 transaction 对象也提供了一个 clearentitycache ( )方法,您可以使用以编程方式明确实体排 某一实体类型(或所有类型) 。通过清理一个实体缓存,实体行的该类型将来自数据库的下次刷 新,他们发现主键的一个实体为基础的 View 对象,正如你将看到在以下各节。 7.7.3 元数据捆绑在一起,干净利落地分离的角色,数据源和数据库 当您想要通过关联的存取找到一个实体列由主键和导航有关的实体,您只有通过实体为基础的 View 对象才能完成任务。在一个实体为基础的 View 对象, View 对象和实体对象的发挥干净利落 地分离的角色: • •

视图对象是数据源 :它使用 SQL 撷取的数据 。 实体对象是数据库 :它处理验证和保存数据的变化。

因为视图对象和实体对象有干净分离的角色,您可以建立一个 100 不同的视图,对象-预测, 过滤,添加,整理资料的方式,无论在您的用户界面的应用需要申请后-没有任何改变,可重用的 实体对象。事实上,在一些较大的开发,组织,工作队负责的核心业务,网域层的实体对象,那些 具体应用模块和视图对象可能是完全分开的,以解决最终用户的请求。这一极其灵活,互利互惠的 关系是启用的中继一个实体为基础的 View 对象封装如何选择清单中列相关的属性,一个或多个基 本实体对象。 想象一个新的请求,将出现您的最终用户所请求的网页很快地看到开放和等待服务请求。他们 希望看到的唯一的服务请求 ID ,状态和问题的描述;技术员分配要解决的请求;和天数的请求。它 应该是可能的更新状态。 7-23 图显示,一个新的实体为基础的 View 对象命名 openproblemsandassignees 能够支持这项新请求。 该虚线,在这个图代表的中继中被俘的视图,对象的 XML 组件定义地图选择集合列在查询的属 性的实体对象使用。

---------------------------------------------------------------265----------------------------------------------------------------------------


有关 View 对象及其查询中应注意的几件事是: • • •

添加数据从主实体的使用( servicerequest ) ,从中等参考使用实体( 用户 )的基础 上,该关联有关指派技术,你已经看到在上面的例子。 它的使用外部联接的 servicerequest.assigned_to = technician.user_id ( + )。 它包括一个 SQL 的计算属性 daysopen 基于 SQL 的表达 ceil ( sysdate -t runc( r equest_date)) 。

7-23 图,鉴于对象封装 SQL 查询和实体属性映射元数据

7.7.4 视图对象执行查询 时会发生什么事 后添加的一个实例openproblemsandassignees具有相同名称的向srservice ' s的数据模型, 你可以看看会发生什么情况在运行时当您执行查询。就像一个只读View对象,一实体为基础的View 对象发送SQL查询直到数据库的使用标准的Java数据库连接( JDBC的)API,和数据库产生一个结 果集。相反,然而,由于实体为基础的View对象检索的每一行的数据库结果集,它的分割该行属性 的基于实体的使用关联。分割发生,创造一个实体对象行根据对象的实体惯例的适当类型的每个视 图,填充他们与有关的属性检索查询,储存每个这些实体列在其各自的实体缓存。然后,而非储存 副本的数据,鉴于行简单点,收于实体行部分组成。图 7-24 ,该行强调,在结果集是分割成一个 用户实体行与主键 306 和 1 servicerequest实体行与主键 112 。由于SQL的计算daysopen属性是无 关的任何实体对象,其值是直接存储在行的视图。 该 servicerequest 实体行被带入高速缓存使用上述 findbyprimarykey ( )所载的所有属性 的 servicerequest 实 体 对 象 。 在 相 比 之 下 , servicerequest 实 体 行 所 创 造 的 分 割 行 从 openproblemsandassignees 查询结果包含的值只有属性中出现的查询。它并不包括集合完整的属 性。这部分实体行是一个重要的运行性能的优化。

---------------------------------------------------------------266----------------------------------------------------------------------------


由于比例行,以行改性在一个典型的企业应用是非常高的,使其只有属性到内存中去,您需要 展示能够代表一个大的内存储蓄超过使所有属性到内存中。 最后,需要注意,行服务的请求, 114 是没有指派技术的,因此,在视图行它有一个空的实体 行的一部分,其用户实体对象。 7-24 图,鉴于行分割成实体列在实体缓存

数据纳入其后台的实体行的组成部分,首先有利于您获得的是所有行,包括一些数据查询,用 户与用户名 = 306 将显示一个一致的结果时,改变了在目前的 transaction 。换句话说,如果一 个 View 对象,让电子邮件的属性用户 306 要修改的话,所有的行,在任何实体为基础的 View 对象, 显示电子邮件的属性为 306 的用户将即时更新,以映射变化。由于相关的数据用户 306 存储正是一 次是在用户实体缓存在实体行与主键 306 ,从任何角度来看行已查询,用户的电子邮件的属性,只 是指出在这个单一的实体行。 幸运的是,这些执行细节,是完全隐藏从客户端的工作与列在一个 View 对象的行订定。正如 你所做的业务组件的浏览器,客户端工程,获取和设置属性,是不知道如何后台实现这些属性可能 与实体行。 7.7.5 当您修改视图行属性会发生什么事 您在上面看到的,其中的其他行, openproblemsandassignees结果集,包括一排相关的服务 请求 112 。当客户端尝试更新的状态,服务的请求,以 112 的值封闭 ,最终setstatus( “封闭” ) 方法取得的所谓对的视图排。 7-25 图说明步骤,将出现自动协调这种视图行属性修改与基本实体 行: 1. 客户端试图设定状态的属性的值封闭 ---------------------------------------------------------------267----------------------------------------------------------------------------


2. 由于状态,是一个整体映射的属性从 servicerequest 的实体使用,鉴于行代表的属性设置 为适当的基本实体,在行 servicerequest 实体缓存有主键 112 。 3. 任何属性级别的验证规则的状态属性在 servicerequest 实体对象得到评估,并会失败的运 作,如果他们没有成功。 假设一些验证规则的状态属性编程参考 requestdate 属性(例如,执行业务规则,一个 servicerequest 不能封闭,这是开放的) 。该 requestdate 不是其中的 servicerequest 属性检索查询,因此在 servicerequest 实体缓存部分行实体它是不在的,。 4. 以确保业务规则,随时可以参考,所有的属性的实体对象,实体对象检测这种情况和“断 层”的整套 servicerequest 实体对象属性为实体行被修改的使用主键(即必须存在为每个 实体的使用添加了视图对象。 5. 后的属性级别的所有验证成功,实体对象,企图获取的锁就行,在 service_requests 表, 才可让第一属性来修改。

6. 如果该行可以被锁定,设定状态的属性,在行成功且值在实体行改变了。 ======================================================================== 注 意:jbo.locking.mode 配置信息管制如何行锁定。默认值是悲观的。在悲观的锁定模式下,该行必 须上锁之前的任何改变,是允许它在实体缓存。通常情况下, Web 应用程序将设置此属性为乐观 , 反而使行不上锁,直至 transaction commit 的时间。 图 7-25 更新行属性代表实体

---------------------------------------------------------------268----------------------------------------------------------------------------


7.7.6 当你改变一个外键属性会发生什么事 如果用户还更新了 technician assigned 服务请求 112 ,然后其他有趣的事情发生。请求,是 目前分配给 vpatabal ,谁拥有用户 ID 306 。假设最终用户设置 assignedto 属性 300 重新请求 sking 。如图 7-26 ,在后台,发生下列情况: 1. 客户端的企图设置 assignedto 属性的值 300 。 2. 自 assignedto 是一个整体映射的属性从 servicerequest 实体使用,鉴于行代表的属性设 置为适当的基本实体,在行 servicerequest 实体缓存有主键 112 。 3. 任何属性级别的验证规则对 assignedto 属性在 servicerequest 实体对象得到评估,并会 失败的运作,如果他们没有成功。 4. 该行已经锁定,因此,企图设置 assignedto 属性,在行成功和值是改变了在实体行。 5. 自 assignedto 属性就 servicerequest 实体的使用是相关的参考实体使用名为 Technician 到用户的实体对象,这个改变外键值的原因的视图行,以取代其目前的实体行的一部分, 为用户 306 与实体行相应的新的用户名= 300 。这有效地使鉴于行的服务请求 112 点,实 体为行 sking ,所以电子邮件的值中的视图行更新,以映射正确的参考信息,这为新分配 的 technician。 图 7-26 外键更新后,视图行变成一个新的实体

7.7.7 当您重新查询资料 发生什么事 当您重新执行期对象的查询,默认情况下的视图,排在其当前行集的“被遗忘”在准备读在一 个新的结果集。这一观点的运作对象,并不直接影响到实体的缓存,不过。视图对象发送的 SQL 到 数据库和进程开始再次检索数据库结果集行和分割成实体的行部分。

---------------------------------------------------------------269----------------------------------------------------------------------------


注意:通常当您重新查询,你这样做,才能看到最新的数据库信息。如果不是您想避免一个数据库 往返的限制,你的视图,反对查询,只是现有的实体列在缓存中,或超过现有行已经在视图对象的 行集, 第 5.27, “表演在记忆的排序和筛选行集“说明如何做到这一点。 ======================================================================== 7.7.7.1 未修改的属性在实体缓存刷新期间重新查询 作为这项工作的一部分实体行分割过程,重新查询,如果一个属性对实体行是未经修改的,那 么它的值在实体缓存更新,以映射新的值提出查询。 7.7.7.2 修改属性在实体缓存是原封不动,在重新查询期间 如果该值的一个实体的行属性已被修改 ,在目前的 transaction ,然后在重新查询实体的行 分割的过程中不刷新其值。在目前的 transaction 是原封不动,以便最终用户的逻辑单元的工作是 保存。作为与任何实体的属性值,这些修改之前,继续以一贯显示在任何实体为基础的 View 对象 的行参考改良实体行。 7-27 图说明了这种情况。可想而知,在上下文目前 transaction 等候变化,用户使用 servicerequests View 对象的“drills down”到不同的网页,,例如撷取所有的细节的服务请求 112 。这一观点的对象有 4 个实体的用法: 1 ,主 servicerequest 使用, 3 产品的参考用途, 用 户 (Technician) ,和用户 (客户) 。当其查询结果是分割成实体行,它结束了指向在同一 servicerequest 实体的行前 openproblemsandassignees 视图行了修改。这意味着最终用户将正确 地看到,有待改变,即服务的请求,在此 transaction 是分配给 Technician 。 图 7-27 嵌套实体的属性,从不同的视图,对象是合并后的实体在缓存

---------------------------------------------------------------270----------------------------------------------------------------------------


7.7.7.3 嵌套的子属性是合并当重新查询 7-27 图,也说明了情况,该 servicerequests View 对象的查询检索不同于 openproblemsandassignees 子参考使用者的信息。该 servicerequests 疑问最多的用户的 FirstName 和 LastName,当 openproblemsandassignees View 对象查询用户的电子邮件 。图显示, 在运行时在这种情况下发生了什么事。而如果分割文字行,实体行的一部分,包含了一组不同的属 性,超过部分数量的实体行已经在缓存中,属性得到“合并” 。结果是,部分数量实体行在缓存 中与集合重叠的子用户属性。相反的,John Chen( 308 用户)在缓存中已经存在,由此产生的新 的实体行只包含名字和姓的属性,而不是电子邮件 。 7.7.8 你 committransaction 时会发生什么 假设用户是很乐意变化的,并 commit 进行 transaction 。 图 7-28 ,有两个基本步骤: 1. 对象验证的任何无效的实体列在其之前,更改集合。 2. 实体行,在等候集合上的变化,保存到数据库中。 图描绘了一个循环在第 1 步之前,该法的验证修改实体对象的编程方式可能的变化,影响到其 他实体的对象。一旦 transaction 处理其集合无效的实体就更改集合,如果集合上仍然是非空,将 完成另外通过的集合无效。 It will attempt up to ten passes through the list.它会尝试多 达 10 通过的集合。如果由这一点,仍然存在着无效的实体行,它将抛出一个 exception,因为这通 常意味着你有一个错误在您的业务逻辑,需要进行调查。 图 7-28 Committing 的 transaction 确认无效的实体,然后保存他们

---------------------------------------------------------------271----------------------------------------------------------------------------


7.7.9 交互多用户测试情况 最后方面了解有关如何视图对象和实体对象的合作涉及两个例外情况可以发生,当工作在多用 户环境中时。幸运的是,这些都是容易模拟测试的目的只是起了业务组件的浏览器的两次对 srservice 应用模块(当然,第一个实例不存在) 。请尝试以下两项测试,看看如何让这些多用户 可以出现 exceptions 情况: •

在一个业务组件的浏览器测试修改的状态,现有的服务请求和标签在域之外。在其他的业 务组件的浏览器窗口,尝试修改相同的服务,请求以某种方式。您会看到第二个用户得到 oracle.jbo.alreadylockedexception 尝试重复测试,但经过覆盖值 jbo.locking.mode , 以乐观对性能页的业务组件的浏览器连接对话框。您会看到错误发生在 commit 的第二个用 户而不是立即。 在一个业务组件的浏览器测试修改的状态,现有的服务请求和标签在域之外。然后,在其 他的业务组件的浏览器窗口,检索(但不修改)同等的状态的请求。早在第一窗口,commit 的改变。如果第二个用户,然后尝试修改相同的服务请求,您就可以看到第二个用户得到 oracle.jbo.rowinconsistentexception 。该行已被修改,并 commit 由另一位使用者,第 二个用户检索行到实体的缓存

7.8

工作编程与实体为基础的视图对象

从客户端访问您的应用模块的数据模型的角度来看,API 的工作与只读 View 对象和一个实体基 于对象的视图是一致的。键功能不同的是,实体为基础的视图对象,让数据在一个 View 对象得到 充分的更新。应用模块包含实体为基础的视图对象界定单位的工作和管理 transaction 。本节介绍 四个简单的测试客户端程序的工作与 srservice 应用模块来说明: • • • •

迭代主/详细/详细的等级 找到一个行和更新外键的值 创建一个新的服务请求 检索该行确定的键行

7.8.1 遍历主/详细/详细的等级的例子 例如 7-1 执行下列基本步骤: 1. 2. 3. 4. 5.

找到 stafflist 视图对象实例 执行查询 迭代所产生的 stafflist 行 打印工作人员的全名,由获得的值计算 fullname 属性 取得相关的行集合 servicerequests 使用视图连结存取属性

6. 迭代 servicerequests 行

---------------------------------------------------------------272----------------------------------------------------------------------------


7. 打印出来的一些服务请求的属性值 8. If the status is not Closed , then get the related row set of ServiceHistories using a view link accessor attribute 如果状态是没有关闭,然后使用视图连结存取属性,得 到 servicehistories 相关的的行结果集。 9. 迭代 servicehistories 行

10.打印出来的一些服务的请求的历史属性 ======================================================================== 注 意: 其他的有一个额外的级别,这个例子使用相同的 API 的,在 testclient2 工程,这是迭代主/ 详细资料,只读视图对象在第 5.10.4.2 , “如何获取详细的收集使用的视图连结存取“ 。 例如 7-1 迭代主/详细/详细的等级 package devguide.client; import oracle.jbo.ApplicationModule; import oracle.jbo.Row; import oracle.jbo.RowSet; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; public class TestClient { public static void main(String[] args) { String amDef = "devguide.model.SRService"; String config = "SRServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); // 1. Find the StaffList view object instance. ViewObject staffList = am.findViewObject("StaffList"); // 2. Execute the query staffList.executeQuery(); // 3. Iterate over the resulting rows while (staffList.hasNext()) { Row staffMember = staffList.next(); // 4. Print the staff member's full name System.out.println("Staff Member: "+staffMember.getAttribute("FullName")); // 5. Get related rowset of ServiceRequests using view link accessor RowSet reqs = (RowSet)staffMember.getAttribute("ServiceRequests"); // 6. Iterate over the ServiceRequests rows while (reqs.hasNext()) { Row svcreq = reqs.next(); // 7. Print out some service request attribute values System.out.println(" ["+svcreq.getAttribute("Status")+"] "+ ---------------------------------------------------------------273----------------------------------------------------------------------------

svcreq.getAttribute("SvrId")+": "+


svcreq.getAttribute("ProblemDescription")); if(!svcreq.getAttribute("Status").equals("Closed")) { // 8. Get related rowset of ServiceHistories RowSet hists = (RowSet)svcreq.getAttribute("ServiceHistories"); // 9. Iterate over the ServiceHistories rows while (hists.hasNext()) { Row hist = hists.next(); // 10. Print out some service request history attributes System.out.println(" "+hist.getAttribute("LineNo")+": "+ hist.getAttribute("Notes")); } } } } Configuration.releaseRootApplicationModule(am,true); } } 运行程序产生下列输出: Staff Member: David Austin [Open] 104: Spin cycle not draining 1: Researching issue Staff Member: Bruce Ernst [Closed] 101: Agitator does not work [Pending] 102: Washing Machine does not turn on 1: Called customer to make sure washer was plugged in...工 作人员:大卫柯士甸[公开] 104 :自旋周期不排水 1 :研究的问题,工作人员:布鲁斯恩斯特[关 闭] 101 :搅拌器不工作[待定] 102 :洗衣机不转 1 :所谓的客户,以确保洗衣机是堵塞英寸.. 2: We should modify the setup instructions to include... 2 :我们应该修改安装的指示,包括… … [Open] 108: Freezer full of frost 1: Researching issue Staff Member: Alexander Hunold [Closed] 100: I have noticed that every time I do a... [公开] 108 :冷冻充分霜 1 :研究 的问题,工作人员:亚历山大胡诺尔德[关闭] 100 :我注意到,每一次我做甲.. [Closed] 105: Air in dryer not hot : [关闭] 105 :空气中干燥不热: 7.8.2 找到一个行和更新外键的值的例子 例如 7-2 执行下列基本步骤: 1. 2. 3. 4. 5.

找到 servicerequests 视图对象实例 建构一个键的对象寻找服务请求的数目为 101 的行 找到该行 打印的一些服务请求的属性值 尝试非法转让值, 重开后的状态属性 由于 View 对象行的实体对象,验证的规则的状态属性抛出一个 exception,防止这种非法 的改变。

6. 集的状态 ,以开放的合法的值。


7. 打印状态属性的值,以显示它已成功更新 8. 打印的当前值指派 technician 的电子邮件 9. 重新指派服务的请求,technician 人数 303 (Alexander Hunold)通过设置 assignedto 属性 10. 显示值的参考信息( technicianemail )映射了一个新的 technician 11. 取消 transaction 发表了一份回滚 ---------------------------------------------------------------274----------------------------------------------------------------------------

例如 7-2 寻找和更新外键的值 package devguide.client; import oracle.jbo.ApplicationModule; import oracle.jbo.JboException; import oracle.jbo.Key; import oracle.jbo.Row; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; public class TestFindAndUpdate { public static void main(String[] args) { String amDef = "devguide.model.SRService"; String config = "SRServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); // 1. Find the ServiceRequests view object instance ViewObject vo = am.findViewObject("ServiceRequests"); // 2. Construct a new Key to find ServiceRequest# 101 Key svcReqKey = new Key(new Object[]{101}); // 3. Find the row matching this key Row[] reqsFound = vo.findByKey(svcReqKey,1); if (reqsFound != null && reqsFound.length > 0) { Row svcReq = reqsFound[0]; // 4. Print some service request information String curStatus = (String)svcReq.getAttribute("Status"); System.out.println("Current status is: "+curStatus); try { // 5. Try setting the status to an illegal value svcReq.setAttribute("Status","Reopened"); } catch (JboException ex) { System.out.println("ERROR: "+ex.getMessage()); } // 6. Set the status to a legal value svcReq.setAttribute("Status","Open"); // 7. Show the value of the status was updated successfully


System.out.println("Current status is: "+svcReq.getAttribute("Status")); // 8. Show the current value of the assigned technician System.out.println("Assigned: "+svcReq.getAttribute("TechnicianEmail")); // 9. Reassign the service request to technician # 303 svcReq.setAttribute("AssignedTo",303); // Alexander Hunold (technician) // 10. Show the value of the reference info reflects new technician System.out.println("Assigned: "+svcReq.getAttribute("TechnicianEmail")); // 11. Rollback the transaction am.getTransaction().rollback(); System.out.println("Transaction cancelled"); } Configuration.releaseRootApplicationModule(am,true); } } 运行这个例子产生下列输出: Current status is: Closed ERROR: The status must be Open, Pending, or Closed Current status is: Open Assigned: bernst Assigned: ahunold Transaction cancelled ---------------------------------------------------------------275----------------------------------------------------------------------------

7.8.3 创建一个新的服务请求的例子 例如 7-3 将执行以下基本步骤: 1. 2. 3. 4. 5. 6.

找到 servicerequests 视图对象实例 创建一个新行并插入到该行建立 显示实体对象相关的默认状态属性 在新行,集和值的一些必要属性 commit transaction 检索和显示触发指定的服务请求 ID

例如 7-3 创造一项新的服务请求 package devguide.client; import java.sql.Timestamp; import oracle.jbo.ApplicationModule; import oracle.jbo.Row; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; import oracle.jbo.domain.DBSequence;


import oracle.jbo.domain.Date; public class TestCreatingServiceRequest { public static void main(String[] args) throws Throwable { String amDef = "devguide.model.SRService"; String config = "SRServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef, config); // 1. Find the ServiceRequests view object instance. ViewObject svcReqs = am.findViewObject("ServiceRequests"); // 2. Create a new row and insert it into the row set Row newSvcReq = svcReqs.createRow(); svcReqs.insertRow(newSvcReq); // 3. Show effect of entity object defaulting for Status attribute System.out.println("Status defaults to: "+newSvcReq.getAttribute("Status")); // 4. Set values for some of the required attributes newSvcReq.setAttribute("CreatedBy",308); // Nancy Greenberg (user) Date now = new Date(new Timestamp(System.currentTimeMillis())); newSvcReq.setAttribute("RequestDate",now); newSvcReq.setAttribute("ProdId",119); // Ice Maker newSvcReq.setAttribute("ProblemDescription","Cubes melt immediately"); // 5. Commit the transaction am.getTransaction().commit(); // 6. Retrieve and display the trigger-assigned service request id DBSequence id = (DBSequence)newSvcReq.getAttribute("SvrId"); System.out.println("Thanks, reference number is "+id.getSequenceNumber()); Configuration.releaseRootApplicationModule(am, true); } } 运行这个例子产生下列结果: Status defaults to: Open Thanks, reference number is 200 ---------------------------------------------------------------276----------------------------------------------------------------------------

7.8.4 检索该行确定的键的例子 例如 7-4 执行下列基本步骤: 1. 2. 3. 4. 5. 6.

找到 servicerequests View 对象 建构一个键找到服务请求的数目 101 找到 servicerequests 行的这个键 显示键的 servicerequests 行 访问行集合 servicehistories 使用视图连结存取属性 遍历该 servicehistories 行


7. 显示每个 servicehistories 行的键, 例如 7-4 检索该行确定的键 package devguide.client; import oracle.jbo.ApplicationModule; import oracle.jbo.Key; import oracle.jbo.Row; import oracle.jbo.RowSet; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; public class TestFindAndShowKeys { public static void main(String[] args) { String amDef = "devguide.model.SRService"; String config = "SRServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef, config); // 1. Find the ServiceRequests view object ViewObject vo = am.findViewObject("ServiceRequests"); // 2. Construct a key to find service request number 101 Key svcReqKey = new Key(new Object[] { 101 }); // 3. Find the ServiceRequests row with this key Row[] reqsFound = vo.findByKey(svcReqKey, 1); if (reqsFound != null && reqsFound.length > 0) { Row svcReq = reqsFound[0]; // 4. Display the key of the ServiceRequests row showKeyFor(svcReq); // 5. Access row set of ServiceHistories using view link accessor RowSet histories = (RowSet)svcReq.getAttribute("ServiceHistories"); // 6. Iterate over the ServiceHistories row while (histories.hasNext()) { Row historyRow = histories.next(); // 7. Display the key of the current ServiceHistories row showKeyFor(historyRow); } } Configuration.releaseRootApplicationModule(am, true); } private static void showKeyFor(Row r) { // get the key for the row passed in Key k = r.getKey(); // format the key as "(val1,val2)" String keyAttrs=formatKeyAttributeNamesAndValues(k); // get the serialized string format of the key, too String keyStringFmt = r.getKey().toStringFormat(false);


System.out.println("Key "+keyAttrs+" has string format "+keyStringFmt); } // Build up "(val1,val2)" string for key attributes private static String formatKeyAttributeNamesAndValues(Key k) { StringBuffer sb = new StringBuffer("("); int attrsInKey = k.getAttributeCount(); for (int i = 0; i < attrsInKey;i++) { if (i > 0 ) sb.append(","); sb.append(k.getAttributeValues()[i]); } sb.append(")"); return sb.toString(); } } ---------------------------------------------------------------277----------------------------------------------------------------------------

运行产生了以下结果。通知指出,序列化字符串格式的一个键是一个十六进制数,其中包括信 息在一个单一的字串,代表的所有属性在一个键。 Key (101) has string format 000100000003313031 Key (101,1) has string format 000200000003C2020200000002C102 Key (101,2) has string format 000200000003C2020200000002C103 7.9 区别实体为基础的视图对象和只读视图对象的摘要 你现在知道,视图对象可以是相关的基本实体的对象或不是。本节有助于总结之间的差额,运 行时的行为这两个基本种视图对象。 7.9.1 以独特的实体为基础的视图对象的运行特点 当一个 View 对象有一个或多个基本实体的惯例,您可以创建新行,并修改或删除查询行。该 实体为基础的 View 对象后台的实体对象,以执行业务规则,并永久保存更改。此外,您看过该实 体为基础的视图对象: • • •

通过其他视图对象在同一 transaction,立即映射之前所做的更改的相关实体对象属性, 从后台的实体对象属性,初始化属性值在新创建的行的值, 映射最新的参考信息,当外键属性值的改变

7.9.2 视图对象没有实体的使用时是只读的 视图对象没有实体的使用时是只读的,不要拿起实体衍生预设值,不映射等候的变化,不映射 最新的参考信息。您需要决定什么样的功能,您的应用需要和设计的视图。通常视图对象用于基于 SQL 的验证目的,并且在一个下拉列表展示集合有效的选择,并可以为只读。有少量的运行时的开 销与之间的协调 View 对象行和实体对象行,因此,如果你不需要任何的功能提供一个实体映射的 View 对象,您可以稍微提高性能用只读视图对象,没有相关的实体对象。


---------------------------------------------------------------278----------------------------------------------------------------------------

7.9.3 只读视图对象的密钥管理,您需要知道什么 一个实体为基础的 View 对象代表的任务是寻找列的键,其后台的实体行部分。当您使用 findbykey ( )方法来找到一个行的视图是键,鉴于行轮流左右,并使用实体定义的 findbyprimarykey ( )来找出每个实体的行属性的视图行的键。 这项计划是不可能的只读 View 对象,因为它没有基本的实体列。因为你可能会利用只读视图 对象快速迭代的查询结果,而不需要任何额外提供的功能实体为基础的 View 对象,只读 View 对象 不承担您要招致轻微的额外开销。 为了成功使用 findbykey ( )方法在一个只读 View 对象,你需要执行两个额外的步骤: 1. 确保至少一个属性,在视图对象的键属性的属性集 2. 让一个自定义的 Java 类为视图对象,并覆盖其 create( )方法来调用 setmanagerowsbykey (true)后,请求 super.create ( ): // In custom Java class for read-only view object public void create() { super.create(); setManageRowsByKey(true); }

========================================================================注 意: 在应用程序中使用 ADF 的模型层的数据绑定到一个只读 View 对象,成功运作的 ADF 界面表或其他管制措施,允许最终用户设置当前行按一下在网页-这需要额外的一步。这也适用于成功使 用内置的具有约束力的层的操作一样, setcurrentrowwithkey 或 setcurrentrowwithkeyvalue 在 一个只读 View 对象。所有这些归结为调用 findbykey ( )。 ======================================================================== 第 25.3.2 , “实施一般功能的使用运行时元数据”描述了一个通用的技术,您可以使用,以 避免不必记住这样做对您所有的只读视图对象对您想要 findbykey ( )如预期般运作。

---------------------------------------------------------------279----------------------------------------------------------------------------


---------------------------------------------------------------280---------------------------------------------------------------------------


这一章介绍了如何贯彻落实的整体功能,您的企业服务结合使用的应用模块,视图对象,和实 体对象。 这一章包含以下部分:

• • • • • • • • •

第 8.1 节 第 8.2 节 第 8.3 节 第 8.4 节 第 8.5 节 第 8.6 节 第 8.7 节 第 8.8 节 第 8.9 节

“介绍应用模块” “创造一个应用模块” “添加自定义服务法” “出版客户服务的方法给客户” “工作编程与应用模块的客户端接口” “有覆盖性的内置框架方法” “创造一个应用模块框图为您的企业服务” “支持多页的单位工作” “粒度的应用模块”

8.1 介绍应用模块 应用模块是甲骨文公司 ADF 的业务构件组成封装业务服务的方法和活动的的数据模型为一逻辑 单元有关的工作的最终用户的任务。 到这一章,你就会明白所有的概念,说明在图 8-1 中: • • • •

您使用的实例来看,对象在一应用模块,以界定其活动的的数据模型。 你写的服务方法,概括任务级的业务逻辑。 你为客户通话对服务接口选定的方法。 您使用的应用模块,在一个合乎逻辑的 transaction 可以跨多个网页。

---------------------------------------------------------------281----------------------------------------------------------------------------


• •

您的应用模块工程与 transaction 对象取得数据库连接和坐标存储或回滚所做的更改实体 对象。 对象提供了对当前用户的应用的运行时的资料。

应用模块是一个商业服务组件的封装,一个单位的工作

前几章解释的细节,包括 View 对象实例中,一应用模块的数据模型和如何客户端存取视图对 象合作,内部与实体对象在您的可重复使用的业务域层。这一章介绍了如何结合起来,商业服务的 方法与数据模型实施一个完整的商业服务。 ======================================================================== 注意: 在这一章中使用相同的基本 srservice 应用模块,从第 7 章, “建设一个更新的数据模型 与实体为基础的视图对象” ,包括实体为基础的视图对象中显示的图 8-1 。实验的一个工作版本, 下 载 devguideexamples 工 作 空 间 , 例 如 从 下 载 页 面 在 http://otn.oracle.com/documentation/jdev/b25947_01 看到 applicationmodules 项目。 8.2 建立一个应用模块 在一个大的应用,您通常创建一个应用模块相互支持粗粒最终用户的任务。在一个较小规模的 应用,如 srdemo 应用,您可能会决定建立一个单一的应用模块是足够的处理需要的集合完整的应 用功能。 节 9.8, “决定对粒度的应用模块”提供额外的指导意见这个课题。

---------------------------------------------------------------282----------------------------------------------------------------------------


8.2.1 建立一个应用模块 创建一个应用模块: 1. 打开创造应用模块向导 ,该向导可从 Business Tier > ADF Business Components。 2. 在第 1 步窗格中,提供一个软件包的名称和应用模块的名称。 3. 在第 2 步中,包括实例 View 对象您先前的界定和调整 View 对象,例如姓名,正是您想要 的客户看到的。然后单击完成 。 为更一步一步详细资讯,请参阅第 5.3 节, “使用期的对象在一应用模块的数据模型” 。 8.2.2 当您创建了一个应用模块会发生什么事 当您创建一个应用程序模块,JDeveloper 创建的 XML 组件定义文件,代表其声明的设置,并将 它存储在目录中对应的名称,它的包。举例来说,由于一应用模块命名 srservice ,在 devguide.model 包, XML 文件创建将。 / devguide / model / srservice.xml 下,该项目的源路 径。此 XML 文件包含所需要的信息在运行时重新创建 View 对象实例中,应用模块的数据模型。如 果您感到好奇,看看它的内容,您可以看到 XML 文件,在相应的源文件夹在结构窗口,为应用模块 通过选择 View 对象在应用 Navigator 和查找。连按两下该 srservice.xml 节点将打开 XML 在编辑 器,使您可以检查它。 ======================================================================== 注意:如果您的 IDE 级别的业务组件的 Java 一代偏好设定,以便表明,该向导还可以创建一个可 选的定制应用软件模块级 srserviceimpl.java 。 8.2.3 编辑现有的应用模块 当您创建了一个新的应用模块,您可以编辑任何其设置使用的应用模块编辑器。选择编辑菜单 上的选项,右边的鼠标上下文菜单在应用 Navigator 中,或双击该应用模块,发射编辑器。通过访 问不同的面板的编辑器,您可以调整的数据模型, Java 的代设置,远程部署选项,客户端接口的 方法,和自定义属性。 8.2.3 编辑现有的应用模块 当您创建了一个新的应用模块,您可以编辑任何其设置使用的应用模块编辑器。选择编辑菜单 上的选项,右边的鼠标上下文菜单在应用 Navigator 中,或双击该应用模块,发射编辑器。通过访 问不同的面板的编辑器,您可以调整的数据模型, Java 的代设置,远程部署选项,客户端接口的 方法,和自定义属性。

---------------------------------------------------------------283----------------------------------------------------------------------------


8.2.4.1 使用 JDBC 的网址连接类型 默认 youappmodule 本地配置使用 JDBC 的网址连接。它是基于命名定义的,定义于业务组件页 的该项目的属性对话框,该项目包含您的应用模块。 8.2 图表明,本节看起来就像在一个配置使用 JDBC 的网址连接, 连接名称 srdemo。当您使用 JDBC 的网址连接类型为 srservicelocaltesting 配置该 srdemo 应用,模块在任何上下文的 Java 可以运行。在其他换言之,这是不局限于运行的 J2EE 应用服务器在此连接类型下。 8-2 图在配置编辑器连接类型设置

======================================================================== 注意:见第 29.5.2 , “数据库连接池”和第 29.7 , “数据库连接池的参数”的更多信息,就 如何数据库连接池的使用和如何您还可以调整。 ======================================================================== 8.2.4.2 使用 JDBC DataSource 的连接类型 其他类型的连接,您可以使用是一个 JDBC datasource。您定义的 JDBC DataSource 的一部分, 您的应用程序服务器的配置信息,然后应用模块中通过在运行时使用的逻辑名称查找资源。您定义 的 DataSource 方面的细节由两部分组成: • •

一个逻辑的一部分,使用标准 J2EE 部署描述符定义的 DataSource 名称的应用将在运行时 使用. 无力的一部分,这是应用服务器的具体映射的逻辑 DataSource 的名称,物理方面的细节。

举例来说, 例如 8-1 显示了资源编号标记,在 web.xml 文件的 srdemo 的应用。这些定义两个 逻辑数据源命名的 JDBC / srdemods 和 JDBC / srdemocoreds 。当您使用的 JDBC DataSource 连接 为您的应用模块,您参考合乎逻辑的连接名称,前缀为 java:comp/env。因此,如果您检查 srservicelocal 配置为 srservice 应用模块,在 srdemo 应用,您就可以看到值,其 JDBC DataSource 的名称栏位是 java:comp/env/jdbc/SRDemoDS。

---------------------------------------------------------------284----------------------------------------------------------------------------

例如 8-1 逻辑的 DataSource 资源的名称定义的 web.xml


<!-- In web.xml --> <resource-ref> <res-ref-name>jdbc/SRDemoDS</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> <resource-ref> <res-ref-name>jdbc/SRDemoCoreDS</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> 一旦您定义的逻辑 DataSource 的名称在 web.xml 和参照他们在一个配置,然后,您需要包括 额外的服务器的具体配置文件以映射逻辑 DataSource 的名称,以物理连接的定义在目标应用服务 器上。 例如 8 -2 显示的内容,该 srdemo 应用程序的 data-sources.xml 文件。此文件是具体到 Oracle 容器的 J2EE ( oc4j ) ,并确定了物理细节的 DataSource 连接池和连接的名称。您需要 提供一个类似的文件不同的 J2EE 应用服务器,文件将有一个供应商的具体格式。 例如 8-2DataSource 方面的细节定义外部的应用 <data-sources ... > <connection-pool name="jdev-connection-pool-SRDemo"> <connection-factory factory-class="oracle.jdbc.pool.OracleDataSource" password="->DataBase_User_8PQ34e6j3MDg3UcQD-BZktUAK-QpepGp" user="srdemo" url="jdbc:oracle:thin:@localhost:1521:XE"/> </connection-pool> <managed-data-source name="jdev-connection-managed-SRDemo" jndi-name="jdbc/SRDemoDS" connection-pool-name="jdev-connection-pool-SRDemo"/> <native-data-source name="jdev-connection-native-SRDemo" jndi-name="jdbc/SRDemoCoreDS" ... /> </data-sources> 最后一步,在这个过程中涉及测绘物理连接的细节,以合乎逻辑的资源参考的 DataSource 。 在 oc4j 服务器,您完成这一步骤使用 orion-web.xml 文件中显示的例子 8-3 。 例如 8-3 服务器特定文件的逻辑图的 DataSource 体的 DataSource <orion-web-app ... > <resource-ref-mapping location="jdbc/SRDemoDS" name="jdbc/SRDemoDS"/> <resource-ref-mapping location="jdbc/SRDemoCoreDS" name="jdbc/SRDemoCoreDS"/> </orion-web-app>


一旦这些 DataSource 的配置细节是在本地,您可以成功地使用您的应用模块在一个 J2EE 应用 服务器的一个组成部分 Web 应用程序。 ---------------------------------------------------------------285----------------------------------------------------------------------------

8.2.5 管理您的应用模块的运行时配置 此外,创造良好的应用模块的 XML 组件定义,JDeveloper 也增加了一个默认配置命名 srservicelocal 向 bc4j.xcfg 文件中的子目录命名为共同的相对目录载有 srservice.xml 文件。管 理您的应用模块的配置,选择它在 Application Navigator 和选择 Configurations...从右边鼠标 上下文菜单。 8.2.6 您需要了解应用模块的连接中的什么 当测试您的应用模块与业务构件的浏览器,你应该知道的连接配置。 8.2.6.1 业务组件的浏览器需要一个 JDBC 的网址连接 在以前的章节,您已经学会了商业组件的浏览器工具可以测试您的应用程序模块的数据模型交 互。当它运行在 J2EE 应用服务器上下文之外,它不能测试应用模块使用的配置依赖于 JDBC DataSource 的。解决的办法是,以测试应用模块选择配置使用 JDBC 的网址连接。你做到这一点, 选择连接对话框的业务组件的浏览器从业务构件的配置名称下拉列表中。 8.2.6.2 在业务组件的浏览器测试 srservice 应用模块, 测试 srdemo 应用程序的 srservice 应用模块使用的业务组件的浏览器,选择 srservicelocaltesting 配置。顺便一提, 与 junit 配置相同,在 unittests 工程中的工作。测试 也运行了一个 J2EE 应用服务器上下文之外,所以基于同样的理由,作为业务组件的浏览器无法使 用配置与 JDBC DataSource 的连接类型。 8.3 添加自定义服务方法 一个应用模块,可以揭露其数据模型视图对象的客户,而不需要任何自定义 Java 代码。这使 得客户端代码使用 ApplicationModule, ViewObject, RowSet ,和 Row 接口在 oracle.jbo 包中, 直接与从任何角度来看对象的数据模型。然而,只是因为你可以以编程方式操纵视图对象以任何方 式您想要在客户端代码,并不意味着这样做往往是最佳的做法。 每当纲领性代码操纵视图对象是一个逻辑方面的执行您的完整的商业服务功能,您应该概括了 详细写,在您的应用程序模块的 Java 类自定义方法。这包括代码: • • •

配置视图对象属性的查询正确的数据显示 遍历超过 View 对象的行返回累计计算 执行任何种类的多程序的逻辑与一个或多个视图对象

---------------------------------------------------------------286----------------------------------------------------------------------------


通过集中这些执行细节在您的应用模块,您可以获得以下好处: • • • • •

你的意图,您的代码更清楚地向展现给客户 你允许多个客户端的页面轻松地调用相同的代码 您简化 regression-testing 您的完整的商业服务功能 您可以选择保持开放,以改善您的实施在不影响客户 您启用宣示引用合乎逻辑的业务功能,在您的网页上。

8.3.1 如何生成一个自定义类为一应用模块 添加自定义服务方法到您的应用模块,您必须先启用一个自定义的 Java 类。 If.如果您已设 定您的 IDE 级别的业务组件,自动生成一个应用程序模块级。如果您不能确定是否您的应用模块有 一个自定义的 Java 类,显示在图 8-3 ,检查上下文菜单中为去应用模块级的选择。此选项可让您 快速浏览到您的应用模块的自定义类,但如果您没有看到的选项,在菜单上,然后这意味着您的应 用模块,是目前一种以 XML 为唯一的组成部分。 8-3 图,很快转到了一个应用模块的自定义的 Java 类

开放的应用模块编辑器,请访问 Java 的网页上,选取生成的 Java 文件复选框为 1 级的应用模 块 ,然后单击确定以完成向导。

---------------------------------------------------------------287----------------------------------------------------------------------------


8.3.2 当你生成一个自定义类为一应用模块会发生什么事 一个应用模块命名 devguide.model.srservice ,默认名称为自定义的 Java 文件 srserviceimpl.java 。该文件是建立在相同的 / devguide /目录组件的 XML 组件定义文件。 Java 的 generation 选项的应用模块继续映射到 Java 的网页,随后访问的应用模块编辑器。正如与 XML 的定义文件, JDeveloper 生成的代码在您的自定义 Java 类动态变更。如果后来您决定并不需要一 个自定义的 Java 文件以任何理由,取消有关的选项,在 Java 的网页的原因自定义的 Java 文件被 删除。 8.3.3 您可能需要了解的默认代码生成的一些知识 默认情况下,应用模块的 Java 类将类似于你所看到的在 8-4 例,当您首次启用。它包含了: • •

Getter 方法为每个 View 对象,例如在数据模型 Main ( )方法让你调试应用程序模块使用的业务组件的浏览器

例如 8-4 默认的应用程序模块生成的代码 package devguide.model; import devguide.model.common.SRService; import oracle.jbo.server.ApplicationModuleImpl; import oracle.jbo.server.ViewLinkImpl; import oracle.jbo.server.ViewObjectImpl; // --------------------------------------------------------------------// --- File generated by Oracle ADF Business Components Design Time. // --- Custom code may be added to this class. // --- Warning: Do not modify method signatures of generated methods. // --------------------------------------------------------------------public class SRServiceImpl extends ApplicationModuleImpl { /** This is the default constructor (do not remove) */ public SRServiceImpl() { } /** Sample main for debugging Business Components code using the tester */ public static void main(String[] args) { launchTester("devguide.model", /* package name */ "SRServiceLocal" /* Configuration Name */); } /** Container's getter for YourViewObjectInstance1 */ public ViewObjectImpl getYourViewObjectInstance1() { return (ViewObjectImpl)findViewObject("YourViewObjectInstance1"); } // ... Additional ViewObjectImpl getters for each view object instance // ... ViewLink getters for view link instances here } ---------------------------------------------------------------288----------------------------------------------------------------------------


如图 8-4 ,您的应用模块级的继承基于 ADF 的 applicationmoduleimpl 级继承所有的默认行为之前 添加您的自定义代码。 8-4 图,您的自定义应用模块级继承 applicationmoduleimpl

8.3.4 调试应用模块使用的业务组件测试仪 正如你了解更多关于整体的营商服务的作用,应用模块在这一章中,你会发现它有用的行使你 的应用模块下的 JDeveloper 调试器,而使用的业务组件的浏览器作为测试接口。调试应用程序模 块使用的测试,选择 Application module 在 Application Navigator,然后: • •

在应用 Navigator 中,右键单击应用模块,并选择到应用模块级从上下文菜单中。 选择调试从右边鼠标上下文菜单在代码编辑器

添加自定义服务方法,只需在浏览到应用模块的自定义类和类型,在 Java 代码为一种新方法 到应用模块的 Java 实现类。使用以下指引,以决定应采取的适当的可用的方法。如果您将使用的 方法,只有这个组成部分作为一个帮助的方法的运行,该方法 private 。如果您想要让最终的子应 用模块,以便能够引用或覆盖方法,使用 protected 。如果您需要客户要能够引用它,它必须 public 。.见例子,该 srserviceimpl 方法在以往的章节。 注意:该 srservice 应用模块在这一章中是使用强类型,自定义实体对象类,你看到的说明,以 srserviceimpl2.java 为例,在第 6 章, “创造一个商业网域层使用实体对象” 。 ======================================================================== 例如 8-5, private retrieveservicerequestbyid ( )帮助方法,在 srservice 应用模块 中 srserviceimpl.java。它使用的静态 getdefinition ( )方法的 servicerequestimpl 实体对 象类的访问,其相关实体的定义,使用 createprimarykey ( )方法对实体对象类,以创造一个适 当的主要对象,研究了服务请求,然后用 findbyprimarykey ( )方法对实体的定义,找到该实体 在行的实体缓存。它返回一个实例的强类型 servicerequestimpl 类,自定义的 Java 类为 servicerequest 实体对象。

---------------------------------------------------------------289----------------------------------------------------------------------------


例如 8-5 的 Private 帮助方法在自定义应用程序模块类 // In devguide.model.SRServiceImpl class /* * Helper method to return a ServiceRequest by Id */ private ServiceRequestImpl retrieveServiceRequestById(long requestId) { EntityDefImpl svcReqDef = ServiceRequestImpl.getDefinitionObject(); Key svcReqKey = ServiceRequestImpl.createPrimaryKey(new DBSequence(requestId)); return (ServiceRequestImpl)svcReqDef.findByPrimaryKey(getDBTransaction(), svcReqKey); } 例如 8-6 显示了 public createproduct ( )方法,让调用者通过在一个名称和描述来创建。 它使用 getdefinition ( )方法的 productimpl 实体对象类的访问,其相关实体的定义,使用 createinstance2 ( )方法来创建一个新的实体 productimpl 行,其名称和描述属性填充它与参 数值通过在之前的 transaction 。 例如 8-6 Public 方法在自定义应用程序模块类 /* * Create a new Product and Return its new id */ public long createProduct(String name, String description) { EntityDefImpl productDef = ProductImpl.getDefinitionObject(); ProductImpl newProduct = (ProductImpl)productDef.createInstance2(getDBTransaction(),null); newProduct.setName(name); newProduct.setDescription(description); try { getDBTransaction().commit(); } catch (JboException ex) { getDBTransaction().rollback(); throw ex; } DBSequence newIdAssigned = newProduct.getProdId(); return newIdAssigned.getSequenceNumber().longValue(); } ---------------------------------------------------------------290----------------------------------------------------------------------------


8.4 发布自定义服务的方法给客户 当您添加一个 public 的自定义方法到您的应用模块类,如果您想要的客户能够引用它,你需 要包括方法上的应用模块的客户端接口。 8.4.1 如何发布定制服务的方法给客户 包括一个 public 方法从您的应用模块的自定义的 Java 类在用户端接口,使用客户端接口页的 应用模块编辑器。如图 8-5 ,选择一个或多个理想的方法从现有的集合并按> ,以接送他们到选定 的集合。然后单击确定以解雇编辑器。 图 8-5 添加一个 Public 方法,以一个应用模块的客户端接口

8.4.2 当您发布定制服务的方法给客户发生什么事 当您发布自定义服务方法在客户端接口,说明在图 8-6 ,创建一个的 JDeveloper Java 接口具 有相同名称作为应用模块,在您的应用模块里普通 subpackage 嵌套包里。一个应用模块命名 srservice ,在 devguide.model 包,此接口将被命名为 srservice 和该 devguide.model.common 包。接口扩展基于 applicationmodule 接口,在 oracle.jbo 包,映射客户端就可以使用所有的基 于功能的,您的应用模块继承了从 applicationmoduleimpl 类。 8-6 图,自定义服务接口,继承 applicationmodule 接口


例如 8-7 , srservice 接口包括方法的签名,您所选择的必须在客户端的接口您的应用程序 模块的所有方法。 ---------------------------------------------------------------291----------------------------------------------------------------------------

例如 8-7 自定义服务接口选择方法在客户端接口面板 package devguide.model.common; import oracle.jbo.ApplicationModule; // --------------------------------------------------------------------// --- File generated by Oracle ADF Business Components Design Time. // --------------------------------------------------------------------public interface SRService extends ApplicationModule { long createProduct(String name, String description); String findServiceRequestStatus(long requestId); String findServiceRequestTechnician(long requestId); void updateRequestStatus(long requestId, String newStatus); } 每一次你在客户端的网页接口从选定的集合添加或删除方法,相应的服务接口文件会自动更 新。客户端代理类是用来当您部署您的应用程序模块访问一个远程客户端。srservice 应用模块中, 客户端代理文件是所谓的 srserviceclient ,这是建立在 devguide.model.client 包中的 。 注意: 添加新的自定义方法后,在客户端接口,如果您新自定义方法,没有可使用的 JDeveloper 的代码,尝试使用自定义接口从客户端代码,请尝试重新编译生成的客户端接口。为此,选择应用 模块在应用 Navigator 中,在结构窗口选择源文件,并选择从上下文菜单中重建。考虑此提示为新 的自定义方法的补充,以视图对象和行的视图。 ======================================================================== 8.4.3 如何生成客户端接口,用于视图对象和视图行, 此外,生成一个客户端接口为您的应用模块,它也有可能产生强类型的客户接口,使您可以与 其他主要的客户对象工作,您可以自定义。在方式上完全相同,你如何学会做应用模块,您可以打 开客户端的接口和客户端行接口页的视图,对象编辑器来添加自定义方法的视图,对象客户端接口 和视图行客户端接口,分别为。 如果为servicerequests View对象,在devguide.model.queries包,使自定义视图对象的Java 类,并添加一个或多个自定义方法的视图,对象客户端接口,JDeveloper会产生 servicerequestsimpl类和servicerequests接口,显示在 图 8-7 。作为与应用模块的自定义接口, 请注意它产生的共同 subpackage 。 8-7 图,自定义视图对象的接口继承 viewobject 接口 ---------------------------------------------------------------292----------------------------------------------------------------------------


同样,如果为同一 View 对象,你自定义视图行的 Java 类,并添加一个或多个自定义的方法向 行客户端接口,JDeveloper 会产生 servicerequestsrowimpl 类和 servicerequestsrow 接口,如图 所示,在图 8-8 。 图 8-8 自定义视图行接口继承行接口

8.4.4 您可能需要了解在客户端接口的方法签名要知道什么, 你可以包含任何自定义的方法在客户端的接口,服从这些规则: • • •

如果该方法有一个非无效返回型,该类型必须是可串行化。 如果该方法接受任何参数,其所有类型必须序列。 jbo package.如果该方法的签名,包括 throws clause,除必须的一个实例 jboexception , 在 oracle.jbo 包。

换言之,所有种类在其方法签名必须实现 java.io.serializable 接口,以及任何检查异常必 须加以 jboexception 或其子类。您的方法可以抛出任何未经检查的异常-j ava.lang.runtimeexception 或子类-没有 disqualifing 的方法,从应用模块的客户端接口。 ========================================================================


注意:如果您的方法添加到应用模块级没有出现在可用的集合,首先检查它是否不违反任何上述规 则。如果它好像这应该是一个法律的方法出现在清单中,尝试重新编译的应用模块级之前访问的应 用模块编辑器。 ======================================================================== 8.4.5 您可能需要了解数据模型的资料, Private 实现一个应用模块的自定义方法可以轻松地指从任何角度来看对象,例如,在数据模 型的使用所产生的存取方法。通过调用 getcurrentrow ( )方法从任何角度来看对象,它可以存 取相同的当前行,从任何角度来看对象,用户端的用户作为当前行接口看到。由于这个好处,在某 些情况下,应用模块的业务服务的方法是没有必要通过在参数从客户端,在相同的应用模块的数据 模型,如果他们只会通过从目前的列的另一个视图对象实例得到值。 ---------------------------------------------------------------293----------------------------------------------------------------------------

举例来说, createservicerequest ( )方法,在 srdemo 应用程序的 srservice 应用模块接 受任何参数。它内部调用 getglobals ( ) 。 getcurrentrow ( )来访问当前行的全局视图对 象实例。然后它使用强类型的存取方法对行访问的值的 problemdescription 和 ProductID 属性设 置他们的值相应的属性在一个新创建的 servicerequest 实体对象列。 例如 8-8 使用 View 对象存取方法来访问当前行 // In SRServiceImpl.java, createServiceRequest() method GlobalsRowImpl globalsRow = (GlobalsRowImpl)getGlobals().getCurrentRow(); newReq.setProblemDescription(globalsRow.getProblemDescription()); newReq.setProdId(globalsRow.getProductId()); 8.5 工作编程与应用模块的客户端接口 发布方法对您的应用程序模块的客户端接口,您可以引用这些方法从客户端。 8.5.1 如何工作的编程与应用模块的客户端接口 工作编程与应用模块的客户端接口,做到以下几点: • •

applicationmodule 以更具体的客户端接口。 调用任何方法对接口。

========================================================================== 注意:为简单起见,本节的重点,不仅在自定义应用程序的模块接口,也有相同的 downcasting 的 做法,工程对客户端使用 viewobject 接口作为一个 View 对象的接口, servicerequests 或行接口 作为一个自定义视图行接口象 servicerequestsrow 。 ========================================================================== 8-9 例,说明了 testclientcustominterface 类提出了这两个步骤付诸实践。您可能会承认, 它从第 6.8 , “工作编程与实体对象和关联” ,而你测试一些例子,应用模块的方法从内部 Main


( )方法的 srserviceimpl 类本身。在这里,它的调用所有的同样的方法从客户端使用 srservice 客户端接口。 ---------------------------------------------------------------294----------------------------------------------------------------------------

示例的基础逻辑遵循下面的步骤: 1、 获得应用模块的实例并且设置更多的明确的SRService客户端接口。 ============================================================== 注意:如果应用模型使用了缺省的oracle.jbo包中的ApplicationModule接口,将 不能访问自定义的方法。确信设置应用模型实例到更明确的自定义接口,像这 个 例子中的SRService接口。 ============================================================== 2、 通过findRequestStatus()来查找服务请求101的状态。 3、 通过findServiceRequestTechnician()来查找程序员分配给服务请求101的名字。 4、 通过updateRequestStatus()为非法的重新开始的值更新请求101的状态。 5、 通过createProduct()用缺少的产品名字的属性值来创建产品,并且显示新的分配给它的产品 ID。 Example 8–9 Using the Custom Interface of an Application Module from the Client package devguide.client; import devguide.model.common.SRService; import oracle.jbo.JboException; import oracle.jbo.client.Configuration; public class TestClientCustomInterface { public static void main(String[] args) { String amDef = "devguide.model.SRService"; String config = "SRServiceLocal"; /* * This is the correct way to use application custom methods * from the client, by using the application module's automatically* maintained custom service interface. */ // 1. Acquire instance of application module, cast to client interface SRService service = (SRService)Configuration.createRootApplicationModule(amDef,config); // 2. Find the status of service request 101 String status = service.findServiceRequestStatus(101); System.out.println("Status of SR# 101 = " + status); // 3. Find the name of the technician assigned to service request 101 String techName = service.findServiceRequestTechnician(101); System.out.println("Technician for SR# 101 = " + techName); try { // 4. Try updating the status of service request 101 to an illegal value service.updateRequestStatus(101,"Reopened"); } catch (JboException ex) {


System.out.println("ERROR: "+ex.getMessage()); } long id = 0; try { // 5. Try creating a new product with a missing required attribute id = service.createProduct(null,"Makes Blended Fruit Drinks"); } catch (JboException ex) { System.out.println("ERROR: "+ex.getMessage()); } // 6. Try creating a new product with a missing required attribute id = service.createProduct("Smoothie Maker","Makes Blended Fruit Drinks"); System.out.println("New product created successfully with id = "+id); Configuration.releaseRootApplicationModule(service,true); } } 8.5.2 当为应用模型的客户端接口服务时会发生什么? 如果客户端层代码使用应用模型是位于J2EE体系的同一层,这一构造使用应用模型在 “本地模式”。在本地模式中,客户端接口直接被自定义的服务模型Java类来实现。典型的在本地 模式中使用应用模型情况是: JavaServer 应用,在 web 层访问应用模型 JSP/Struts 应用,在 web 层访问应用模型 Swing 应用,在客户端层(2 层 客户端/服务器结构)访问应用模型 对比来说,当客户端层代码访问应用模型是位于 J2EE 体系的不同的层时,在“远程模式” 中使用应用模型。在远程模式中,产生的客户端代理类描述以上的应用模型服务接口在客户端 层的实现,并且处理所有为远程-配置应用模型服务的通信细节。应用“远程模式”的典型情 况是瘦客户端 Swing 应用在远程应用服务端访问应用模型。 ADF 业务组件独特的特性是通过坚持最好的-实践接口-唯一接近为客户端服务方法服务, 可以确信客户端代码工作不改变你选择的实施模式。即使你只计划在“本地模式”下工作,它 仍然是在 J2EE 发展中最好的实践,来采用接口-基础为你来服务。使用应用模型,极其的容易 来跟随你的应用中的最好的实践。 =================================================================== 注意:不管是否在本地配置模式还是远程模式中使用应用模型,像在 8.4.4 节“可能需要 知道在客户端接口上关于方法的名称”中描述的一样,JDeveloper 设计时强迫自定义接口 方法使用 Serializable 类型。这就允许在本地和远程模式之间转化任何时间,或者不用改 变代码就可以同时支持两种。 =================================================================== 8.5.3 如何访问应用模型客户端接口 oracle.jbo.client包中的配置类可以使获得一个应用模型的例子非常的简单。你已经看到它 使用在许多测试客户端程序中,并且将会看到它再次在应用模型服务测试中作为JUnit测试的一部 分。因为它简单,因此对于开发者无论在那想访问应用模型都可以使用它的 createRootApplicationModule() 和 releaseApplicationModule()方法。 ---------------------------------------------------------------296----------------------------------------------------------------------------


因而,对于web应用来说,应该抵抗这一诱惑因为有更简单的方法。 8.5.3.1 在JSF Web应用中如何访问应用模型客户端接口 当为 JSF 或 Struts/JSP 应用服务时为数据绑定来使用 ADF 模式层,JDeveloper 在你的 Viewcontroller 工程中配置一个 servlet 过滤器,叫做 ADFBindingFilter。它在声明的绑定数据 的基础之上自动的获得并且释放适当的应用模型,并且确信服务对查找数据是有效的。你将会在后 续的章节中学习到更多的关于 ADF BindingContext 和数据控制方面的东西,在这里只记住可以通 过 BindingContext 来访问应用模型的客户端接口就足够了。因而 BindingContext 在每一个 web 页 面的请求中是有用的,通过涉及到的请求-范围属性命名数据,可以在 JSF 的控制 bean 中涉及绑定 的上下文。 例如,如果想访问devguide.model.SRService应用模型的自定义接口,要遵循例8-10中提到的 基本的步骤: 1、 访问JSF FacesContext 2、 为#{data}EL表达式创建数值绑定 3、 求绑定的值,计算BindingContext的结果 4、 通过名字从BindingContext中查找数据控制 5、 从数据控制中访问应用模型数据提供者 6、 计算应用模型到它的客户端接口 7、 通过客户端接口访问任意一个方法

---------------------------------------------------------------297----------------------------------------------------------------------------


Example 8–10 Accessing the Application Module Client Interface in a JSF Backing Bean package demo.view; import devguide.model.common.SRService; import javax.faces.context.FacesContext; import javax.faces.el.ValueBinding; import oracle.adf.model.BindingContext; import oracle.adf.model.binding.DCDataControl; import oracle.jbo.ApplicationModule; public class YourBackingBean { public String commandButton_action() { // 1. Access the FacesContext FacesContext fc = FacesContext.getCurrentInstance(); // 2. Create value binding for the #{data} EL expression ValueBinding vb = fc.getApplication().createValueBinding("#{data}"); // 3. Evaluate the value binding, casting the result to BindingContext BindingContext bc = (BindingContext)vb.getValue(fc); // 4. Find the data control by name from the binding context DCDataControl dc = bc.findDataControl("SRServiceDataControl"); // 5. Access the application module data provider ApplicationModule am = (ApplicationModule)dc.getDataProvider(); // 6. Cast the ApplicationModule to its client interface SRService service = (SRService)am; // 7. Call a method on the client interface service.doSomethingInteresting(); return "SomeNavigationRule"; } } SRDemo应用包含JSFUtils类,使用JSF值绑定来压缩评估EL表达式的步骤,并且可以在EL表达 式中使用符号在继承beans和查找到的beans中将方法链接在一起。因此,将这两个方法联合在一起, 就可以对单一的语句减少上述的步骤,如: // Access the SRService custom interface with a single EL expression SomeService service = (SomeService)JSFUtils.resolveExpression("#{data.SRServiceDat aControl.dataProvider}"); ======================================================================== 注意:10.3.2节,“如何在开始创建页面之前改变数据控制的名字”中解释了怎样在一个应用模 型中重命名数据控制。如果像SRDemo应用中那样做的话,你可以使用方法描述来为 SRService重命名数据控制,从缺省名字SRServiceDataControl到更短一些的名字 SRService,这也可以匹配应用模型自己的名字,这时上面的代码就会变成如下的形式: // Access SRService custom interface with a single EL expression // NOTE: SRService app module data control renamed to "SRService" SomeService service = (SomeService)JSFUtils.resolveExpression( "#{data.SRService.dataProvider}"); ======================================================================== ---------------------------------------------------------------298----------------------------------------------------------------------------


8.5.3.2 怎样在 JSP/Struts Web 应用中访问应用模型客户端接口 像例 8-11 中所示,如果在你的视图和控制器层中使用 Struts 和 JSP,就可以从自定义的 PageController 中使用代码访问 BindingContext 和应用模型自定义接口。注意可以直接的计 算 getDataProvider()的结果到应用模型客户端接口,而不需要重新得到 ApplicationModule。 Example 8–11 Accessing the Application Module Client Interface in ADF Page Controller package demo.view; import devguide.model.common.SRService; import oracle.adf.controller.v2.context.LifecycleContext; import oracle.adf.controller.v2.lifecycle.PageController; import oracle.adf.model.BindingContext; import oracle.adf.model.binding.DCDataControl; public class YourPageController extends PageController { public void prepareModel(LifecycleContext lcContext) { super.prepareModel(lcContext); BindingContext bc = lcContext.getBindingContext(); DCDataControl dc = bc.findDataControl("SRServiceDataControl"); SRService service = (SRService)dc.getDataProvider(); service.doSomethingInteresting(); } } 8.5.3 在 ADF Swing 应用中如何访问应用模型客户端接口 如果使用 Swing 来创建精确的应用,可以在 Swing 面板里面使用像例 8-12 中的代码那样访问 BindingContext 和应用模型自定义接口。 Example 8–12 Accessing the Application Module Client Interface in ADF Swing Panel package demo.view.panels; import devguide.model.common.SRService; import oracle.adf.model.BindingContext; import oracle.adf.model.binding.DCDataControl; // etc. public class YourPanel extends JPanel implements JUPanel { // etc. private void jButton1_actionPerformed(ActionEvent e) { BindingContext bc = getPanelBinding().getBindingContext(); DCDataControl dc = bc.findDataControl("SRServiceDataControl"); SRService service = (SRService)dc.getDataProvider(); service.doSomethingInteresting(); } } 8.6 重写已建立框架的方法 ApplicationModuleImp 基础类提供大量的的已建立方法来执行它的功能。附录 D, “普 遍应用的 ADF 业务组件方法”提供了大部分普遍应用代码的快速查找目录,这些是在自定义应用模 型中编写、使用和重写的典型类,这一章中会重点帮助你理解重写这些已建立框架方法的基本步骤。 ---------------------------------------------------------------299----------------------------------------------------------------------------


8.6.1 如何重写已建立的框架方法 为一个应用模型重写已建立的框架方法,可以从应用模型 Java 类中在 JDeveloper 主菜 单中选择“Source|Override Methods…”。如图 8-9“Override Method“对话框所示。你可以 使用滚动条来查看所列出的方法,但是如果你知道你想重写的方法,就输入方法的第一个字母, 这样可以更快的找到你想要的方法。 假如当一个新使用者的应用模型服务组件的对话第一次开始时,想重写应用模型中的 prepareSession()方法来增加缺省功能性。检查 prepareSession(oracle.jbo.Session)的选择 框,并且选中它。 ======================================================================== 注意:这里你仅仅选择了一个方法,“Override Metheds“对话框允许选择同时多个方法来重 写。 ========================================================================

8.6.2 当重写已建立的框架方法时会发生什么 当关闭了“Override Methods“对话框,就回到了鼠标指示的代码编辑器中被重写的方法 处,如图 8-10 所示。注意这一方法是以被称为 super.prepareSession()的单独一行出现的。 这种语法是 Java 在调用基础类的缺省方法的普遍的方法。在自定义应用模型类的这一行之前 或之后添加代码,可以在缺省的函数之前或之后增加缺省的方法。 也要注意当在基础类中重写方法时,代码编辑器的左边会显示一个小的“向上箭头“的图 标。这是这一方法是与接口或基础类有关系的标示。将鼠标放于这一图标上,将会有反应展示 这一方法是基础类中被正确重写的方法,如图所示。

---------------------------------------------------------------300----------------------------------------------------------------------------


当这一方法通过"Override Methods"对话框创建后,如果开始键入被重写的方法名字,这对准 确的署名这些想重写的基础类方法是很重要的。如果在你认为是一个被重写的方法中出现了拼写的 错误,或者如果出现了错误的数字或类型,或者错误的返回值。这时不会出现编译错误但是你不会 得到你预期的重写的基础类方法。这正确的反应使你获取准确的名字更加清晰。为了快速的操纵基 础类到超类,可以通过鼠标右键选择"Go to Overriden Method"菜单在重写标志的旁边。

======================================================================== 注意:在添加“design time“可视方法重写以上描述时,可以在要重写超类时考虑添加JDK5.0@ 重写注释。如果不匹配任何一个超类的方法就会引起编译器产生编译时间错误。 ======================================================================== 8.6.3 如何重写 prepareSession()来为新的用户对话建立应用模型 自从prepareSession()方法被应用模型调用后,当它被一个新的用户对话第一次使用时,它是 一个好的方法来重写自定义应用模型类来完成计划工作,这对每一个新用户都是特殊的,它使用你 的应用模型。例8-13 举例说明了在devguide.model.SRServiceImpl类中重写prepareSession()方 法,调用findLoggedInUserByEmailInStaffList()帮助方法来初始化StaffList视图体,举例显示 相应正确的进入用户。 帮助方法如下: 1、 通过super.prepareSession()来完成缺省的工作 2、 访问StaffList视图体获得使用getStafflist()方法 3、 通过getUserPrincipalName()方法获得正确的鉴权通过的用户的名字 ======================================================================== 注意:直到你学习了J2EE安全性在30.4节,"为使用管理的安全配置ADF业务组件应用", getUserPrincipalName()API在下面的例子中将会返回空值而不是鉴权的用户的名字。因此,为了 达到测试的目的,例子中包含了可撤销的代码来分配固定的email ID。 ========================================================================

---------------------------------------------------------------301----------------------------------------------------------------------------

4、 定义 CurrentUser 命名的绑定变量,用 currentUserName 成员变量作为缺省值 5、 设置附加的 WHERE 子句来通过 email 查找当前用户的行。 6、 执行查询为当前用户重新找回 StaffList 行 用这种方法重写prepareSession()方法后,如果使用业务组件浏览器测试SRService应用模 型,将会看到StaffList视图体有相应的单一的行到Steven King(email = ‘sking’)。 Example 8–13 Initializing the StaffList View Object Instance to Display a Current User'sInformation // In devguide.model.SRServiceImpl class


protected void prepareSession(Session session) { // 1. Call the superclass to perform the default processing super.prepareSession(session); findLoggedInUserByEmailInStaffList(); } private void findLoggedInUserByEmailInStaffList() { // 2. Access the StaffList vo instance using the generated getter method ViewObject staffList = getStaffList(); // 3. Get the name of the currently authenticated user String currentUserName = getUserPrincipalName(); /* * Until later when we learn how to integrate J2EE security, * this API will return null. For testing, we can default it * to the email of one of the staff members like "sking". */ if (currentUserName == null) { currentUserName = "sking"; } /* * We can't use a simple findByKey since the key for the * StaffList view object is the numerical userid of the staff member * and we want to find the user by their email address. We could build * an "EMAIL = :CurrentUser" where clause directly into the view object * at design time, but here let's illustrate doing it dynamically to * see this alternative. */ // 4. Define named bind variable, with currentUserName as default value staffList.defineNamedWhereClauseParam("CurrentUser", // bindvar name currentUserName, // default value null); // 5. Set an additional WHERE clause to find the current user's row by email staffList.setWhereClause("EMAIL = :CurrentUser"); // 6. Execute the query to retrieve the StaffList row for the current user staffList.executeQuery(); /* * If the view object needs to be also used during this session * without the additional where clause, you would use * setWhereClause(null) and removeNamedWhereClauseParam("CurrentUser") to * leave the view object instance back in it's original state. */ } ---------------------------------------------------------------302----------------------------------------------------------------------------

8.7 为业务服务创建应用模型图表 如果你的业务服务的数据模型和服务接口对于你的团队来说是关键的信息。会经常方便的用


UML模型来显示。JDeveloper支持为应用模型创建团队成员都可使用的图表。 8.7.1 如何创建应用模型图表 为应用模型创建图表: 1、 在“Business Tier>ADF Business Components“中,从“New Gallery“中打开“Create Business Components“对话框。 2、 在创建图表的对话框中提示输入图表名字和包的名字,输入图表名字,如“SRService Data Model“和包名,如”devguide.model.design“。选择”OK“来创建空的图表和打开对话 框。 3、 添加已存在的应用模型到图表中,选择它们到应用中并且放到图标表面 4、 使用属性: 隐藏包名, 改变字体 关掉栅格和包 关掉在视图链接上显示的角色名字(“Master/Detail“) 完成这些步骤后,图表像图 8-11 所示。

8.7.2 当你创建应用模型图表时会发生什么 当你创建业务组件图表时,JDeveloper 创建一个 XML 文件通过工程模型路径的目录来说明 图表,这个目录是和图表中的包名相对应的。对于上面的 Bussiness Domain Objects 图表, 它将会创建一个在./devguide/model/design 目录中相对应的*.oxd_bc4j 文件。通过缺省的应 用指导统一显示工程内容路径,可以使 ADF 组件和 Java 文件在源路径出现在相同的树状包中, 使 UML 模型在工程的模型路径中。你可以使用应用指导中的“Toggle Directories”工具栏图 标,来匹配统一的视图和看到的明显的工程内容路径文件。

---------------------------------------------------------------303----------------------------------------------------------------------------

8.7.3 关于应用模型图表需要知道些什么 你可以直接在图表上做大量的工作,如编辑应用模型、控制显示选项、过滤方法名字、显示有


关联的实体和文件、声明应用和开始业务组件浏览器。 8.7.3.1 使用图表编辑应用模型 业务组件的 UML 图表不仅仅是一个静态图片,还可以及时反映你什么时候删除图表上的应用 模型。进一步说,它是一个基础 UML 描写当前组件定义,所以它总是反映当前事物的状态。UML 图表不仅是清楚的帮助也是形象的指导和编辑工具。你可以为图表中的任何应用模型产生应用模 型编辑器,通过右键菜单(或双击)选择 Properties。你可以直接在图表上完成一些应用模型编 辑工作,像重命名视图体实例、在数据模型上删除视图体定义来创建一个新的视图体实例、通过 删除键移动视图体实例。 8.7.3.2 控制显示选项 在图表中选择应用模型之后,使用属性检查器来控制显示选项。在显示种类中,有如下一些 属性: Show Stereotype ——来显示实体类型(如“《application module》 ”) Show Operations —— 来显示服务方法 Show Package —— 来显示包的名字 ======================================================= 注意:术语 operation 是更普通的,方法的 UML 名字。 ====================================================== 在 Operations 种类中,你可以通过细节数量改变下面的属性: 显示方法参数 显示返回类型 显示可见性(公共的、私有的、等等) 在右键菜单中,你也可以选择如下视图: Standard —— 来显示服务操作 Expanded —— 来显示操作和数据模型(缺省的) Compact —— 仅仅显示图表和名字

---------------------------------------------------------------304----------------------------------------------------------------------------


305——318 空白


Example 9–3 为每个新行的标题性的默认属性值 //In ServiceHistoryImpl.java in SRDemo sample protected void create(AttributeList nameValuePair) { super.create(nameValuePair); setSvhType(getDefaultNoteType()); setCreatedBy(getCurrentUserId()); setLineNo(new Number(getServiceRequest().getMaxHistoryLineNumber()+1)); } 9.4.1.1 在 create()和 initDefaults()方法之间选择 如果一个实体行有了新的值,就需要调用refresh()方法,如果无法支持 EFRESH_REMOVE_NEW_ROWS 或 REFRESH_FORGET_NEW_ROWS标记,这个实体行就会返回一个初始值。 做为程序的一部分,会调用这个实体对象的initDefaults()方法,而不是create()方法。当该行第 一次被创建时,或被更新到初始值时,可以不考虑默认逻辑的initDefaults()方法。 9.4.1.2 从数据库序列来默认一个属性值 第6.6.3.7节, “与触发器赋值同步”中已经解释了该如何使用数据库序列在提交时通过数据库 序列来增加主键的属性的方法。有时,你可以希望较早的对一个实体行在创建时就分配一个序列值, 这也使用者就可以获得这个值,而且这个值在数据保存时也不会改变。为了实现这个功能,可以使 用帮助类SequenceImpl中的oracle.jbo.server包中的create()方法,如示例9-4所示,这是来自 SRDemo应用程序产品实体对象中的用户JAVA类代码。在调用super.create()方法后,会创建一个新 的SequenceImpl对象的实例,传递序列的名称和当前事务对象,然后根据SequenceImpl’s getSequenceNumber()方法的返回值调用setProdId()属性定位方法。 Example 9–4 在创建时从数据库序列来默认一个属性值 // In ProductImpl.java import oracle.jbo.server.SequenceImpl; // Default ProdId value from PRODUCTS_SEQ sequence at entity row create time protected void create(AttributeList nameValuePair) { super.create(nameValuePair); SequenceImpl sequence = new SequenceImpl("PRODUCTS_SEQ",getDBTransaction()); setProdId(sequence.getSequenceNumber()); } 9.4.2 保存前对分配起源值 如果想在保存某行前对实体对象属性值赋值标题的默认值,就不需要考虑prepareForDML()方 法,而调用适当的属性定位方法来组装起源属性值。为了在INSERT,UPDATE,或DELETE的过程中完 成���务,需要分别地比较传递到该方法中的相对的整数常量DML_INSERT、DML_UPDATE和DML_DELETE 的操作参数值。


---------------------------------------------------------------319----------------------------------------------------------------------------

示例9-5是当创建某个服务历史记录的某些类型时,显示了prepareForDML()方法用在 ServiceHistory的在SRDemo实例中自动的更新服务请求的状态的实体对象。当插入新的服务历史条 目时,该代码修改了: z 如果新的历史记录被用户添加了,那么未决的或已关闭的服务请求将被打开; z 如果新的历史记录被技术人员添加了,那么一个开启的服务请求将变成等待解决的 Example 9–5 调用PrepareForDML方法在保存前对导出值赋值 // In ServiceHistoryImpl.java protected void prepareForDML(int operation, TransactionEvent e) { super.prepareForDML(operation, e); // If we are inserting a new service history entry... if (operation == DML_INSERT) { ServiceRequestImpl serviceReq = getServiceRequest(); String historyType = getSvhType(); // If request is pending or closed and customer adds note, status => Open if ((serviceReq.isPending() || serviceReq.isClosed()) && CUSTOMER_TYPE.equals(historyType)) { serviceReq.setOpen(); } // If request is open & technician adds a non-hidden note, status => Pending if (serviceReq.isOpen() && TECHNICIAN_TYPE.equals(historyType)) { serviceReq.setPending(); } } } 9.4.3 当赋属性值时分配起源值 不管对那个属性值赋值,为了分配起源属性值,需要对后一个属性的定位方法添加代码。示 例9-6显示了在SRDemo实例中的ServiceRequest实体对象的AssignedTo属性的定位方法。在调用 setAttributeInternal()方法对AssignedTo属性赋值后,可以用定位方法为AssignedDate方法来获 得当前的日期和时间值。 Example 9–6 只要分配的属性值发生变化时就分配的时间赋值 // In ServiceRequestImpl.java public void setAssignedTo(Number value) { setAttributeInternal(ASSIGNEDTO, value); setAssignedDate(getCurrentDateWithTime()); } 注释:正如上面所示,在属性的赋值和取值方法中增加用户代码是安全的。使用 在类文件中修改代码时,明智的保留用户代码。

JDeveloper


---------------------------------------------------------------320----------------------------------------------------------------------------

9.5 对实体使用刷新方法来取消未处理的变化 你可以在需要的时候使用refresh(int flag)方法来对任何一行进行刷新未处理的变化。 refresh()方法的行为依靠传入的flag参数。下面的三个flag常量值决定了刷新方法在行表面的作 用。 z REFRESH_WITH_DB_FORGET_CHANGES:在当前事务中忽略对行进行的修正,从数据库刷新该行的 数据。不管该行是否被修改,最终的数据库数据替换了原有数据。 z REFRESH_WITH_DB_ONLY_IF_UNCHANGED:当行未发生改变时,和 REFRESH_WITH_DB_FORGET_CHANGES功能类似,当某行已经被该事务修改,那么是不能进行刷新 的。 z REFRESH_UNDO_CHANGES:当行未发生改变时,和REFRESH_WITH_DB_FORGET_CHANGES功能类似, 对于发生修改的行,在事务的开始就会刷新该行的属性值,且该行保留在修改状态。 9.5.1 控制刷新时对新记录会发生什么 缺省情况下,某些处于新状态的实体行在刷新后会回复到初始化状态的空白行。重新安排公开 的默认值,和initDefaults()方法中标题的默认值代码一样,但是实体对象在清空的过程中不会调 用create()方法。根据上面部分(使用bitwise-OR 作者注)结合下面两种标记之一,你可以修改 该默认值。 z REFRESH_REMOVE_NEW_ROWS,刷新过程中新行被转移; z REFRESH_FORGET_NEW_ROWS,新行被绝对的标记。 9.5.2 串联刷新组成子实体行 你可以使用refresh()操作来串联,结合结合的有效标记, 利用bitwise-OR的 REFRESH_CONTAINEES标记组成子实体行。这将促成实体调用refresh(),在其包含的组成的子实体 上使用相同的模式。 9.6 使用视图对象来确认 当你的业务逻辑需要执行SQL访问,最自然的选择是使用视图对象来执行这个任务。紧记你确 认执行的SQL声明,如果这些是以实体为基础的视图对象,你在实体缓冲器zhong 将会“看见”未 处理的改变,只读视图对象可以重新找回被数据库表示的数据。 9.6.1 在运行时创建视图对象来确认 当某个应用情节中设计的实体对象被再次利用时,必须确认这些对象没有直接依靠某个特殊应 用模块的数据模型的视图对象实例。这样做可以防止这些对象被另一些及不合需要的应用模块再次 利用。


---------------------------------------------------------------321----------------------------------------------------------------------------

然而,你的实体对象可以使用在运行时可以找到自身的根源应用模块,使用这些应用模块实例 的createViewObject()方法来创建一个需要“确认”的视图对象的实例。同样,在设计时对实体对 象实例增加数据模型,API允许对视图对象分派一个实例名称,这样,你就可以在需要时使用 findViewObject()方法再次找到它。 因为基于SQL的确认代码可能会执行若干次,在每次需要时和使用过程中创建视图对象实例不 是最有效的办法。然而,如果这个视图对象实例已经存在,你可以执行如示例9-7所示的帮助方法 来使用它,或者在第一次需要时另外创建它。为了确保在运行时创建的视图对象实例的实例名称不 会和数据模型中某个设计时指定的实例名称发生冲突,你可以采用基于视图对象定义名称的协议创 建一个带有类似“Validation_”前缀的名称。这仅仅是一种方法,只要这个名称没有和定义时使 用的名称发生冲突,你可以使用任何名称定义方案。 Example 9–7 确认时访问视图对象的帮助方法 /** * Find instance of view object used for validation purposes in the * root application module. By convention, the instance of the view * object will be named Validation_your_pkg_YourViewObject. * * If not found, create it for the first time. * * @return ViewObject to use for validation purposes * @param viewObjectDefName */ protected ViewObject getValidationVO(String viewObjectDefName) { // Create a new name for the VO instance being used for validation String name = "Validation_" + viewObjectDefName.replace('.', '_'); // Try to see if an instance of this name already exists ViewObject vo = getDBTransaction().getRootApplicationModule() .findViewObject(name); // If it doesn't already exist, create it using the definition name if (vo == null) { vo = getDBTransaction().getRootApplicationModule() .createViewObject(name,viewObjectDefName); } return vo; } 有了类似这些适当的帮助方法,你的确认代码可以调用getValidationVO()方法,并在需要使 用时传递已经定义的视图对象的被限定的名称。然后你就可以像示例9-8一样编写代码了。

---------------------------------------------------------------322----------------------------------------------------------------------------


Example 9–8在方法验证中使用确认视图对象 // Sample entity-level validation method public boolean validateSomethingUsingViewObject() { Number numVal = getSomeEntityAttr(); String stringVal = getAnotherEntityAttr(); // Get the view object instance for validation ViewObject vo = getValidationVO("devguide.example.SomeViewObjectName"); // Set it's bind variables (which it will typically have!) vo.setNamedBindWhereClauseParam("BindVarOne",numVal); vo.setNamedBindWhereClauseParam("BindVarTwo",stringVal); vo.executeQuery(); if ( /* some condition */) { /* * code here returns true if the validation succeeds */ } return false; } 正如例子中代码的建议,用于确认的视图对象会象征性的有一个或多个命名的约束变量。依靠 这种类型的数据,你的视图对象可以重新找回,上面“/* some condition */”中的语法看上去会 不同。举例来说,如果你的视图对象的SQL语法是选择count()或一些其他集合,这个条件会有代 表性的使用vo.first()方法来访问第一行,然后使用getAttribute()方法来访问属性值,并获得数 据库返回合计值。 如果确认的成功与否基于查询返回的结果是空或一行,那么这个环境仅仅能简单的测试 vo.first()方法是返回空或非空。如果vo.first()方法返回空,那么就没有“第一”行,换句话说, 就是查询没有找到记录。 在另一些情形中,你可能重申一个或更多根据视图对象找到的查询结果开决定确认是否成功。 9.6.2 执行有效的现有标准 基于SQL的一个命令类型的确认就是一个候选外键的值在相关表中存在的简单测试。这种类型 的确认可以在实体定义的findByPrimaryKey()方法中执行,但是,如果实体存在,这将会返回实体 的所有属性。可以用一个有选择性的方法来执行轻量级的现有标准,包括使用视图对象来确认。 示例9-9显示的是exists()方法在SRDemo应用产品实体对象包含在自定义的实体定义类中的应 用。首先,它使用了上面帮助方法的变量,即将DBTransaction作为变量来返回这个适当的有效的 视图对象。这是封装在getProductExistsVO()方法内部的相同类。 oracle.srdemo.model.entities.validationqueries包中的用于确认的只读视图对象命名为 ProductExistsById。因为该视图对象有自定义JAVA类(ProductExistsByIdImpl),exists()方法 中的代码可以调用固定类型的setTheProductId()方法来获得该视图对象定义的命名约束变量的 值。然后代码执行查询方法并获得基于某行是否找到来获得布尔变量foundIt的值。 ---------------------------------------------------------------323----------------------------------------------------------------------------

Example 9–9使用视图对象和实体高速缓冲存储器来执行有效的现有标准e // In ProductDefImpl.java in SRDemo sample


public boolean exists(DBTransaction t, Number productId) { boolean foundIt = false; ProductExistsByIdImpl vo = getProductExistsVO(t); vo.setTheProductId(productId); vo.setForwardOnly(true); vo.executeQuery(); foundIt = (vo.first() != null); /* * If we didn't find it in the database, * try checking against any new employees in cache */ if (!foundIt) { Iterator iter = getAllEntityInstancesIterator(t); while (iter.hasNext()) { ProductImpl product = (ProductImpl)iter.next(); /* * NOTE: If you allow your primary key attribute to be modifiable * then you should also check for entities with entity state * of Entity.STATUS_MODIFIED here as well. */ if (product.getEntityState() == Entity.STATUS_NEW && product.getProdId().equals(productId)) { foundIt = true; break; } } } return foundIt; } 即使目前SRDemo应用还不允许最终用户创建新的项目,但假设用户在将来的某个被执行应用上 可以这样做,这对执行确认是有益的。跟在executeQuery()测试后的代码可以看出一个新的产品实 体的候选产品ID在缓存中是否存在。 取消那些没有实体用法的有效视图对象,这就仅能“看到”数据库当前的行记录。因此,如果 foundIt变量在数据库中尝试失败后,剩下的代码会获得ProductImpl实体行的重复体,在当前缓存 中和上面指令的循环中查看某个新的产品实体行是否有了约束的产品ID。如果是这样,exists()方 法仍然会返回真。 9.6.3 确认条件和所有的特定类型的实体有关 在确认前,已经提交数据库的每个未解决的改变列表都会调用beforeCommit()方法。执行基于 对象的实体对所有特定类型的实体行必须遵循某些规则的确认会是一个正确的方法。 ---------------------------------------------------------------324----------------------------------------------------------------------------

======================================================================== 注释:如果你的beforeCommit()逻辑抛出了ValidationException(参数异常),你必须在


你的结构上赋jbo.txn.handleafterpostexc特性为真,从而使框架自动处理回退到其他实体 uix的存储状态,在当前提交周期内,这可能已经成功的提交数据库(但还没有确认)。 ======================================================================== 9.7 怎么使用联合存储器存取相关的实体行 一般确认规则或起源的标题默认值可能需要咨询相关联的实体行的值,在你的实体对象自定义 Java类中的这种关联存储方法让任务变得非常简单。通过调用存储方法,你可以很轻易的存取相关 联的实体行——或者赋值获取实体行——依赖于联合的基数性。 示例9-10是一个标题默认逻辑在SRDemo的ServiceHistory实体对象的应用的例子。新的服务历 史行的行数是由ServiceHistoryImpl类型的父实体行计算出的,在该值增加一是,调用了 getMaxHistoryLineNumber()帮助方法。如果父实体行在缓存中已经存在,关联的存储器将从其中 获取该行值,如果不存在,将通过主键带进缓存中。 Example 9–10 在创建方法中访问组成的父实体行 // In ServiceHistoryImpl.java in SRDemo sample protected void create(AttributeList nameValuePair) { super.create(nameValuePair); setSvhType(getDefaultNoteType()); setCreatedBy(getCurrentUserId()); setLineNo(new Number(getServiceRequest().getMaxHistoryLineNumber()+1)); } 示例9-11举例说明了ServiceRequest实体对象的自定义Java类中getMaxHistoryLineNumber() 方法的代码。为了计算在现有服务历史行中的LineNo属性的最大值,这是找回ServiceHistory子行 (ServiceHistoryImpl类型)的Rowset的关联存储器的另一种使用方法。 ---------------------------------------------------------------325----------------------------------------------------------------------------

Example 9–11在使用关联存储器的计算中存取组成的子实体行Association Accessor // In ServiceRequestImpl.java in SRDemo Sample public long getMaxHistoryLineNumber() { long max = 0; RowSet histories = (RowSet)getServiceHistories(); if (histories != null) { while (histories.hasNext()) { long curLine = (ServiceHistoryImpl)histories.next()).getLineNo().longValue(); if (curLine > max) { max = curLine; } } } histories.closeRowSet(); return max; } 9.8 如何取得有关权限用户的参考信息 如果你已经把运行时间配置参数“jbo.security.enforce”的值设定为“Must”或者


“Auth”,那么“oracle.jbo.server.SessionImpl”对象会提供给你一种方法,可以获取关于权 限用户名称以及属于哪一类型用户的信息。这种执行类用于用户可以访问的 “oracle.jbo.Session”用户界面。 9.8.1 如何存取权限用户的类型信息 “oracle.jbo.Session”用户界面提供了2种方法: z String[] getUserRoles(),返回用户所属的类型名称队列 z boolean isUserInRole(String roleName),如果用户属于指定类型则返回“true” 实体对象代码可以通过“Session session = getDBTransaction().getSession();”来访问 “Session/会话”。 例 9-12 给出了一个如何使用上述方法的事例:用 isUserInRole()来判断用户是否属于技术 类型角色来决定当前用户是否属于技术人员。 ---------------------------------------------------------------326----------------------------------------------------------------------------

Example 9–12 测试权限用户是否是给定角色的帮助方法 protected boolean currentUserIsTechnician() { return getDBTransaction().getSession().isUserInRole("technician"); } 当重新代理在单个的SRConstants类中的常量后,SRDemo应用程序包含了类似基于 SREntityImpl类的帮助方法,即示例中的所有实体对象都延伸到继承这个公共的功能。 protected boolean currentUserIsTechnician() { return getDBTransaction().getSession() .isUserInRole(SRConstants.TECHNICIAN_ROLE); } protected boolean currentUserIsManager() { return getDBTransaction().getSession() .isUserInRole(SRConstants.MANAGER_ROLE); } protected boolean currentUserIsCustomer() { return getDBTransaction().getSession() .isUserInRole(SRConstants.USER_ROLE); } protected boolean currentUserIsStaffMember() { return currentUserIsManager() || currentUserIsTechnician(); } 基于当前用户的角色,可以使用create()方法来有条件的默认服务请求。getDefaultNoteType() 帮助方法如下: // In ServiceHistoryImpl.java in SRDemo sample private String getDefaultNoteType() { return currentUserIsStaffMember() ? TECHNICIAN_TYPE : CUSTOMER_TYPE; } 用于ServiceHistory实体对象不考虑create()方法来默认基于当前用户角色的服务历史类型。 // In ServiceHistoryImpl.java in SRDemo sample protected void create(AttributeList nameValuePair) {


super.create(nameValuePair); setSvhType(getDefaultNoteType()); setCreatedBy(getCurrentUserId()); setLineNo(new Number(getServiceRequest().getMaxHistoryLineNumber()+1)); } 9.8.2 如何存取权限用户的名称/命名 你必须把 Session 用户界面(会话界面)指定为 SessionImpl 执行类,这样才能够获取权限用 户的名称。然后你可以应用 getUserPrincipalName()方法。例 9-13 阐述了在实体对象中应用这 样的方法来找回当前用户名的事例。 Example 9–13 存取当前权限用户名的帮助方法 protected String getCurrentUserName() { SessionImpl session = (SessionImpl)getDBTransaction().getSession(); return session.getUserPrincipalName(); } ---------------------------------------------------------------327----------------------------------------------------------------------------

9.9 如何获取初始特征值 在当前事务/处理过程中如果一个实体属性值被改变,你可以通过属性获取方式/调用去获取改 变后的属性值。使用getPostedAttribute()函数, 实体对象业务逻辑可以参照那些在数据库中读 取到且未曾修改过的初始属性值。函数以特征/属性值为参数,因此Jdeveloper会根据生成的属性 值常数来进行维护。 9.10 如何存储当前用户会话的信息 你可以使用会话/Session 对象提供的用户数据 hashtable 来存取当前用户相关的信息,以供 实体对象业务逻辑做参考。至于 SRDemo 程序如何使用 hashtable:当一个新用户第一次使用程序模 块时, prespareSession()函数就响应起来,如同例 9-14 中显示的那样,SRService 程序模块会 跳 过 prepareSession() 函 数 并 且 通 过 LoggedInUser view 对 象 实 例 中 retrieveUserInfoForAuthenticatedUser() 函 数 去 自 动 找 回 权 限 用 户 的 信 息 。 因 此 , 它 通 过 setUserIdIntoUserDataHashtable()辅助函数把用户的数字 ID 信息存入用户数据 hashtable 中。 Example 9–14 自动查询用户信息的重要的prepareSession()方法 // In SRServiceImpl.java in SRDemo Sample protected void prepareSession(Session session) { super.prepareSession(session); /* * Automatically query up the correct row in the LoggedInUser VO * based on the currently logged-in user, using a custom method * on the LoggedInUser view object component. */ getLoggedInUser().retrieveUserInfoForAuthenticatedUser(); setUserIdIntoUserDataHashtable();


} 例9-15显示了LoggedInUser view对象的retrieveUserInfoForAuthenticated User()函数代码。代码动态的绑定了EmailAddress 与会话中的权限用户名称,然后通过 executeQuery()函数从USERS表中找回该用户其他的信息。 Example 9–15 访问权限用户名来找回用户的其他信息细节 // In LoggedInUserImpl.java public void retrieveUserInfoForAuthenticatedUser() { SessionImpl session = (SessionImpl)getDBTransaction().getSession(); setEmailAddress(session.getUserPrincipalName()); executeQuery(); first(); } 在LoggedInUser view对象找回的权限用户信息中,其中的一部分是用户数字ID号码-这就是 函数返回的结果。例如,sking用户的数字UserID是300。 ---------------------------------------------------------------328----------------------------------------------------------------------------

例9-16 事例说明了setUserIdIntoUserDataHashtable()函数 ―在上文中应用于 prepareSession()代码中―使用字符常量SRConstants.CURRENT_USER_ID提供的密钥把用 户数字ID存储在用户数据hashtable中。 Example 9–16在 UserData Hashtable中通过实体对象的存取来赋予信息 // In SRServiceImpl.java private void setUserIdIntoUserDataHashtable() { Integer userid = getUserIdForLoggedInUser(); Hashtable userdata = getDBTransaction().getSession().getUserData(); if (userdata == null) { userdata = new Hashtable(); } userdata.put(SRConstants.CURRENT_USER_ID, userid); } serviceRequest和ServiceHistory实体对象都有一个覆盖性的create()函数,该函数通过使用 一个如下所示的辅助函数能够计划性的设定CreatBy属性为当前权限用户的数字ID。 protected Number getCurrentUserId() { Hashtable userdata = getDBTransaction().getSession().getUserData(); Integer userId = (Integer)userdata.get(SRConstants.CURRENT_USER_ID); return userdata != null ? Utils.intToNumber(userId):null; } 9.11 如何获取”Current Date and Time”当前日期和时间 你也许会发现在涉及到实体对象业务逻辑中的当前日期和时间时,这种辅助函数很有用。没有 任何时间信息的情况下想得到当前日期,请参考例 9-17 中的一个辅助函数。 Example 9–17随时存取当前时间的帮助方法 /* * Helper method to return current date without time * * Requires import: oracle.jbo.domain.Date


*/ protected Date getCurrentDate() { return new Date(Date.getCurrentDate()); } 相反的,如果你需要得到已包含部分当前日期的时间信息,可以使用例9-18中所示的辅助函数。 ---------------------------------------------------------------329----------------------------------------------------------------------------

Example 9–存取带时间的当前日期的帮助方法 /* * Helper method to return current date with time * * Requires imports: oracle.jbo.domain.Date * java.sql.Timestamp */ protected Date getCurrentDateWithTime() { return new Date(new Timestamp(System.currentTimeMillis())); } 9.12 如何在一个成功业务提交的基础上发送通告 对于每个处于等待修改列表中和成功保存到数据库中的实体行都必须调用afterCommit()函数, 你也许可以用这个函数来发送关于一个实体状态改变的邮件通告。 9.13 如何有条件的预防一个实体行被删除 每一个实体行在被删除前都会调用 remove()函数。 你可以有条件的在这个函数中应用 JboException 来防止一行在相关因素不符合的情况下被错误的删除掉。 ======================================================================== 注释:实体对象提供了能够防止删除已有的管理员实体行以及下面的子行的功能,你必须在 Association Editor中的Association Properties页面上选择这个选项。 ======================================================================== 9.14 如何实现有条件的属性更新能力 你可以不管实体对象类中的 isAttributeUpdateable()函数而去有规划的判断一个既有的在合 适条件的运行时间内的属性是否可更新。例 9-19 显示了仅仅因为当前权限用户是一个公司员工, ServiceHistory 实体对象就可以跳过 isAttributeUpdateable()函数来强调 SvhType 属性是可更新 的。请注意:当实体对象激活这个函数时,整个属性索引就是可更新的。在基于属性索引的一个 if or Switch 说明的特殊属性,可以使用有条件的更新逻辑。在实体对象用户 java 类的完整属性索 引常数由 JDeveloper 自动维护, SVHTYPE 则涉及到这个常数。 Example 9–19 有条件的决定一个属性在运行时的更新能力 // In ServiceHistoryImpl.java public boolean isAttributeUpdateable(int index) { if (index == SVHTYPE) { if (!currentUserIsStaffMember()) { return super.isAttributeUpdateable(index);


} return CUSTOMER_TYPE.equals(getSvhType()) ? false : true; } return super.isAttributeUpdateable(index); } ---------------------------------------------------------------330----------------------------------------------------------------------------

======================================================================== 注释:基于实体的对象继承了这种条件化的可更新能力,如同可以把任何东西压缩进实体对象。你 需要执行这种条件化的可更新能力逻辑,通过指定一个临时的对象属性或者强调那些包含在多实体 对象中的信息。 在观察对象的观察行类中,你可以不用同一个方法去达到期望的结果。 ======================================================================== 9.15 其他相关资料 在 Oracal 的 ADF 商务组件白皮书里的商务规则,这个白皮书是由 oracal 协助整理的通用的用 来分类,执行真实世界每一个商用规则, 这个规则他们曾经在执行他们的项中遇到过当使用 orcal ADF 并且这个 adf 应用了 adf 的商用组件。你可以在 OTN 的 Oracle JHeadstart Product Center 获取相关资料。

---------------------------------------------------------------331----------------------------------------------------------------------------


---------------------------------------------------------------332----------------------------------------------------------------------------


使用srdemo应用程序的srservice作为一个例子,这章介绍了应用模块的积极数据模型和商业服 务界面的方法如何能够在设计时进行拖放数据绑定以及它们是如何在运行时均可以由ADF的模型数 据约束层使用的应用模块的数据来进行控制。 这章包括如下几节 ■ Section 10.1, "Overview of Data Controls and Declarative Bindings" ■ Section 10.2, "Understanding the Application Module Data Control" ■ Section 10.3, "How an Application Module Appears in the Data Control Palette" ■ Section 10.5, "Application Module Databinding Tips and Techniques" ■ Section 10.6, "Overview of How SRDemo Pages Use the SRService" 10.1 数据控制以及公开性的约束 在Oracle ADF的模型层是一个公开性数据绑定的设施。它实现了这两个概念,在符合JSR-227 的规格,使解耦的用户界面技术从商业服务的执行情况:数据控制和公开绑定。数据控制和公开绑 定,使一个统一的设计时间和运行时的做法,约束任何用户界面的任何后端业务服务代码。 10.1.1 商业服务实现技术的数据控制摘要 商业服务实现技术的数据控制摘要用标准的中继接口来描述服务的业务和数据收集工作。这包 括有关的属性,方法和所涉及的类型信息。在设计时,类似与 JDeveloper 的可视工具可以使用标 准服务元数据来简化 UI 组件的任何数据控制操作或数据收集的所以绑定工作。在运行时,通用的 Oracle ADF 的模型层可以读取到这类信息:描述数据控制以及来自 XML 文件并能实施双程“有线” 连接您用户界面与业务服务的相关绑定功能。

---------------------------------------------------------------333----------------------------------------------------------------------------


10.1.2UI 控件与数据收集和操作的绑定 公开的绑定摘要详情访问数据,从数据控制中的数据收集和其业务。有三种基本的公开绑定的 对象,所有企业应用要求的自动化方面的数据绑定: z Iterator bindings,可以绑定到一个数据收集的当前行。 z Value bindings, 可以把一个数据收集中的UI组件&属性绑定起来 z Action bindings,可以在一个数据控制或数据采集中调用自定义或内置的操作 迭代绑定简化建设的用户界面,允许滚动和传呼通过收集的数据和钻井从概要到详细信息。用 户界面组件显示数据利用价值的绑定。值绑定范围从最基本品种工程,一个简单的文本字段,以更 精密的名单,表,并树的绑定,支持额外的需求清单,表格,树和UI控件。1有约束力的行动所使 用的UI组件一样,超链接或按钮引用方法。一行动的约束力,使用户可以按一下该组件援引商业服 务没有代码。有两种,一种行动绑定:定期行动约束力援引一个内置的在运作,和方法的行动具有 约束力援引一自定义操作。 图 10–1 控件与数据收集和操作的绑定

======================================================================== 注释:值的绑定是绑定有约束的属性值。所有的绑定的值都在oracle.bingding.AttributeBinding界 面上。行动绑定的用户界面是oracle.binding.operationbinding 。由于这两种具有绑定功能的界面都是 与UI控件相关,他们都能够拓展oracle.binding.controlbinding界面。控制绑定术语是用于在本指南中 描述的既是价值绑定和也是行动的绑定事情。 ========================================================================

---------------------------------------------------------------334----------------------------------------------------------------------------


10.2 理解应用模块的数据控制 应用模块的数据控制就是其中的几个实现了与 Oracle ADF 的数据控制。它的工作是要薄适配器 在请求一开始的时候,通过应用模块池自动获得一个可用的应用模块。在当前请求的应用模块的数 据控制持有参考应用模块,例如,当前用户的 session。在请求的最后,应用模块的数据控制通过应 用模块返回池中。 重要的是,应用模块组成,直接实现了接口,具有约束力的对象期望的数据集 合,内置的操作,和服务方法。这使得绑定的工作变成在其活动的的数据模型中,应用模块和 View 对象的实例的导向。 具体而言,这项互动优化允许: • •

迭代绑定,直接绑定到默认设置连续迭代的默认行 iterator 从任何视图来看对象实例 行动绑定,直接绑定到无论是: • 对应用模块的客户端接口自定义方法 • 应用模块和查看视图对象内置的操作中

图 10-2,说明库管理的作用,并突出发挥应用模块的数据控制应用模块之间的直接联系绑定,例如。

图 10-2 绑定直接连接到视图对象和方法和应用模块从池中

10.3 如何应用模块产生数据控制的调色盘 在设计时,您可以使用数据控制的调色盘,以执行拖放数据绑定为 jsf , JSP / Struts,和 swing 应用。每个应用模块在工作区自动出现在数据控制的调色盘。使用 srservice 应用模块从 srdemo 的应用作为一个例子,您会看到在数据控制的调色盘,当您与自己的应用程序模块的工作。

---------------------------------------------------------------335----------------------------------------------------------------------------


10.3.1 概述了 srservice 应用模块 图 10-3 显示 srservice 应用模块,实现了商业服务层的 srdemo 的应用。通知指出,其数据 模型,包括众多的 View 对象的实例,其中包括几个主/详细的层次。视图层演示组成 jsf 页面的 UI 组件是必然的数据从视图对象实例,在 srservice '的活动的数据模型,在客户端接口之上,内建 在业务和服务方法。在段 6.10, "概述了 srdemo 的页面如何使用 srservice " ,您可以找到一个 概述,正是这方面的应用模块的每一页的用途。 图 10-3 的 srdemo 应用程序的 oracle.srdemo.model.srservice 应用模块 UML 图表

10.3.2 如何改变数据控制的名称,在你开始建立页面之前 默认情况下,一个应用模块将显示在数据控制的调色盘作为数据控制命名 appmodulename datacontrol 。举例来说, srservice 原本的名称 srservicedatacontrol 。要更改默认的数据控 制的名称,以较短的,或者干脆较为可取的姓名,做到以下几点: 要改变应用模块名称��� 1. 2. 3. 4.

开放的应用模块在应用模块编辑器。 打开 Custom Properties 页。 在 Name 单选框中,选择 data_control_name 属性从下拉列表中。 输入您的首选数据控制的名称在值字段并单击确定以关闭该向导。

图 10-4 显示,自定义属性的设置,改变了数据控制的名称,该 srservice 从默认 srservicedatacontrol ,以较短的 srservice 名称名称相匹配的应用模块。您会发现变化,立即 在数据控制的调色盘。

---------------------------------------------------------------336----------------------------------------------------------------------------


图 10-4 设置自定义应用程序模块的属性,以重写数据控制名称

请注意,当您开始绑定数据从应用模块,以您的应用程序的页面或面板,为您的应用模块建 立的数据控制的名称将出现在 databindings.cpx 文件在您的用户界面的项目,并在每个数据绑定 页的定义 XML 文件。此外,您也可以参考的数据控制的名称代码时,需要工作的编程与应用模块服 务接口。基于这个原因,如果您计划的名称更改为您的应用模块,甲骨文建议做这项改变之前,你 正在建设你的 view 层。

注意: 在 JDeveloper 3 中 ,如果您决定要改变应用模块的数据控制的名称之后,你已经参照它 在一个或多个页面,您将需要打开该网页定义文件的地方,这是以新名称手动引用和更新旧名称。 未来版本的 JDeveloper 可以延长其重构的支持,使重命名数据控制简单。 10.3.3 如何将数据模型和服务的方法显示在数据控制的调色盘 图 10-5 说明如何控制调色盘数据显示 View 对象的实例,在 srservice '的积极数据模型。每 个视图对象实例看来,作为命名的数据收集他的名字相匹配的 View 对象的实例名称。注意层次结 构的数据集合,并视图简单化,这个图忽略了一些细节,在树中出现的每一个 View 对象。这些额 外的 View 对象的细节是突出了在段 10.3.6 , “如何查看对象显示在数据控制的调色盘” 。数据 控制的调色盘,反映了主/详细层次根据其掌握的数据收集在您的应用模块的数据模型显示详细的 数据收集嵌套。 数据控制的调色盘,也显示每个自定义方法上的应用模块的客户端接口作为命名的数据控制的 自定义操作的名字相匹配的方法名称。如果一个方法接受的参数,他们出现在一个文件夹作为参数 运行参数嵌套内的运作节点。

---------------------------------------------------------------337----------------------------------------------------------------------------


图 10-5,如何使活动数据模型出现在数据控制的调色盘

10.3.4 如何改变视图,例如名称,在你开始之前建立的页面 当您一开始新增 View 对象,例如向数据模型,如果您还没有输入的一个实例名称自己,它得 到补充,与一个名称组成的看法,对象名称与数字后缀附加。举例来说,加入的一个实例一 servicerequests 视图对象的数据模型,默认视图对象的实例名称将为 servicerequests1 。您可 以轻易地重新命名 View 对象实例中,使用 Data Model 页的应用模块编辑器。 当您开始绑定数据,从数据收集,在数据控制的调色盘,以您的应用程序的页面或面板中,除了数 据控制的名称,数据收集的名字中引用的网页的定义XML文件所用的ADF的模型数据绑定层。你的 视图对象的名字,例如在应用模块的数据模型是用来作为名称,这些数据集合,甲骨文公司建议回 复您的视图对象实例的名称之前,利用他们建立数据绑定的页面,以确保名称是描述。 注意:在JDeveloper 3中 ,如果您决定要改变一个View对象的实例名称后,你已经参照它在一个或 多个页面,您将需要打开该网页定义文件的地方,这是以新名称手动引用和更新旧名称,未来版本 的 JDeveloper 可 以 延 长 其 重 构 的 支 持 , 使 重 命 名 View 对 象 , 例 如 简 单 --------------------------------------------------------------338----------------------------------------------------------------------------


10.3.5 如何转换操作控制在数据控制的调色盘 应用模块的数据控制,暴露了两个数据控制内置在操作命名为 Commit 和 ROLLBACK 显示,在图 10-6 。在运行时,当这些行动所援引的数据具有约束力层,他们代表向 commit( )和 rollback ( )方法,该转换对象,与目前的应用模块,例如。 请注意,该 Operations 文件夹中的数据控 制树忽略了所有的数据收集和自定义的运作一个更精简的视图。

注意:在一个应用模块,与许多 View 对象实例和定制方法,你可能需要向下滚动的数据控制的调 色盘显示,寻找 Operations 的文件夹,这是直接子节点的数据控制。 此文件夹是一个包含其内置 的操作中。

图 10-6 是如何转换控制操作,在数据控制的调色盘

示例 10.3.6 如何查看视图显示在数据控制的调色盘 图 10-7 显示了如何使每个视图对象,例如在应用模块的数据模型出现在数据控制的调色盘。 视图对象属性显示为即时的子节点相应的数据集。如果您已选取的任何自定义的方法,出现在视图 象的客户端接口,如 performsearch ( )方法在图中,它们显示为自定义业务之后跟随视图对象 的属性在同一水平。如果该方法接受的参数,这些出现在嵌套参数文件夹中运行参数。

--------------------------------------------------------------339----------------------------------------------------------------------------


图 10-7 如何查看对象显示在数据控制的调色盘

10.3.6.1 内建操作 View 对象的数据集 显示在图 10-7 , Operations 文件夹下的数据集显示所有可用的内置操作。如果一项行动中 接受一个或多个参数,然后他们出现在嵌套 Parameters 文件夹中。在运行时,当一个人对这些数 据集操作是引用的名称由数据绑定层,应用模块的数据控制调用,以适当的方法对 viewobject 界 面处理内建功能。内置操作分为三类:业务影响当前行,行动刷新数据集,和所有其他操作。 10.3.6.1.1 操作影响当前行 • • • • • • • • •

Create —创建一个新行成为当前行 Delete —删除当前行 First —设置当前行到第一行,在该行成立 Last —设置当前行到最后一行,在该行成立 Previous —设置当前行到前行,在该行成立 Next —设置行到下一行,在该行成立 Previous Set —浏览着一整页的行 Next Set —浏览落后一整页的行 setcurrentrowwithkey -试图认定,连续使用系列化的字串代表行的关键作为参数传递。 如果发现,它就成为当前行。


setcurrentrowwithkeyvalue -试图认定,连续使用主键的属性值作为参数传递。如果发现, 它就成为当前行。

10.3.6.1.2 操作刷新数据集 • •

Execute —刷新数据集(重新)执行 V iew 对象的查询,留下任何约束参数在目前的值。 executewithparams -刷新数据集,首先是指派新的值,以命名绑定变量通过作为参数,然 后(转口)执行视图对象的查询。 注意:该 executewithparams 操作,只会出现视图的对象所定义的一个或多个命名绑定变 量在设计期。

10.3.6.1.3 所有其他操作 • •

removerowwithkey -试图认定,连续使用序列化的字串代表行的关键作为参数传递。如果 发现,该行被删除。 Find —切换“寻找模式”和关闭,以便收集数据

10.3.7 如何嵌套应用模块显示在数据控制的调色盘 如果您建立复合应用模块,包括嵌套的实例其他应用模块,数据控制的调色盘,反映了这一点 构件组装在树的层次。 举例来说,假设说,除了向 srdemo 应用程序的 oracle.srdemo.model.srservice 应用模块,您还设立了下列应用模块在相同的包: • •

一个应用模块命名 productservice ,并更名为它的数据控制 productservice 一个应用模块命名 compositeservice ,并更名为它的数据控制 compositeservice

然后假设您添加了两个 View 对象实例命名 otherviewobject 和 anotherviewobject 向数据模 型 compositeservice ,并就 Application Modules 页的应用模块编辑器,您已经加入的一个实例 该 srservice 应用模块和一个实例的 productmaintenance 应用模块重用他们的一部分 compositeservice 。 图 10-8 说明了如何将您的 compositeservice 显示在数据控制的调色盘。嵌 套实例 srservice 和 productservice 出现在调色板树显示嵌套内的 compositeservice 数据控制。 整个数据模型和客户端的方法,嵌套应用模块实例展露给客户的自动可用部分的 compositeservice,重用他们。 其中一个可能混淆的一点是,即使您已再用嵌套实例 srservice 和 productservice 内 compositeservice , srservice 和 productservice 应用模块也会出现自己的顶部的数据控制节点 在调色板树。在 Jdeveloper 中,假设您可能会想要有时会使用 srservice 或 productservice 对自 己作为一个单独的数据控制,从 compositeservice ,因此它显示所有他们三个。你必须小心执行 您的拖放数据绑定,从正确的数据控制。如果您想您的页面使用一个视图对象实例,例如,从嵌套 srservice 实例的数据模型这是一个大略的一部分,该 compositeservice 数据控制,然后确保在调 色盘该 compositeservice 数据控制节点为您选择的数据集出现的一部分。


--------------------------------------------------------------341----------------------------------------------------------------------------

图 10-8,如何嵌套应用模块显示在数据控制的调色盘

这是很重要的做拖放操作对应您打算使用。当您在在调色盘的下拉数据集从高层 srservice 数据控制节点,在运行时您的页面将使用该 srservice 应用模块的一个实例获得了一批 srservice 组件。当您从嵌套实例 srservice 的下拉数据集,这是一部分 compositeservice ,在运行时您的 网面将使用该 compositeservice 应用模块的一个实例获得了一批 compositeservice 组件。由于不 同类型的应用模块的数据控制,将有明显的转换和数据库连接,不经意地混合和匹配的数据收集工 作无论从嵌套应用模块还是高层的数据控制,将导致意想不到的运行时的行为。 会预先得到警告。

10.4 如何在一个网页上添加创建按钮,

添加创建按钮,拖放操作,从数据控制的调色盘上的创建一个数据集到 JSP 网页或 swing 面 注意: 在一个基于 Web 的应用内置创造的操作行为不同于在一个 swing 为基础的桌面应用程序。 在 Web 应用程序,根据情况,您可能要使用 createinsert 而不是创造。


10.4.1,你删除一个创建网页上的按钮时会发生什么 如果你从数据控制的调色盘上的拖动一个创造操作数据集到一个 JSP 页面-无论是使用或不 j sf-您将获得一个创建按钮 ,在您的网页上,这个按钮是以内置创造操作声明约束。创建操作创建 一个新的行为集数据,但不插入到据收集的行 set 里。 创建时不插入新行到该行,它有助于避免 不必要的空白连续出现在其他的网页,可以避免用户浏览在您建立网页之前,或者,为新行进入任 何数据。当您行使了创建操作中,迭代约束暂时点以这个新行,如果它当前行。 当用户成功地提 交数据的属性到新行,新行插入到该行。这是声明连续创造的方法,最适合大部分 Web 应用程式的 使用。 10.4.2,你放弃创造的操作在 SWING 面板上时会发生什么 如果你拖动一个创造操作的数据集从数据控制的调色盘上到 swing 面板上,您将获得一个创建 按钮,您的面板就是以声明的约束,以一个内置的操作,而不是所谓的 createinsert 。 该 createinsert 操作,创建一个新的行中的数据集和插入新行到当前行集之前,。createinsert 是 最好的方法,为 swing 应用。 10.4.3 何时使用 createinsert 而不是 Create 有某些情况下在一个 Web 应用程序的地方,您需要使用 createinsert 而不是 Create 。当创建 时使用 createinsert: • • •

编辑表的控制 一个表与一个单一的,目前修改的行 主/详细资料网页,并希望新成立的主行正确显示,没有存在的详细列

createinsert 时使用的一种新的连续插入。因为只有一个 Create 操作结果表明,在数据控制 的调色盘,使用 createinsert 操作,在 Web 应用程序涉及一个额外步骤。 如何改变一个创建操作,以 createinsert : 1. e 下拉 Create 的操作,从数据控制的调色盘上您的页面 2. 选择按钮,在可视化编辑器,并选择 Edit Binding...从上下文菜单 3. 在 Action Binding Editor 使用 Select an Action 下拉列表中改变操作绑定,使用 Create 代替 createinsert。 当您使用 Create 或 createinsert 操作,以声明建立一个新的行,显示下面的代码行: // create a new row for the view object Row newRow = yourViewObject.createRow(); // mark the row as being "initialized", but not yet new newRow.setNewRowState(Row.STATUS_INITIALIZED);


此外,如果您使用的 createinsert 操作,它执行额外的代码行插入数据行到该行集: // insert the new row into the view object's default rowset yourViewObject.insertRow(newRow); --------------------------------------------------------------343----------------------------------------------------------------------------

当您创建一个连续在一个实体为基础的 View 对象,相关到目前的应用模块的 Transaction 对 象,会立即注意到这一事实。新的实体,连续获得创建后台的视图,已经是连续 Transaction 的一 部分,有待改变。 当一个新创建的行标示为有初始化的集合,这是从 Transaction 之前,更改名 单和被认为是一个空白列,在其中最终用户尚未进入的任何资料值。term 初始化将使最后的用户将 看到新行初始化与任何预设值,后台的实体对象的定义。如果用户从来没有进入任何数据到任何属 性即初始化行,那是因为如果行根本不存在。在 transaction 提交的时候,因为这行是不是 Transaction 的一部分,没有为他声明插入。 尽快至少一个属性在初始化行设置,它会自动转换,从初始化状态到新状态 ( row.status_new ) 。在那个时间,基本实体,连续获得登记,在 Transaction 中未决改变的 名单之前的变化,以及新行会被永久保存在您下一次 commit 的 transaction。

注意:如果最终用户执行步骤,同时使用您的应用程序,结果在创建许多初始化行,但他们从 来没有填充,它似乎像一个食谱,一个缓慢的内存泄漏。不过不用担心,使用的内存初始化行从来 没有过渡到新的状态,最终会由 Java 虚拟机的垃圾收集。

10.5 应用模块与资料的提示和技巧 10.5.1 如何创建一个记录,状态显示器 当创建页面,您往往要显示某种形式的记录,状态指示器,例如: “记录 5 25 ” 。如果您 多行显示在一个网页上,那么您可能也想展示的一个不同的,如“记录, 5 月 10 日 25 ” 。您可 以建立一个创建记录的指标,像这样用简单的文字组成,其中每显示一个适当的值从 1 迭代约束或 绑定 el 约束的表。 该迭代约束的 rangesize 属性的定义很多行的每页列数,它可用来显示在用户 界面中。如果您的页面的定义包含一个迭代约束的命名 someviewiter 或一张表有约束的命名 someview ,您可以参考 el 表达式: •

行数每页

#{bindings.SomeViewIter.rangeSize} #{bindings.SomeView.rangeSize} •

共行

#{bindings.SomeViewIter.estimatedRowCount}


#{bindings.SomeView.estimatedRowCount}

--------------------------------------------------------------344----------------------------------------------------------------------------

第一行对当前页

#{bindings.SomeViewIter.rangeStart + 1} #{bindings.SomeView.rangeStart + 1} •

最后一行对当前页

#{bindings.SomeViewIter.rangeStart + bindings.SomeViewIter.rangeSize} #{bindings.SomeView.rangeStart + bindings.SomeView.rangeSize} •

当前行的数目

#{bindings.SomeViewIter.rangeStart + bindings.SomeViewIter.currentRowIndexInRange + 1} #{bindings.SomeView.currentRowIndex + 1} 10.5.2 如何给命名 View 对象绑定变量 当一个 View 对象已命名绑定变量,额外 executewithparams 操作出现在相应的数据集的 Operations 文件夹中。显示在图 10-9 日 ,这个内置的操作,接受一个参数相应的每一个命名绑定 变量。举例来说, stafflistbyemailnamerole View 对象在图中有 4 个绑定变量- EmailAddress, Role, t hefirstname, t helastname-因此,参数显示为 e x ecutewithparams 操作后,这些相 同的名称。在运行时,当 executewithparams 内置操作是引用的名称由数据绑定层,每一个命名绑 定变量值设定为各自的参数值所通过的具有约束的层,然后视图对象的执行查询以刷新其行的结果 集。

图 10-9 对命名绑定变量使用内置或自订的操作


--------------------------------------------------------------345----------------------------------------------------------------------------

另一种做法,也显示在图 10-9 ,涉及创建一个自定义视图对象的“查找”的方式,你想允许用 户设置,然后包括这个方法在客户端界面的视图对象,在视图对象编辑器。 例子, 10-1 表明,例 如一个自定义的方法的代码将这个样子。这是 stafflistbyemailnamerole View 对象,在 srdemo 的应用。 注意指出,由于应用模块的活动数据模型,该方法并不需要返回数据给客户端。ss 该方 法有一个无效的返回类型,设置绑定变量值的参数值通过成为该方法使用所产生的绑定变量的存取 方法 setemailaddress ( ) , setrole ( ) , setthefirstname ( ) ,和 setthelastname ( ) 。然后,它要求 executequery ( )来执行查询以刷新查看对象的连续的结果集。

例如 10-1 自定义视图对象方法集命名为绑定变量并执行查询 // From SRDemo Sample's StaffListByEmailNameRoleImpl.java public void findStaffByEmailNameRole(String email, String firstName, String lastName, String role) { setEmailAddress(email); setRole(role); setTheFirstName(firstName); setTheLastName(lastName); executeQuery(); } 这两个方法,完成相同的最终结果。他们两个都可以用数据控制的调色盘,以创建一个搜索表 单。The key differences between the approaches are the following:关键的不同的方法如下: • 您不需要编写代码,因为它的的内置功能。


• 您可以下拉的操作,从数据控制的调色盘,以创造一个 ADF 的搜索表单 ,让用户搜

索对象的视图,就命名绑定变量的值。 • 您的搜索领域相应的命名绑定变量继承绑定变量的 UI 控制的提示为他们的标签的文 字,您可以定义对象的一部分的视图。 使用自定义视图对象的“查找”操作 • 你需要写一小自定义代码,但您看到一个高层的操作,在数据控制的调色盘,其名称

可以帮助清晰您的意图,寻找操作。 • 您可以下拉的本地操作,从数据控制的调色盘,以创造一个 ADF 的搜索表单 ,让用 户搜索对象的视图和参数值。 • 您的搜索领域的相应的方法,输入参数不继承标签文字的 UI 控制提示您定义在视图 对象的命名绑定变量本身。相反,您需要界定的用户界面控制他们的提示标签文字, Properties...选择从上下文菜单中的网页的定义变了相应的方法的说法显示,在 图 10-10 。 图 10-10 访问性能页的定义变量设置的用户界面控制的提示

总之,应该很好的掌握这两种方法,您可以自行决定自己的做法,其中你喜欢。 第一节 所述的 10.6.5 , "The SRStaffSearch Page" ,而stafflistbyemailnamerole视图对象包含上述 findstaffbyemailnamerole ( )自定义的方法为教育目的,演示的jsf页使用的声明 executewithparams内建在操作上。 10.5.3 如何使用查找模式实现查询按例子 在 ADF 模型层,一个有约束的迭代支持的一个特点所谓的"寻找模式" ,当工作与数据收集工作, 支持查询按例子。作为列举在第 5.8 "Filtering Results Using Query-By-Example View Criteria",查看对象支持查询按按例子,当视图准则列一套已应用标准视图行。 视图标准,列有 相同的结构,列在视图对象,但数据的每个属性是字符串 ,让标准像" > 304 "或"IN (314,326) " 输入搜索标准。


在支持寻找模式功能,一个有约束的迭代有一个布尔值 findmode 属性默认为是假 。显示在图 10-11 ,当 findmode 是假,迭代约束点,迭代器绑定到设置容器的数据。相反的,当您设定 findmode 为真,该迭代约束的开关点在该行的一套视图的标准行。图 10-11,当找到模式为真,有约束的迭 代点,收于 viewcriteria 行 set,

在有约束的层,action 绑定认为,引用内置的数据控制操作是与迭代具有相关的约束的页面的 定义元数据。 这使具有约束的层应用于一项操作中一样, Create 或 Delete ,例如到正确的数据 收集在运行时。当一项操作一样, Create 或 Delete 是引用一迭代约束已 findMode 设置为 false, 这些操作会影响该行设定载数据。相反,如果该操作是引用一迭代约束已 findmode 设置为 true , 那么它们影响到该行设置载有鉴于准则,连续,有效地建立或删除一列查询按例如准则。 内置操作让您切换迭代约束的 findmode 标志位。如果一个迭代约束的 findmode 是 false ,引 用找到的操作为迭代约束的设定标志位为 true 。 在标准列集连续的视图.该 UI 组件是必然的属性 绑定与此相关的迭代开关相应的显示当前视图的准则。如果用户修改的值 UI 组件,而其相关的迭 代是在寻找模式,在视图标准的值应用到视图准则列,连续设置。如果您引用 Execute 或 executewithparams 内置操作,一个迭代是在寻找模式下,每个会先找到切换模式为 false,适用 于寻找模式的标准,并刷新数据集。 在视图准则的行订定,如果一个迭代约束的 findmode 是 true 的引用 Find 它的操作设置为 false ,并移除视图准则的行。这实际上取消了查询按例如模式。 10.5.4 如何自订页 ADF 的生命周期工作,编程与绑定 ADF 控制器层整合 jsf 页的生命周期与 ADF 的模型数据绑定层。您可以自订,这无论是在全局还 是您的整个应用程式,或对每个网页。本节着重一些提示有关如何 srdemo 的应用表明,使用这两 项技术。 10.5.4.1 全局内自订页 ADF 的生命周期 以全局范围内自订页 ADF 的生命周期,做到以下几点: • •

创建一个类继承 oracle.adf.controller.faces.lifecycle.facespagelifecycle 创建一个类继承 adfphaselistener 和重写于 createpagelifecycle ( )方法返回的一个 实例您的自定义页面生命周期的类。


变更您的 faces-config.xml 文件中使用您的子 adfphaselistener 而不是默认的 adfphaselistener 。 显示,在图 10-12 ,你可以这样做就 Overview 标签的的 JDeveloper faces-config.xml 编辑器,在 Life Cycle 的类别。 注意: 请务必将取代现有的 adfphaselistener 与您的自定义子 adfphaselistener ,或一 切,在 jsf / ADF 的生命周期协调会发生两次!

该 srdemo 的应用,包括 srdemopagelifecycle 类全局重写于 reporterrors ( )方法,该网 页的生命周期要更改默认的方式,异常捕获和缓存由 ADF 的模型层据记录 jsf 。改变执行减少异 常报告给用户,使其只包含异常,他们可以直接采取操作,制止更多的“包装”的异常情况将不会 作出多大意义,给最终用户。 图 10-12 设置一个自定义 adfphaselistener 安装的自订网页,全局生命周期

10.5.4.2 定制网页的生命周期为单页 您可以自定义生命周期的单一网页设置 controllerclass 属性的 pagedefinition 找出一个类,无 论是: • •

继承 oracle.adf.controller.v2.pagecontroller 类 实现 oracle.adf.controller.v2.pagephaselistener 接口

网页定义的 controllerclass 属性可以是: • •

完全合格的类名称 一个 EL 的表达式一个类符合上述要求

用 el 表达式为值的 controllerclass ,是有可能的指定名称的自订网页,控制器类(或网页 listener 的执行情况) ,您已经设定作为一个管理的 bean ,在 face- config.xml 文件中。 这 包括后台 bean 为 jsf 页,只要它不是继承 pagecontroller 或实现 pagephaselistener 。10-13, 说明了如何选择的根节点该网页的定义,在结构上的窗口设置。 图 10-13controllerclass 一个网页的定义


注意: 当使用 el 表达式为值的 controllerclass 属性,结构窗口可能会显示警告说, “ # ( yourexpr ession ) ”是不是一个有效的类。您可以放心地忽略此警告。

10.5.4.3 使用自订页 ADF 的生命周期援引 onpageload 后台 bean 方法 该 srdemo 应用包含一个 onpageloadbackingbeanbase 类,在 oracle.srdemo.view.util 的实 现 pagephaselistener 界面上文所述的使用代码一样的表现在,例如 10-2 日 。这个类实现了接口 的 beforephase ( )和 afterphase ( )方法,以便在引用一 onpageload ( )方法之前,正常 的 ADF 的准备模型的阶段,一 onpageprerender ( )方法后,正常 ADF 的准备。

--------------------------------------------------------------349----------------------------------------------------------------------------


例如 10-2 pagephaselistener 援引 onpageload ( )和 onpageprerender ( )方法 // In class oracle.srdemo.view.util.OnPageLoadBackingBeanBase /** * Before the ADF page lifecycle's prepareModel phase, invoke a * custom onPageLoad() method. Subclasses override the onPageLoad() * to do something interesting during this event. * @param event */ public void beforePhase(PagePhaseEvent event) { FacesPageLifecycleContext ctx = (FacesPageLifecycleContext)event.getLifecycleContext(); if (event.getPhaseId() == Lifecycle.PREPARE_MODEL_ID) { bc = ctx.getBindingContainer(); onPageLoad(); bc = null; } } /** * After the ADF page lifecycle's prepareRender phase, invoke a * custom onPagePreRender() method. Subclasses override the onPagePreRender() * to do something interesting during this event. * @param event */ public void afterPhase(PagePhaseEvent event) { FacesPageLifecycleContext ctx = (FacesPageLifecycleContext)event.getLifecycleContext(); if (event.getPhaseId() == Lifecycle.PREPARE_RENDER_ID) { bc = ctx.getBindingContainer(); onPagePreRender(); bc = null; } } public void onPageLoad() { // Subclasses can override this. } public void onPagePreRender() { // Subclasses can override this. } 如果有管理的 bean,扩大 onpageloadbackingbeanbase 类,那么它可以被用来作为 ADF 的网页 相 listener,因为它继承了实现这一界面从基类。如果后台 bean,然后压倒一方或双方的 onpageload ( )或 onpageprerender ( )方法,该方法将援引 ADF 页的生命周期,在适当时候 在页面请求的生命周期。 最后一步,在获得这样一个后台 bean 工作,是要告诉 ADF 的网页的


定义,使用后台 bean 作为其网页控制器该网页。如上文所述,这样做是通过设置 controllerclass 属性在网页上的定义问题,以 el 表达式评价,以后台 bean。 该 srmain 页,在 srdemo 应用程序使用中描述的技术在本节中,说明写作的包代码,在 onpageload ( )方法的 srmain 后台 bean,在 oracle.srdemo.view.backing 包。 因为后台 bean 命名 backing_srmain 在 face- config.xml 中 , controllerclass 属性的 srmain 网页的网页的定 义是设定为的 El 表达“ # ( backing_srmain ) ” 。 10.5.5 如何使用刷新正确 invokeaction 和迭代绑定 图 10-14,说明如何 jsf 页的生命周期涉及到继承网页处理阶段,该 ADF 的网页生命周期的增 加。您可以使用刷新属性的迭代绑定和 invokeaction 可执行文件在您的网页中的定义来控制时, 每个评估期间, ADF 的网页生命周期,无论是在 preparemodel 阶段, preparerender 阶段,或两 者兼而有之。由于任期的“刷新”是不完全的第二性质,本节阐明了它的意思为每种可执行文件, 以及如何你应该设置其刷新的属性,以达到正确的行为,您所需要的。

10-14,如何使 jsf 页的生命周期与 ADF 页生命周期阶段有关

10.5.5.1 正确配置刷新属性迭代绑定 在实践中,工作时,与迭代绑定 View 对象实例中,一应用模块,您可以简单地默认设置 Refresh=" ifneeded " 。 您可能会补充,这与布尔值 el 表达式,在 refreshcondition 属性有条件地避免 刷新迭代如果想要的。但是,您仍然可以要求自己, "是什么令人耳目一新的迭代约束" ? 迭代约束,因为它顾名思义,是一份有约束的指向一个迭代。可扩展性的原因,在运行时该迭代 绑定在有约束的容器释放任何参考他们要连续设置迭代在每个要求的最后。在未来的要求,迭代绑 定再次刷新点,在“活”列设置迭代是跟踪当前行的一些数据集。该法的令人耳目一新的一 ADF 的 迭代约束的期间, ADF 的一页,正是生命周期的操作进入连续设置迭代,以 reunite 的约束,以该 行设置迭代,以它的约束。


如果一个迭代约束的是没有刷新,在生命周期,这是不指向任何连续设置迭代为这一请求。这 个结果在值绑定有关该迭代约束没有任何要显示的数据。这可能是一个理想的结果,例如,如果您 想要一个网页就像一个搜索页,初步显示没有数据。要做到这一点,您可以使用 refreshcondition : #{adfFacesContext.postback == true} 该 adffacescontext.postback 布尔属性评估,当一个网页首先提供的 false 时,或所提供的, 由于导航到了另一页。它评价为 True 时,对最终用户具有互动与一些用户界面控制在网页上,造 成了 postback 该网页,以处理事件。通过使用此表达作为 refreshcondition 为某一迭代约束,但 它会重新整理迭代约束,只有当用户交互与控制在网页上。有效值刷新属性一个迭代约束的内容如 下。如果你想刷新迭代,当: • • •

preparemodel 和 preparerender 阶段,使用 Refresh = “ ifneeded ” ( default ) "仅在 preparemodel 阶段,使用 Refresh = “ preparemodel ” "仅在 preparerender 阶段,使用 Refresh = “ rendermodel ”

如果您只希望迭代约束刷新时,您自己的代码调用 getrowsetiterator ( )对迭代的约束, 设置 Refresh = “never ” 。 其他值的刷新不是没有相关的迭代绑定,或保留供以后使用。 10.5.5.2 令人耳目一新的一迭代约束不强行重新执行查询 这是很重要的要明白,工作时,与迭代绑定一个主题相关的 View 对象,例如在一个应用模块, 令 人耳目一新的一迭代约束不强行重新执行其查询每一次。第一时间的视图,对象实例的行设置迭代 访问期间,特定用户的单位的工作,这将含蓄地执行视图对象的查询,如果不是已经执行枪决。随 后耳目一新的迭代具有约束的有关这一观点的对象,例如对网页请求属于同一逻辑单元的工作将只 能使用一套连续迭代再次,而不是强行重新执行查询。如果您希望,重新执行查询以刷新其数据, 使用 Execute 或 executewithparams 内建在操作中,或以编程方式调用 executequery ( )方法对 迭代约束。 10.5.5.3 正确配置刷新属性 invokeaction 可执行文件 几个页面的定义,在 srdemo 应用使用的声明 invokeaction 约束的触发无论是内建在操作或自订的 操作在这一继承 ADF 的页处理生命周期。 每个 invokeaction 有一个 ID 属性,让大家有约束的名 称,然后其他三个性能的利益: • •

该具约束的属性控制什么 invokeaction 其值的名称是一个具有约束的操作或方法的操作 具有约束的,在同等约束的容器。 刷新属性管制时, invokeaction 将引用的操作具有约束 ADF 的网页生命周期的: • preparemodel 阶段,使用 Refresh = preparemodel • preparerender 阶段,使用 Refresh = rendermodel • preparemodel 和 preparerender 阶段,使用 Refresh = ifneeded 该 refreshcondition 属性可以用来控制是否会发生 fire,所有 它的值,如果供应,是一 个布尔值 el 表达式。如果表达的评价,以真正当 invokeaction 被认为是在网页上的生命 周期,相关的操作是有约束的引用。如果评估,以 false 的 ,那么有约束的操作是不引用。

--------------------------------------------------------------352----------------------------------------------------------------------------


注意: 其他的值刷新属性不能形容这里是保留供以后使用。

通知在图 10-14 指出,关键的区别 ADF 的 preparemodel 阶段和 preparerender 阶段是一个在 先, jsf 的 invokeapplication 阶段。自 jsf 的 invokeapplication 阶段,是当 listener 的 fire 操作,如果您需要您的 invokeaction 触发后,这些操作的 listener 表现他们的处理,您将要使用 的 Refresh = “ rendermodel ”设置它。 如果 invokeaction 结合的方法,操作有约束的接受参数,然后两个附加的值可以提供为刷新属性: preparemodelifneeded 和 rendermodelifneeded 。这些具有相同的含义,作为他们的共同设定, 而不* ifneeded 后缀,除非他们执行的优化比较,目前的一套评估参数值与设定被用来引用的方法, 有约束的操作前时。如果参数值为当前调用是完全一样,那些以前使用的, invokeaction 不援引 其约束方法的操作具有约束。 注意: 默认值 Refresh 属性是 ifneeded 。这意味着,如果你不供应 refreshcondition 表达,以 进一步完善其 firing,相关的操作有约束的将援引两次在每个请求。甲骨文建议,无论是加入一个 适当的 refreshcondition 表达(如果您想要评估期间,这两个阶段)或更改默认刷新设置 invokeaction 绑定要么 preparemodel 或 rendermodel ,根据您是否想要您的 invokeaction 发生 之前或之后 jsf invokeapplication 阶段。

10.5.6 理解 setcurrentrowwithkey 和 setcurrentrowwithkeyvalue 之间的差别 您可以调用 getkey ( )方法从任何角度来看连续获得的一个关键的对象封装一个或多个关键 属性,查明该行。正如您所看到的在各个例子,您也可以使用一个关键的对象是这样找到的视图列 在一排设置使用 findbykey ( ) 。在运行时,当要么 setcurrentrowwithkey ,或 setcurrentrowwithkeyvalue 内建在操作是引用的名称由数据绑定层, findbykey ( )方法是用 来寻找该行,通过值在作为一个参数设置之前,发现列当前行。 混淆的,正如所显示的图 10-15 日 , setcurrentrowwithkey 和 setcurrentrowwithkeyvalue 操作,都期望一个参数命名为 rowkey ,但他们的不同,正是由每一个期望 rowkey 参数值要在运行 时:

--------------------------------------------------------------353----------------------------------------------------------------------------


setcurrentrowwithkey

setcurrentrowwithkey 预计 rowkey 参数值要序列化的字串,连续的键。这是一个十六进制 编码字符串看起来像这样: 000200000002C20200000002C102000000010000010A5AB7DAD9

序列化的字串代表的一个关键编码的所有关键属性可能包括以期连续的关键在一个方式可以 方便地通过了作为一个单一的值在浏览器中的 URL 字符串或形式参数。在运行时,如果您不小心通 过一个参数值是不是一个合法序列化字符串的关键,您可能会收到的异常一样, oracle.jbo.invalidparamexception 或 java.io.eofexception 作为一个结果。在您的网页,您就 可以使用的值,序列化的字串键一排由参照 rowkeystr 属性 ADF 的控制具有约束的(例如# ( bindings.someattrname.rowkeystr ) ) ,或连续变量的一 ADF 界面表(如# ( row.rowkeystr ) ) 。 setCurrentRowWithKeyValue setcurrentrowwithkeyvalue 预计 rowkey 参数值要字面的值代表的键的视图行。 举例来说, 它的值将是简单的“ 201 ”来查找服务请求的数目 201 。 10-15,该 setcurrentrowwithkeyvalue 操作,预计字面属性值为键

注意: 如果您写的自定义代码在一个应用模块工人类和需要找到一个连续的基础上,序列化的字 串的关键通过从客户端,您可以使用 getrowfromkey ( )方法,在 jboutil 类,在 oracle.jbo.client 包: static public Row getRowFromKey(RowSetIterator rsi, String sKey) 通过查看对象,例如在您想要找到一行作为第一个参数,序列化字符串格式的键,作为第二个参 数。

--------------------------------------------------------------------354----------------------------------------------------------------------------


10.5.7 了解捆绑异常模式 应用模块提供了一个功能叫做捆绑异常模式,使 Web 应用程序很容易,目前最大的一套失败验 证这些异常情况向最终用户,而不是只介绍第一的错误。默认情况下, ADF 的业务组件应用模块池, 使捆绑异常模式的 Web 应用程式。 您通常不需要改变这个默认设置。 不过,这是重要的是要了解,这是默认启用,因为它的效 果如何验证的异常是抛出。 由于 Java 语言和运行时只支持投掷一个单一的异常,对象,方式,捆 绑验证的异常是实现是由包装一套异常详情一种新的”父类” ,除了包含他们。 举例来说,如果 有多个属性在一个单一的实体对象的属性失败级别的验证,那么,这些多重 validationexception 对象将包裹在一个 rowvalexception 。这个包装异常载列的关键,该行已无法验证。在 transaction commit 的时间,如果多行不顺利通过审定期间执行的承诺,那么所有的 rowvalexception 对象将获 得裹在一并附 txnvalexception 对象。 写作时的自定义错误处理代码,说明所重写 reporterrors ( )方法,在 srdemopagelifecycle 类,在 srdemo 应用,您可以使用 getdetails ( )方法的 jboexception 的异常处理类以递过程中 捆绑的异常。

注意: 所有异常类,这里所指的是,在 oracle.jbo 的包。

10.6 概述如何 srdemo 的页面使用 srservice 本节提供了一个简要概述了如何在每个网页,在 srdemo 应用程序使用 srservice 应用模块的 视图,对象实例和服务方法。 10.6.1 该 srlist 页 10.6.1.1 概述了数据绑定,在 srlist 页 10-16,说明了数据绑定为 srlist 页。 迭代绑定,以查看对象的实例

servicerequestsbystatusiterator 为 servicerequestsbystatus 视图对象实例 网页的定义变数 None 无 操作绑定内建 setcurrentrowwithkey , executewithparams 关系到 servicerequestsbystatusiterator 迭代约束 方法操作绑定到自定义业务 None 无 定制页生命周期 None 无 --------------------------------------------------------------------355----------------------------------------------------------------------------


图 10-16 视图对象 srlist 页

10.6.1.2 为 srlist 页 Business Service Notes 对象实例

servicerequestsbystatus 是一个实例的实体为基础的视图 servicerequestsbystatus 对象,它扩展了 servicerequests 对象的视图,并增加了一个命名绑定变量所谓 statuscode 。 Application Module Custom Methods 应用模块的自定义方法

None 毫无

10.6.2srmain 页面 10.6.2.1 概述了数据绑定,在 srmain 页 10-17,说明了数据绑定为 srmain 页。 迭代绑定,以查看对象的实例 • servicerequestsiterator 为 servicerequests 视图对象实例 • servicehistoriesiterator 为 servicehistories 视图对象实例

网页的定义变数 None 无 操作绑定内建在操作 • setcurrentrowwithkey , 删除涉及到 servicerequestsiterator 迭代约束 • 创建 deletenewhistory ( Delete 内置)关系到 servicehistoriesiterator 迭代约

束 • Commit 涉及到 srservice 数据控制 --------------------------------------------------------------------356----------------------------------------------------------------------------


方法操作绑定到自定义业务 deleteservicehistorynotes 引用 deleteservicehistorynotes ( )方法对 srservice 客户端接 口 定制页生命周期 None 无

注意: 该的 srmain 后台 bean 为 srmain 页(在 oracle.srdemo.view.backing 包)正在使用中描 述的技术在第 10.5.4.3 , “使用自定义页 ADF 的生命周期援引 onpageload 后台 bean 法” ,以 编程方式完成,同样的,该 sredit 页才做以声明。

10-17 服务的方法和查看对象为 srmanage 页

10.6.2.2srmain 页的商业服务文档 对象实例 • servicerequests 是一个实例的实体为基础的 View 对象 servicerequests 。加入数

据从 servicerequest 实体的使用情况和 3 个额外的参考实体用法:createdbyuser ( 用户实体对象) , assignedtouser ( 用户实体对象) ,和产品 。 该 servicerequests 的 View 对象是联系在一起的主/详细地向 servicehistories View 对象。 • servicehistories 是一个实例的实体为基础的 View 对 servicehistories 。加入数 据,servicehistory 实体的使用情况和额外的参考实体使用 systemuser ( 用户实 体对象) 。它是一个 XML 视图对象,没有自定义的 Java 类。 --------------------------------------------------------------------357----------------------------------------------------------------------------


应用模块的自定义方法 例 10-3 , deleteservicehistorynotes ( )方法删除服务历史,注意行相应的 Key 对象,键 设置通过作为输入参数。 例 10-3deleteservicehistorynotes 方法在 srserviceimpl.java public void deleteServiceHistoryNotes(Set keySet) { if (keySet != null) { ViewObject histories = getServiceHistories(); for (Key k: (Set<Key>)keySet) { Row[] rowToDelete = histories.findByKey(k, 1); if (rowToDelete == null || rowToDelete.length == 0) { throw new JboException("Failed to find row with serialized key" + k.toStringFormat(false)); } rowToDelete[0].remove(); getDBTransaction().commit(); } } } 10.6.3 sredit 页面 10.6.3.1 概述了在 sredit 页数据绑定 10-18,说明了数据绑定,在 sredit 页。 迭代绑定,以查看对象的实例 • servicerequestsiterator 为 servicerequests 视图对象实例 • servicerequeststatuslistiterator 为 servicerequeststatuslist 视图对象实例

网页的定义变数 操作绑定内建

• setcurrentrowwithkey 关系到 servicerequestsiterator 迭代约束 • Commit 涉及到 srservice 数据控制

方法操作绑定到自定义业务 canceleditstocurrentservicerequest 引用 canceleditstocurrentservicerequest ( ) 对 srservice 客户端接口 定制页生命周期


setrowtoeditfromrequestparameter 调用内置在 setcurrentrowwithkey 的操作模式,在准备阶段 ( Refresh = " preparemodel " ) 时 , 第 一 次 导 航 到 网 页 上 ( 即 不 处 理 postback ) 和 processscope.rowkeystr 属性是没有设定( refreshcondition =${adfFacesContext.postback == false and not empty processScope.rowKeyStr}) 。 10-18 服务的方法和查看对象在 sredit 页

10.6.3.2sredit 页商业服务文档 对象实例 • servicerequests 是一个实例的实体为基础的 View 对象 servicerequests 。加入数

据,servicerequest 实体的使用情况和 3 个额外的参考实体用法: createdbyuser ( User 实体对象) , assignedtouser ( User 实体对象) ,和 Product 。 • servicerequeststatuslist 是一个实例只读 View 对象 servicerequeststatuslist 。 servicerequeststatuslistimpl.java 类,其数据是由一个静态的名单。这个类继 承于 srstaticdataviewobjectimpl ,在 frameworkextensions 项目提供了基本的 支持,实现一个 View 对象的基础上的静态数据。 应用模块的自定义方法 在例 10-4 中, canceleditstocurrentservicerequest ( )方法使用刷新( )方法来取消所 做的修改在目前的 transaction,以目前的服务请求行。 例 10-4canceleditstocurrentservicerequest 方法在 srserviceimpl.java --------------------------------------------------------------------359----------------------------------------------------------------------------


public void cancelEditsToCurrentServiceRequest() { Row svReq = getServiceRequests().getCurrentRow(); if (svReq != null) { svReq.refresh(Row.REFRESH_WITH_DB_FORGET_CHANGES); } } 10.6.4srsearch 页 10.6.4.1 概述了 srsearch 页数据绑定 10-19,说明了数据绑定,在 srsearch 页。 迭代绑定,以查看对象的实例 • searchservicerequestsiterator 为 searchservicerequests 视图对象实例(被迫留

在寻找模式 alwaysfind invokeaction ) 。 • searchservicerequestsresultsiterator 为 searchservicerequests 视图对象实例 • servicerequeststatuslistiterator 为 servicerequeststatuslist 视图对象实例 网页的定义变数 无 操作绑定内建在操作 • Execute , Delete , Create 涉及到 searchservicerequestsiterator 迭代约束 • Find , First , Next , Previous , Last , setcurrentrowwithkey 关系到

searchservicerequestsresultsiterator 迭代约束 方法操作绑定到自定义业务 无 定制页生命周期 • alwaysfind 调用内置在寻找操作(为 searchservicerequestsiterator 迭代有约束

的)无论是准备模型,或使模型阶段( Refresh = “ ifneeded ” )时, searchservicerequestsiterator 是不是在寻找模式($ ( bindings.searchservicerequestsiterator.findmode == false) ) • insertblankviewcriteriarowiftherearenone 调用内置在创建操作中,无论是准备模 型,或使模型阶段( Refresh = “ ifneeded ” )时, searchservicerequestsiterator 是不是在寻找模式( $ ( bindings.searchservicerequestsiterator.findmode == false) )

--------------------------------------------------------------------360----------------------------------------------------------------------------


图 10-19 视图对象为 srsearch 页

10.6.4.2 srsearch 页商业服务注意 对象实例 • searchservicerequests 是一个实例的实体为基础的 View 对象 servicerequests 。

加入数据,从主要 servicerequest 实体的使用情况和 3 个额外的参考实体用法: createdbyuser ( User 实体对象) , assignedtouser ( User 实体对象) 和 Product 。 • servicerequeststatuslist 是一个实例只读 View 对象 servicerequeststatuslist 。 其数据是由一个静态的名单,在 servicerequeststatuslistimpl.java 类。这个类 继承 srstaticdataviewobjectimpl ,在 frameworkextensions 项目提供了基本的 支持,实现一个 View 对象的基础上的静态数据。 应用模块的自定义方法 无 10.6.5 srstaffsearch 页 10.6.5.1 概述了数据绑定,在 srstaffsearch 页 10-20,说明了数据绑定为 srstaffsearch 页。 迭代绑定,以查看对象的实例 stafflistbyemailnameroleiterator 为 stafflistbyemailnamerole 视图对象实例 --------------------------------------------------------------------361----------------------------------------------------------------------------


网页的定义变数 StaffListByEmailNameRole_Role , StaffListByEmailNameRole_EmailAddress , StaffListByEmailNameRole_TheFirstName , StaffListByEmailNameRole_TheLastName stafflistbyemailnamerole_role , stafflistbyemailnamerole_emailaddress , stafflistbyemailnamerole_thefirstname , stafflistbyemailnamerole_thelastname 操作绑定内建在操作 executewithparams 相关 stafflistbyemailnameroleiterator 迭代约束 方法操作绑定到自定义业务 无 定制页生命周期 无 10-20 查看对象在 srstaffsearch 页

10.6.5.2srstaffsearch 页商业服务注意 对象实例 stafflistbyemailnamerole 是一个实例的实体为基础的 View 对象 stafflistbyemailnamerole ,它 扩展了 stafflist View 对象,并增加了名称绑定变量 EmailAddress , Role , thefirstname , thelastname 。 应用模块的自定义方法 无 --------------------------------------------------------------------362----------------------------------------------------------------------------


10.6.6srmanage 页 10.6.6.1 概述了数据绑定在 srmanage 页 10-21,说明了数据绑定在 srmanage 页。 迭代绑定,以查看对象的实例 • staffwithopenrequestsiterator 为 staffwithopenrequests 视图对象实例 • expertiseareasiterator 为 expertiseareas 视图对象实例 • openorpendingservicerequestsiterator 为 openorpendingservicerequests 视图对

象实例 • servicehistoriesforrequestiterator 为 servicehistoriesforrequest 视图对象实

例 网页的定义变数 无 操作绑定内建在操作 setcurrentstaffrowwithkey ( setcurrentrowwithkey 内置)有关 staffwithopenrequestsiterator 迭代约 束 方法操作绑定到自定义业务 setcurrentproblemandassigneerows 引用 setcurrentproblemandassigneerows ( )对 srservice 客户端接口 定制页生命周期 无 10_21 服务的方法和查看对象为 srmanage 页


10.6.6.2 srmanage 页商业服务注意 对象实例 • staffwithopenrequests 是一个 staffwithopenrequests 实例只读 View 对象。它的查

询信息,从 USERS 和 service_requests 表。它是一个 XML 只读视图对象,没有相关 的 Java 类。这一观点的目的是联系主/明细与 expertiseareas 和 openorpendingservicerequests 查看对象。 • expertiseareas 是一个实例的实体为基础的 View 对象 expertiseareas 。加入信息 从初级 expertisearea 实体的使用和参考产品的实体使用。它是一个 XML 只读视图 对象,没有相关的 Java 类。 • openorpendingservicerequests 是一个 openorpendingservicerequests 实例只读 View 对象。查询信息从 service_requests 表。它是一个 XML 只能视图对象,没有 相关的 Java 类。这一视图对象是联系主/明细与 servicehistories View 对象。 • servicehistoriesforrequest 是一个实例的实体为基础的 View 对象 servicehistories 。servicehistory 实体的使用情况和额外的参考实体使用 systemuser ( 用户实体对象)加入数据。它是一个 XML 只读视图对象,没有自定 义的 Java 类。 应用模块的自定义方法 图 10-5 , setcurrentproblemandassigneerows ( )方法使用一个助手方法来设置当前 行,在 staffwithopenrequests 视图对象实例和 openorpendingservicerequests 视图对象 实例的基础上,序列化的字符串键


例 10-5setcurrentproblemandassigneerows 方法在 srserviceimpl.java public void setCurrentProblemAndAssigneeRows(String requestKeyStr, String staffKeyStr) { setRowWithKeyString(getStaffWithOpenRequests(), staffKeyStr); setRowWithKeyString(getOpenOrPendingServiceRequests(), requestKeyStr); } 10.6.7srskills 页 10.6.7.1 概述了数据绑定在 srskills 页 10-21,说明了数据绑定为 srskills 页。 迭代绑定,以查看对象的实例 • stafflistiterator 为 stafflist 视图对象实例 • staffexpertiseareasiterator 为 staffexpertiseareas 视图对象实例 • productlistiterator 为 productlist 视图对象实例

网页的定义变数 无 操作绑定内建在操作 无 --------------------------------------------------------------------364----------------------------------------------------------------------------

方法操作绑定到自定义业务 updateskillsforcurrentstaff 援引 updateskillsforcurrentstaff ( )方法对 srservice 客户端接口 定制页生命周期 无 10-22 服务的方法和查看对象为 srskills 页

10.6.7.2 srskills 页商业服务注意


对象实例 • stafflist 是一个实例的实体为基础的 View 对象 stafflist 。它查询资料,从初级

实体使用 systemuser ( User 实体对象) 。这一 view 对象是联系主/明细与 expertiseareas View 对象。 • staffexpertiseareas 是一个实例的实体为基础的 View 对象 expertiseareas 。加入 信息从初级 expertisearea 实体的使用和参考产品的实体使用。它是一个 XML 只读 视图对象,没有相关的 Java 类。 • productlist 是一个实例的实体为基础的 View 对象 productlist 。它查询的数据从 主实体使用的 Products ( Products 的实体对象) 。它是一个 XML 只读视图对象, 没有相关的 Java 类。 应用模块的自定义方法 在例 10-6 , updateskillsforcurrentstaff ( )方法执行下列步骤: 1. 2. 3. 4. 5.

Clones the list of product ids 克隆名单的产品识别码 创建一个中级 rowsetiterator 做包的迭代超过 expertiseareas 删除的行为当前用户的产品是没有在清单中的产品识别码 关闭中级连续设置迭代时,当迭代时 增加了新的行为的键 提交的事务。

例 10-6updateskillsforcurrentstaff 方法在 srserviceimpl.java public void updateSkillsForCurrentStaff(List productIds) { if (productIds != null && productIds.size() > 0) { // 1. Cone the list of product ids List<Number> copyOfProductIds = (List<Number>)Utils.cloneList(productIds); ViewObject skills = getStaffExpertiseAreas(); // 2. Create a secondary rowset iterator for iteration RowSetIterator rsi = skills.createRowSetIterator(null); // 3. Remove rows for current user for products not in list of products while (rsi.hasNext()) { Row r = rsi.next(); Number productId = (Number)r.getAttribute("ProdId"); // if the existing row is in the list, we're ok, so remove from list. if (copyOfProductIds.contains(productId)) { copyOfProductIds.remove(productId); } // if the existing row is in not list, remove it. else { r.remove(); } } // 4. Close the secondary row set iterator when we're done


rsi.closeRowSetIterator(); // 5. Add new rows for the keys that are left for (Number productIdToAdd: copyOfProductIds) { Row newRow = skills.createRow(); skills.insertRow(newRow); newRow.setAttribute("ProdId", productIdToAdd); } // 6. Commit the transaction getDBTransaction().commit(); } } 注意: 自迭代绑定,在ADF的模型层绑定默认情况下,到默认设置连续迭代为默认集的视图,对 象实例,以他们所相关,它的最佳做法,以创造一个中级连续设置迭代执行包的迭代期对象的连续 设置在您的应用程序的业务逻辑。 This way you do not affect the current row that the user sees in the user interface.通过这种方式,您在不影响当前行认为,用户看到在用户界面中。二次迭代能有 一个开发商指定的名称,但是如果您通过空系统分配它一个名称。 由于您通常会永远关闭尽快您 完成迭代,使用系统分配的名称是好的。 --------------------------------------------------------------------366----------------------------------------------------------------------------

10.6.8srcreate 页 10.6.8.1 概述了数据绑定在 srcreate 页 10-23,说明了数据绑定为 srcreate 页。 迭代绑定,以查看对象的实例 • globalsiterator 为全局视图对象实例 • productlistiterator 为 productlist 视图对象实例

网页的定义变数 无 操作绑定内建在操作 无 方法操作绑定到自定义业务 cancelnewservicerequest 援引 cancelnewservicerequest 方法对 srservice 客户端接口 定制页生命周期 clearservicerequestfieldsifnotintrain援引cancelnewservicerequest方法具有约束的操作 在 准 备 阶 段 模 型 ( Refresh="prepareModel" ) 的 网 页 时 , 是 不 是 处 理 postback 事 件 和 requestscope.processchoice属性是没有设定( RefreshCondition="${adfFacesContext.postbac k == false and emptyrequestScope.processChoice}") 。 10-23 服务的方法和查看对象为 srcreate 页


--------------------------------------------------------------------367----------------------------------------------------------------------------

10.6.8.2srcreate 页商业服务注意 对象实例 • productlist 是一个实例的实体为基础的 View 对象 productlist 。它查询的数据从

主实体使用的 Products ( Products 的实体对象) 。它是一个 XML 只读视图对象, 没有相关的 Java 类。 • Globals . 全局是一个实例瞬态 View 对象全局 。它包含瞬态属性暂时存放信息的 ProductID , ProductName ,和 productdescription 跨页 注意:短暂的 View 对象是一个没有 SQL 查询和所有的瞬态属性。它可以包含行是 人口要么以编程方式由您的应用模块的业务逻辑以声明或使用操作绑定内建在操作 中, ADF 的模型层。为用户熟悉甲骨文的形式,这是类似什么你知道作为一个“非 数据库块”的形式。

应用模块的自定义方法 在 10-7 , cancelnewservicerequest ( )的方法,确保有一个单一的空白行,在全局视图 对象实例。 例 10-7cancelnewservicerequest 方法在 srserviceimpl.java


public void cancelNewServiceRequest() { ViewObject globals = getGlobals(); globals.clearCache(); globals.insertRow(globals.createRow()); } 10.6.9srconfirmcreate 页 10.6.9.1 概述了数据绑定在 srconfirmcreate 页 10-24,说明了数据绑定为 srconfirmcreate 页。 迭代绑定,以查看对象的实例 • globalsiterator 为全局视图对象实例 • loggedinuseriterator 为 loggedinuser 视图对象实例

网页的定义变数 无 操作绑定内建在操作 无 方法操作绑定到自定义业务 • cancelnewservicerequest 援引 cancelnewservicerequest ( )对 srservice 客户

端接口 • createservicerequest 援引 createservicerequest ( )对 srservice 客户端接口 定制页生命周期 无 --------------------------------------------------------------------368----------------------------------------------------------------------------

10-24 服务的方法和查看对象为 srconfirmcreate 页


10.6.9.2srcreate 页商业服务注意 对象实例 • Globals 是一个实例瞬态 View 对象 Globals 。它包含瞬态属性暂时存放信息的

ProductID , ProductName ,和 productdescription 跨页。 • loggedinuser 是一个实例的实体为基础的 View 对象 loggedinuser 。它查询的数据 从主实体使用的 Users ( Users 实体对象) 。该 loggedinuser View 对象,是联 系主/明细与 servicerequestsbystatus View 对象。 应用模块的自定义方法 显示,在 10-8 , createservicerequest ( )方法执行以下基本步骤: 1. 2. 3. 4. 5. 6.

获得实体的定义 servicerequest 创建一个新的 servicerequest 实体行( servicerequestimpl 类) 访问当前行,在 Globals 作为一个强类型 globalsrowimpl 设置问题的说明和产品 ID 为新的服务请求 提交事务 返回一个整数,代表数据库触发指派 SR 数为新的服务请求。

--------------------------------------------------------------------369----------------------------------------------------------------------------

例 10-8createservicerequest 方法在 srserviceimpl.java


public Integer createServiceRequest() { // 1. Get the entity definition for ServiceRequest EntityDefImpl svcReqDef = ServiceRequestImpl.getDefinitionObject(); // 2. Create a new ServiceRequest entity row ServiceRequestImpl newReq = (ServiceRequestImpl)svcReqDef.createInstance2(getDBTransaction(),null); // 3. Access the current row in Globals as a strongly-typed GlobalsRowImpl GlobalsRowImpl globalsRow = (GlobalsRowImpl)getGlobals().getCurrentRow(); // 4. Set the problem description and product id for new service request newReq.setProblemDescription(globalsRow.getProblemDescription()); newReq.setProdId(globalsRow.getProductId()); // 5. Commit the transaction getDBTransaction().commit(); // 6. Return an integer representing the database-assigned SR Number return newReq.getSvrId().getSequenceNumber().intValue(); } --------------------------------------------------------------------370----------------------------------------------------------------------------

Part III contains the following chapters: ■

Chapter 11, "Getting Started with ADF Faces"

Chapter 12, "Displaying Data on a Page"

Chapter 13, "Creating a Basic Page"

Chapter 14, "Adding Tables"

Chapter 15, "Displaying Master-Detail Data"

Chapter 16, "Adding Page Navigation"

Chapter 17, "Creating More Complex Pages"

Chapter 18, "Creating a Search Form"

Chapter 19, "Using Complex UI Components"

Chapter 20, "Using Validation and Conversion"

Chapter 21, "Adding ADF Bindings to Existing Pages"

Chapter 22, "Changing the Appearance of Your Application"

Chapter 23, "Optimizing Application Performance with Caching"

Chapter 24, "Testing and Debugging Web Applications"

--------------------------------------------------------------------371----------------------------------------------------------------------------


This chapter describes the process of setting up your user interface project to use ADF Faces. It also supplies basic information about creating and laying out a web page that will rely on ADF Faces components for the user interface. The chapter includes the following sections: ■

Section 11.1, "Introduction to ADF Faces"

Section 11.2, "Setting Up a Workspace and Project"

Section 11.3, "Creating a Web Page"

Section 11.4, "Laying Out a Web Page"

Section 11.5, "Creating and Using a Backing Bean for a Web Page"

Section 11.6, "Best Practices for ADF Faces"

11.1 介绍 ADF 界面

甲骨文公司 ADF 界面是一个百分之一百的 JavaServer 界面( jsf )兼容的组件库,提供一套 广泛的增强型的用户界面组件为 jsf 应用的开发。 的基础上, jsf 的 JSR 127 的规格, ADF 界面 组件可用于任何的 IDE 支持 jsf 。更具体地说, ADF 界面工程与 Sun 的 jsf 参考执行 1.1_01 (或 更高版本)和 Apache myfaces 1.0.8 (或更新版本) 。 ADF 界面保证了一致的外观和感觉为您的应用程式,可让您更专注于用户界面的互动比外观和 感觉方便。该构件库支持多语言与翻译的实现,并辅助功能。 ADF 界面还支持多个 HTML ,mobile, 和 Telnet 用户-这意味着你可以建立网页具有相同的组成,无论设备将被用来显示何种页面。 使用部分页绘制的特点 ADF 界面组件,您可以建立互动的网页更新显示,而不需要一个完整的 页面刷新。在未来,甲骨文公司计划提供 render kits,使更先进的使用 Ajax 的技术-的 JavaScript , XML 的,和文档对象模型( DOM ) ,以提供更多的丰富的互联网应用与互动性接近,即桌面式的 应用。 --------------------------------------------------------------------373----------------------------------------------------------------------------

ADF 界面有很多的框架和组件的功能,最需要的 jsf 发展的今天,包括:


• • • • • • • •

部分页的绘制 客户端的转换和验证 r 一个过程的范围,这使得它更容易通过的值从一个网页到另一个 一种混合 state-saving 策略,提供更有效率的客户端状态保存 内置支持,标签和讯息显示,在所有输入组件 s 内置在无障碍的支持组件 支持自定义皮肤 支持移动应用

ADF 界面 UI 组件,包括先进的表栏的排序和行选择的能力,树组件显示数据,层次,颜色和日 期排列的采摘,以及许多其他部件,如菜单,命令按钮,shuttle choosers ,progress meters。 ADF 界面出来- - -框组件简化用户的互动,如输入文件的组成部分为上载档案,并选择输入组件内 置在对话框的支持,导航至中级的 Windows 和返回到原产网页与选定的值��� 如需有关 ADF 界面,请参阅下列资源: •

ADF 界面为核心的标签 http://www.oracle.com/technology/products/jdev/htdocs/partners/addins/exchange /jsf/doc/tagdoc/core/index.html http://www.oracle.com/technology/products/jdev/htdocs/partners/addins/exchange /jsf/doc/tagdoc/core/index.html

ADF 界面的 HTML 标记在 http://www.oracle.com/technology/products/jdev/htdocs/partners/addins/exchange /jsf/doc/tagdoc/html/index.html http://www.oracle.com/technology/products/jdev/htdocs/partners/addins/exchange /jsf/doc/tagdoc/html/index.html

ADF 界面 javadocs 在 http://www.oracle.com/technology/products/jdev/htdocs/partners/addins/exchange /jsf/doc/apidocs/index.html http://www.oracle.com/technology/products/jdev/htdocs/partners/addins/exchange /jsf/doc/apidocs/index.html

ADF 界面开发指南 http://www.oracle.com/technology/products/jdev/htdocs/partners/addins/exchange /jsf/doc/devguide/index.html http://www.oracle.com/technology/products/jdev/htdocs/partners/addins/exchange /jsf/doc/devguide/index.html


当您创建 jsf JSP 页面使用 ADF 界面组件的用户界面和使用 jsf 技术进行网页导航,您还可以 利用的优势,甲骨文的应用程序开发框架( ADF 的甲骨文)使用 ADF 的模型具有约束的能力,以便 在页面组成部分。如需有关数据,控制和 ADF 的模型,请参阅第 1.3 节“ ,声明性的发展与 Oracle ADF 和 JavaServer 界面” 。 --------------------------------------------------------------------374----------------------------------------------------------------------------

11.2 设置一个工作区和项目 JDeveloper提供了应用程序模板,使您能够快速创建工作区和项目结构用适当的技术组合。 srdemo应用程序使用Web应用程序[ jsf , ADF BC]应用程序模板,创建一个项目,并控制和查看 (用户界面)该项目的组件在一个工作区。 选择一个应用程序模板,创建一个新的应用工作区在 JDeveloper: 1. 在应用程序导航栏,右键单击应用程序节点,选择新的应用程序 。 2.

在创建应用程序对话框中,从下拉菜单中选择Web应用程序[JSF, ADF BC]应用程序模板。

你不是必须使用的 JDeveloper 应用程序模板,以创建一个应用程序的工作区,他们所提供的 仅仅是为了您的方便。 有时您可能已经拥有一个现有的 war 文件,需要导入到的 JDeveloper 。

---------------------------------------------------------------375----------------------------------------------------------------------------

在 Jdeveloper 中导入 war 文件到一个新项目: 1. 2. 3. 4.

右键单击您的应用程序工作区在应用程序导航栏中选择新的项目 。 在新的导航中,展开分类树,并选择项目 。 在项目列表中,从 war 文件双击项目。 依照导航指示完成,创造该项目。

11.2.1 当您使用一个应用程序模板创建一个工作区会发生什么


默认情况下, JDeveloper 的项目名称为数据模型的模型 ,该项目为用户界面和控制器 viewcontroller 。您可以重新命名的项目使用文件>重新命名,您创建他们的时候,您可以使用工 具” >管理模板来改变默认名称。

注意:这一章使用的插图和项目名称使用在是 JDeveloper 的默认名称。srdemo 的应用程序,使用 项目名称 userinterface 为 jsf 的视图和控制器组成,项目包含 ADF 的 datamodel 业务组件。srdemo 的应用程序也有额外的应用程序导航(例如, buildanddeploy ) ,其中您手动创建并组织您的 应用程序组成部分到合乎逻辑的文件夹。

图 11-1 显示,当您创建工作区之后,viewcontroller 项目中应用程序导航栏的视图。

图 11-2 显示,JDeveloper 创建的实际的文件夹,在<jdev_home> / jdev / mywork 文件夹中的文 件系统。

举例来说,如果您创建了一个名为 application1 工作区, viewcontroller 文件夹及其子文件夹将 设在<jdev_home> / jdev/mywork/application1 在档案系统中。 当您使用 Web 应用程序[JSF, ADF BC]模板创建一个工作区,Jdeveloper 将为您做如下操作: •

创建一个 viewcontroller 项目,该项目使用 jsf 技术。项目属性包括: o JSP 的标签库 : JSF 核心, JSF HTML 。见表 11-2 。 o 类库 : JSF,Commons beanutils ,Commons Digester, Commons Logging, Commons Collections, JSTL. o 技术范围 : JSF , JSP 和/ Servlets , Java,HTML , XML。


当您在 viewcontroller 项目中时,新 Gallery 会被筛选,以显示标准 Web 技术(包括 jsf ) 在 Web 层的类别。 默认情况下,JDeveloper 使用 JSTL 1.1 和支持 Servlet 2.4 和 JSP 2.0 的 J2EE 1.4 Web 容器。 • • • •

创建一个精简 web.xml 文件在 viewcontroller 项目的/WEB-INF 目录。 见第 11.2.1.1 , “Starter web.xml File"”如果你想知道 Jdeveloper 创建 web.xml 的 详细内容。 创建一个空的 faces-config.xml 文件在 viewcontroller 项目的/WEB-INF 目录。 见第 11.2.1.2 , "Starter faces-config.xml File" 如果你想了解更多有关 faces-config.xml 。 请注意,如果您双击 faces-config.xml 在应用程序导航栏中打开该文件,JDeveloper 创建 一个模型的文件夹中 viewcontroller 文件夹中的文件系统,并增加了 faces-config.oxd_faces 在模型文件夹中。

• •

添加 jsf-impl.jar 在 viewcontroller 项目的/web-inf/lib 文件夹。 创建一个模型项目,该项目使用 ADF 的业务组件技术。如需建立一个可重用层的实体对象 的示范项目,见第 2.5 节, “建立一个层的业务域对象统计表” 。如需建设应用模块和 视图对象,见第 2.6 节, “建设商业服务的案例” 。

11.2.1.1 Starter web.xml文件 JSF 应用程序的配置还取决于组成部分的内容,其 J2EE 应用的部署描述 web.xml 。一个服务 器需要配置 web.xml 文件,它定义您应用程序的一切配置。(除了根上下文的路径,这是应用部署 的时候指定的) 。典型的运行设置包括初始化参数,自定义标签库的位置,和安全设置。 例 11-1 显示 Jdeveloper 为您创建的 starter web.xml 文件。

---------------------------------------------------------------377----------------------------------------------------------------------------


当您第一次创建 jsf 项目的时候,该 jsf 的 Servlet 和 Servlet 的映射配置设置自动添加到 starter web.xml 文件。 • •

Jsf Servlet :jsf Servlet 对应 javax.faces.webapp.facesservlet 类 ,他为用户和容 器初始化并管理请求声明周期。 该配置设置映射 jsf Servlet 的一个象征性的名称。jsf Servlet 的映射: Servlet 的 URL 和 jsf 的 Servlet 的名称。您可以使用路径前缀或后缀的正则表达式。例如,如果您的网 页是 index.jsp 或 index.jspx ,这意味着当网址 http://localhost:8080/srdemoadfbc/faces/index.jsp 或 http://localhost:8080/srdemoadfbc/faces/index.jspx 请求后,激活 jsf 的 Servlet , 并加载/srdemoadfbc/index.jsp 或/srdemoadfbc/ index.jspx 。

编辑 JDeveloper 中的 web.xml,右键单击 web.xml 中的应用程序导航栏,从上下文菜单中选 择属性,打开 Web 应用程序部署描述符编辑器。 如果您熟悉配置元素的名称,您也可以使用 XML 编辑器来修改 web.xml 。

供参考的资料,有关的配置,您可以使用在 web.xml 在你的工作区 ,见第 A.7 节, “ web.xml ” 。 注意: 如果您使用 ADF 的数据管理来创建 databound 网页,新增的 JDeveloper ADF 的具有约束过 滤器和一个 servlet 上下文参数的约束在 web.xml 。 如需详细资讯,请参阅第 12.4 节, “配置 ADF 的具有约束的过滤器” 。

---------------------------------------------------------------378----------------------------------------------------------------------------


11.2.1.2 starter faces-config.xml文件 Jsf 的配置文件是您 jsf 应用程序的资源,如自定义的校验和托管 Beans,并定义所有页到页面 导航规则。当应用程序有任何 jsf 配置文件名,通常 filename 是 faces- config.xml 。 例 11-2 表明,JDeveloper 首先为您创建一个项目包含 starter faces- config.xml 文件,该项目使用 jsf 技术。 小应用程序通常有一 faces- config.xml 文件。如需使用多个配置文件,请参阅第 11.2.3 , “您可能需要了解多个 jsf 配置文件” 。 <?xml version="1.0" encoding="windows-1252"?> <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd"> <faces-config xmlns="http://java.sun.com/JSF/Configuration"> </faces-config>

在 Jdeveloper 中,您可以使用编辑器编辑所有的 faces-config.xml 文件 : • •

JSF 配置编辑器:甲骨文建议您使用 jsf 配置编辑器,因为它提供可视化编辑。 XML 源编辑器:使用源编辑器编辑的文件直接,如果您熟悉与 JSF 的配置元素。

开始使用 JSF 配置编辑器: 1. 在应用程序导航栏中,双击 faces- config.xml 打开该文件。默认情况下,Jdeveloper 的 faces-config.xml 在标签底部显示编辑器窗口。 当创建或修改 JSF 导航规则,甲骨文建议 您使用图模式的 jsf 配置编辑器。 在 JDeveloper 一个图表文件中,它可让您创建和管理 网页的通过图的方式管理 faces- config.xml 。如需关于建立 JSF 导航规则,请参阅第 16 章“ ,加入页面导航” 。 2. 创建或修改配置另外的导航规则,使用概述模式的 JSF 配置编辑器。 在编辑窗口的底部, 选择概况 。 概况和图模式的更新 faces-config.xml 文件。 提示:JSF 允许一个以上的< application >元素声明在一个单独的 faces-config.xml 文件中。例 如在文件里,该 JSF 配置编辑器只允许您编辑的第一个< application > 。为任何其他< application >的节点,您需要直接使用 XML 编辑器编辑该文件.

---------------------------------------------------------------379----------------------------------------------------------------------------


有关的配置,您可以在 faces-config.xml 使用,仅供参考。见第 A.9 , “faces- config.xml ” 。 注意:如果您使用ADF的数据管理建立databound网页, JDeveloper ADF在faces- config.xml中 新增了监听器,第一节 所述的 12.2.3 , “当您使用数据控制的调色盘” 。 11.2.2 您可能需要了解viewcontroller项目 viewcontroller 项目是包括网页和其他资源的 Web 应用程序。默认情况下,的 JDeveloper Web 应用程序模板您选择添加单词“控制器”项目名称,以表明该 Web 应用程序将包括某些档案定义应 用程序的流量或网页导航(控制器)中,除了在网页上自己(检视) 。 注意:概念分开,网页导航,从网页显示的是往往被称为模型 2,从较早前的作风(模式 1 应用程 序管理的网页导航完全符合自己的页面。 在一个模型 2 风格的应用,技术引进了专门的 Servlet 称 为一个网页控制器来处理页面导航活动在运行时。

JDeveloper 将决定您创建网页使用技术的组成部分,您的应用程序将使用 viewcontroller 项目的 网页控制器。该 srdemo 应用程序使用 JSF 的 JSP 建立网页: • •

JSF 提供了一个基于组件的框架,显示动态网页内容。它还提供了它自己的网页控制器来管 理网页浏览。 JSP 技术为 JSF 的用户界面提供了层技术。在 JSP 页面,JSF 组件声明了特殊的 JSP 自定义 标签。

JDeveloper 工具将帮助您轻松地约束 JSF 组件与 Java 对象,从而创造 databound UI 组件。正 如先前所述, viewcontroller 项目包含用户界面。 要在网页中的一项数据模型声明约束的 UI 组 件, viewcontroller 项目必须能够访问数据控制模型。使 viewcontroller 项目访问数据控制,依 赖于示范项目的指定。 当您第一次从数据控制选择器拖放到一个 JSF 页到项目。如果你想设置手 动示范项目,请使用下列程序。 在 JDeveloper 中,设置 viewcontroller 项目为依赖示范项目: 1. 双击 viewcontroller 在应用导航栏,以打开该项目属性对话框。 2. 选择依赖旁边的复选框 ,下一步到 model.jpr 。

---------------------------------------------------------------380----------------------------------------------------------------------------


11.2.3 您可能需要了解多个JSF配置文件 JSF的应用程序可以有一个以上的JSF配置文件。例如,如果您需要个别JSF配置文件为单独的而 且您选择的方案含有自定义组件,您可以为每个工作区或类库创建一个单独的JSF配置文件。 以创建另一个JSF配置文件,仅使用文本编辑器,或使用JDeveloper提供的JSF页向导与配置向 导。 开始JSF页向导与配置向导: 1. 在应用程序导航栏中,右键单击 viewcontroller ,并选择新建 。

2. 在新的窗口中,展开 Web 层 。 选择 JSF 然后双击 JSF 页的向导与配置(所在的 faces -config.xml ) 。 当创建一个自定义组件或其他 JSF 类的 JSF 的配置文件,在类库 Jar 文件中: • • •

该文件命名 faces-config.xml 储存新的文件在/META-INF 目录. 包括您使用来发布您的自定义组件或类的 Jar 包。

这对于含自定义组件和边框的打包的类库是有帮助的。 当创建一个 JSF 配置文件为一个单独的应用: • • •

给文件 faces-config.xml 取一个其他的名称。 储存档案在/WEB-INF 文件夹。 阅读该应用程序的配置的新JSF配置文件的,指定文件的路径使用web.xml中的参数 javax.faces.config_files 。 如果有一个以上的档案,新配置文件名称的参数值是一个 逗号分隔的列表。如果使用JSF页向导与配置向导,选择添加参考web.xml复选框让的 JDeveloper注册新JSF配置文件在web.xml。如果您选择复选框Jdeveloper, 例, 11-3 显示 了如何多个JSF配置文件设置在Web.XML。

这有利于大范围的应用,需要单独配置文件为了不同的领域应用。 Example 11–3 Configuring for Multiple JSF Configuration Files in the web.xml File <context-param> <param-name>javax.faces.CONFIG_FILES</param-name> <param-value>/WEB-INF/faces-config1.xml,/WEB-INF/faces-config2.xml</param-value> </context-param> 任何 JSF 配置文件,无论是命名 faces-config.xml 或没有,必须符合 Sun 的 DTD 中位于 http://java.sun.com/dtd/web-facesconfig_1_x.dtd 。如果您使用向导创建 JSF 配置文件, Jdeveloper 将为你创建。

---------------------------------------------------------------381----------------------------------------------------------------------------


如果某个应用程序使用几个 JSF 配置文件,在运行时认定为 JSF,并加载应用程序的配置设置在下 列顺序排列: 1. 搜索文件名为 meta-inf/faces-config.xml 在任何 jar 文件为应用,并加载每个配置资源 (他们发现是反向的) 。 2. 在该应用程序的 web.xml 文件中搜索为 javax.faces.config_files 参数设置。 3. JSF 然后加载每一个命名文件作为配置资源。 4. 搜索一档名为 faces-config.xml 在 Web-INF 目录,并载入它作为配置资源。 JSF 实例化一个应用类和填充它与设置中发现的各种配置文件。 11.3 创建一个网页 JSF 支持多层技术,使用的 JDeveloper JSP 技术创建 JSF 网页。当您使用 JSF 用 JSP , JSF 的 页面就是 JSP 页面( .JSP )或 JSP 的文件(.jspx ) 。 JSP 的文件格式是良好的 XML 文件, XML 标准提供了许多好处,如对确认一个文档类型。因此,当您建立您的网页使用 ADF 界面组件,甲 骨文建议您使用的 JSP 文件。除非另有说明, JSF 页既指 JSF JSP 页面也指 JSF 的 JSP 文件。 Jdeveloper 有两个方法来创建 JSF 页,出现在您的 viewcontroller 项目中: • •

开始创建 JSF JSP 向导,从 JSF category 在 New Gallery 中。 从组件调节器上拖曳 JSF 页到 faces-config.xml 文件时,文件处于打开状态,并且处于 JSF 配置编辑器的图模式。

第 11.3.1 节 , “如何添加 JSF 页”使用较新的技术。它也介绍了 JSF 导航模型,它可以让 您规划您的应用程序页面,在形式的图表,以确定页面之间导向关系,并创造页面。 11.3.1 如何添加JSF页 甲骨文建议使用 JSF 导航图建立您的应用程序的网页。因为 JSF 导航的视图页也是一个特别有用 的方式,在深入到个别的网页时,您可以在 JSP 中/的 HTML 可视化编辑器中编辑他们。

---------------------------------------------------------------382----------------------------------------------------------------------------


在视图控制器工程中使用JSF导航界面来增加JSF页面: 1. 在应用导航栏双击faces-config.xml,或者从视图控制器上下文菜单选择Open JSF Navigation 菜单打开faces-config.xml文件,来详述ViewController - Web Content - WEB-INF文件夹。 默认情况下,JDeveloper 使用JSF的导航界面Diagram标签打开文件。如果你已经创建视图控制 器工程,导航界面显示的将是一个空白的制图表面,如果打开faces-config.xml还不能看到空 白的制图表面,可以在编辑器底部选择Diagram标签。 2. 在控件组件中,从下拉列表中选择JSF Navigation Diagram,然后选择JSF Page 3. 在你需要显示页面的部分点击图表,在界面中会出现一个带有页面名称标签的页面图像。这个 页面图像被一个黄色警告覆盖——这说明你还没有真正创建这个页面,这仅仅是一个页面的请 求。 4. 为了创建一个新的页面,双击页面图表,使用Create JSF JSP标签。 当第一次在JDeveloper中创建页面时,请确保每一步都按照向导来做。 5. 创建JSF JSP向导的第一步:从jsp类型文件中选择JSP Document (*.jspx)。 6. 输入文件名称,接受默认的目录名称或选择一个新的目录地址。默认情况下,JDeveloper会将 文件保存在文件系统中的/ViewController/public_html目录下。 7. 在向导的第二步,对不使用的自动约束组件保留默认选项。 8. 在向导的第三步,确保下面的库已经添加到Selected Libraries列表中。 ■ ADF Faces Components ■ ADF Faces HTML ■ JSF Core ■ JSF HTML 9. 在剩下页面中接受默认选项,然后点击Finish。 你的新 JSF 页面可以在 JSP/HTML 可视编辑器中打开,在这里,你可以从数据控制组件下拉框中使 用控件组件或数据限制组件来使用 ADF 界面组件开始布置这个页面。 如果你跳转到JSF导航界面(通过上面的faces-config.xml编辑器点击),你会注意到这个页面 图表不再被黄色警告覆盖。 提示:如果你使用新图库中的向导来创建新的 JSF 页面,当设计页面流程的应用时,你可以将他们 从 JSF 导航界面的应用导航中拖出来。

---------------------------------------------------------------383----------------------------------------------------------------------------


11.3.2 当您创建一个JSF页发生什么事 图11–3显示的是,当你完全按照增加JSF页面的向导时,视图控制器工程的应用导航界面。

图11–4 显示的是在JDeveloper创建的在文件系统中<JDEV_HOME>/jdev/mywork 文件夹中真实文 件夹。

在视图控制器工程中通过导航界面创建第一个JSF页面时,JDeveloper会遵循如下步骤: z 将adf-faces-impl.jar包添加到/WEB-INF/lib目录下; z 将下面这些库文件添加到视图控制器工程的特性中: —JSP Tag Libraries:ADF界面组件,ADF界面HTML,参考表11-2; —Libraries:JSP运行,ADF界面运行,ADF公共运行; z 在文件系统中仅创建faces-config.oxd_faces文件,例如: <JDEV_HOME>/jdev/mywork/Application1/ViewController/model/public_html/WEB-INF 目录下,当你在JSF导航页面中设计出且创建了页面流程,该文件会保存所有的类似设计 和注释的页面细节。JDeveloper总是通过相关联的XML文件,即faces-config.xml 来保存 这个文件。faces-config.oxd_faces文件在应用程序或系统导航中是不可见的。 当你开始创建JSF页面时,不管是从JSF导航页面或新的图库中开始创建JSF JSP向导,默认情况 下,JDeveloper创建起动页面时JSF JSP 2.0文件,并且自动的将起动页面中的标签库引入JSF。如 果你在向导的第三步选择添加ADF外观标签库,JDeveloper也同意会在开始页面中引入ADF外观标签 库。例11-4表现了一个起动页面的JSF JSP文档。

---------------------------------------------------------------384----------------------------------------------------------------------------

Example 11–4 通过JDeveloper创建起动JSF JSP文档


<?xml version='1.0' encoding='windows-1252'?> <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:af="http://xmlns.oracle.com/adf/faces" xmlns:afh="http://xmlns.oracle.com/adf/faces/html" <jsp:output omit-xml-declaration="true" doctype-root-element="HTML" doctype-system="http://www.w3.org/TR/html4/loose.dtd" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/> <jsp:directive.page contentType="text/html;charset=windows-1252"/> <f:view> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"/> <title>untitled1</title> </head> <body> <h:form></h:form> </body> </html> </f:view> </jsp:root> 11.3.3 å&#x2026;³äº&#x17D;使ç&#x201D;¨ JSF 导è&#x2C6;ªç&#x2022;&#x152;é?¢ç&#x161;&#x201E;ä¸&#x20AC;äº&#x203A;ç&#x;¥è¯&#x2020; å&#x153;¨JSF导è&#x2C6;ªç&#x2022;&#x152;é?¢ä¸­ï¼&#x152;ä½ é&#x153;&#x20AC;è¦ æ³¨æ&#x201E;?页é?¢å&#x203A;¾æ &#x2021;ç&#x161;&#x201E;æ &#x2021;ç­¾å&#x153;¨é¡µé?¢å??称ç&#x161;&#x201E;å?&#x17D;é?¢æ&#x153;&#x2030;个å&#x2C6;?å§&#x2039;ç&#x161;&#x201E;æ&#x2013;&#x153;线ï¼&#x2C6;/ï¼&#x2030;ã&#x20AC;&#x201A;è¿&#x2122;个å&#x2C6;? å§&#x2039;ç&#x161;&#x201E;æ&#x2013;&#x153;线对äº&#x17D;页é?¢å?¯ä»¥å&#x153;¨å¯¼è&#x2C6;ªç&#x2022;&#x152;é?¢ä¸­è¿?è¡&#x152;æ&#x2014;¶å¿&#x2026;é¡»ç&#x161;&#x201E;ã&#x20AC;&#x201A;å¦&#x201A;æ&#x17E;&#x153;ä½ å&#x2C6; é&#x2122;¤äº&#x2020;è¿&#x2122;个æ&#x2013;&#x153;线ï¼&#x152;JDeveloperä¼&#x161;è&#x2021;ªå&#x160;¨æ·» å&#x160; å®&#x192;ã&#x20AC;&#x201A; ä»&#x17D;JSF导è&#x2C6;ªç&#x2022;&#x152;é?¢é&#x2021;?å&#x2018;½å??æ&#x2C6;&#x2013;å&#x2C6; é&#x2122;¤é¡µé?¢æ&#x2014;¶é&#x153;&#x20AC;è¦ æ³¨æ&#x201E;?ï¼&#x161; z é&#x2021;?å&#x2018;½å??页é?¢ï¼&#x161;å¦&#x201A;æ&#x17E;&#x153;ä½ ä»&#x17D;JSF导è&#x2C6;ªç&#x2022;&#x152;é?¢é&#x2021;?å&#x2018;½å??页é?¢ï¼&#x152;è¿&#x2122;ç&#x203A;¸å½&#x201C;äº&#x17D;ä»&#x17D;ç&#x2022;&#x152;é?¢ä¸­ç§»èµ°å&#x17D;&#x;å??称页é?¢ï¼&#x152;å&#x2020;?ç&#x201D;¨ æ&#x2013;°å??称添å&#x160; ä¸&#x20AC;个页é?¢ã&#x20AC;&#x201A;è¿&#x2122;个页é?¢å&#x203A;¾æ &#x2021;ä¼&#x161;被带æ&#x153;&#x2030;é»&#x201E;è&#x2030;²è­¦å&#x2018;&#x160;ç&#x161;&#x201E;页é?¢å&#x203A;¾æ &#x2021;è¦&#x2020;ç&#x203A;&#x2013;ï¼&#x152;è¿&#x2122;表示è¿&#x2122;个页é?¢ è¿&#x2DC;ä¸?å­&#x2DC;å&#x153;¨ã&#x20AC;&#x201A;å¦&#x201A;æ&#x17E;&#x153;ä½ å·²ç»?å&#x2C6;&#x203A;建äº&#x2020;ä¸&#x20AC;个æ½&#x153;å&#x153;¨ç&#x161;&#x201E;页é?¢ï¼&#x152;è¿&#x2122;个页é?¢å&#x153;¨åº&#x201D;ç&#x201D;¨ç¨&#x2039;åº?导è&#x2C6;ªä¸­ä¿?ç&#x2022;&#x2122;äº&#x2020;å®&#x192;å&#x2C6;?å§&#x2039; ç&#x161;&#x201E;å??称ã&#x20AC;&#x201A; å?&#x152;æ ·ç&#x161;&#x201E;ï¼&#x152;å¦&#x201A;æ&#x17E;&#x153;ä½ å&#x153;¨åº&#x201D;ç&#x201D;¨ç¨&#x2039;åº?导è&#x2C6;ªä¸­å·²ç»?æ&#x153;&#x2030;äº&#x2020;ä¸&#x20AC;个JSF页é?¢ï¼&#x152;ä¸&#x201D;è¿&#x2122;个页é?¢å&#x203A;¾æ &#x2021;å·²ç»?å&#x153;¨å¯¼è&#x2C6;ª ç&#x2022;&#x152;é?¢æ&#x2DC;¾ç¤ºï¼&#x152;å¦&#x201A;æ&#x17E;&#x153;ä½ é&#x153;&#x20AC;è¦ å&#x153;¨åº&#x201D;ç&#x201D;¨ç¨&#x2039;åº?导è&#x2C6;ªä¸­é&#x2021;?å&#x2018;½å??该页é?¢ï¼&#x152;è¿&#x2122;ä¹&#x;ç­&#x2030;å?&#x152;äº&#x17D;å&#x2C6; é&#x2122;¤ä¸&#x20AC;个å&#x2C6;?å§&#x2039;æ&#x2013;&#x2021;件ï¼&#x152; å&#x2020;?å&#x2C6;&#x203A;建ä¸&#x20AC;个æ&#x2013;°æ&#x2013;&#x2021;件ã&#x20AC;&#x201A;ä½&#x2020;æ&#x2DC;¯ï¼&#x152;导è&#x2C6;ªç&#x2022;&#x152;é?¢ä¿?ç&#x2022;&#x2122;äº&#x2020;å&#x2C6;?å§&#x2039;å??称ï¼&#x152;并å&#x153;¨æ&#x2DC;¾ç¤ºè¯¥é¡µé?¢å&#x203A;¾æ &#x2021;æ&#x2014;¶æ&#x153;&#x2030;é»&#x201E;è&#x2030;²è­¦å&#x2018;&#x160; è¦&#x2020;ç&#x203A;&#x2013;å®&#x192;ï¼&#x152;è¿&#x2122;æ &#x2021;å¿&#x2014;è¿&#x2122;è¿&#x2122;个页é?¢å¹¶ä¸?å­&#x2DC;å&#x153;¨ã&#x20AC;&#x201A; z å&#x2C6; é&#x2122;¤é¡µé?¢ï¼&#x161;å½&#x201C;ä½ å&#x153;¨JSF导è&#x2C6;ªç&#x2022;&#x152;é?¢ä¸­å&#x2C6; é&#x2122;¤ä¸&#x20AC;个页é?¢å&#x203A;¾æ &#x2021;æ&#x2014;¶ï¼&#x152;ç&#x203A;¸å&#x2026;³è &#x201D;ç&#x161;&#x201E;web页é?¢å&#x153;¨ç&#x2022;&#x152;é?¢ä¸­å°&#x2020;ä¸?å&#x2020;? å?¯è§ ã&#x20AC;&#x201A;å¦&#x201A;æ&#x17E;&#x153;ä½ å·²ç»?å&#x2C6;&#x203A;建äº&#x2020;å®&#x17E;é&#x2122;&#x2026;ç&#x161;&#x201E;æ&#x2013;&#x2021;件ï¼&#x152;é&#x201A;£ä¹&#x2C6;å&#x153;¨åº&#x201D;ç&#x201D;¨ç¨&#x2039;åº?导è&#x2C6;ªç&#x161;&#x201E;è§&#x2020;å&#x203A;¾æ&#x17D;§å&#x2C6;¶å&#x2122;¨å·¥ç¨&#x2039;中ç&#x161;&#x201E;Web Contentæ&#x2013;&#x2021;件夹中è¿&#x2DC;æ&#x2DC;¯å?¯ç&#x201D;¨ç&#x161;&#x201E;ã&#x20AC;&#x201A; å&#x2026;³äº&#x17D;JSF导è&#x2C6;ªç&#x2022;&#x152;é?¢å&#x2019;&#x152;å&#x2C6;&#x203A;建导è&#x2C6;ªè§&#x201E;å&#x2C6;&#x2122;ç&#x161;&#x201E;ç&#x203A;¸å&#x2026;³ä¿¡æ ¯ï¼&#x152;å?¯ä»¥è§ 第16ç« â&#x20AC;&#x153;æ·»å&#x160; é¡µé?¢å¯¼è&#x2C6;ªâ&#x20AC;?ã&#x20AC;&#x201A; ---------------------------------------------------------------385----------------------------------------------------------------------------


11.3.4 关于 ADF 皮肤依赖性和库的相关知识 ADF皮肤和JDK1.4(或更高版本)是兼容的,但在仅支持SUN的JSF Reference Implementation 1.0 的服务器上是不能运行的。执行工具必须是JSF 1.1_01(或更新的)或Apache MyFaces 1.0.8(或 更新的)。 ADF皮肤提供了: z adf-faces-api.jar:所有公共的ADF皮肤APIs在包oracle.adf.view.faces中; z adf-faces-impl.jar: 所有公共的ADF皮肤APIs在包oracle.adfinternal.view.faces中; ADF皮肤提供了两种标签库供JSF页面使用: z ADF Faces Core library z ADF Faces HTML library 表 11–2 显示了 URIs 和 JDeveloper 中使用的 ADF 皮肤及 JSF 标签库的默认前缀。 Table 11–2ADF皮肤和JSF标签库

JDeveloper也提供了ADF皮肤缓存和ADF皮肤工业产品标签库,它们分别使用afc和afi的前缀。 关于ADF皮肤缓存,具体见第23章“使用缓存优化应用程序性能”。关于ADF皮肤工业产品,具体见 JDeveloper的在线帮助主题“发展中的ADF可变应用程序”。 所有的JSF应用程序必须符合Servlet规范:version 2.3(或更新的)和JSP规范:version 1.2 (或更新的) 。在你配置的J2EE的web容器必须提供必要的关于JavaServer 页面标准标签库(JSTL) JAR文件,jstl.jar和standard.jar。使用的JSTL版本基于J2EE的web容器: ■ JSTL 1.0—Requires a J2EE 1.3 web container that supports Servlet 2.3 and JSP 1.2 ■ JSTL 1.1—Requires a J2EE 1.4 web container that supports Servlet 2.4 and JSP 2.0 关于 ADF 皮肤和 JSF 配置需求的完整信息,可见第 34 章“配置 ADF 应用”。 11.4 布置 web 页面 绝大多数SRDemo页面都使用了ADF皮肤panelPage控件开布置整个页面。panelPage控件允许你在 页面上定义特殊区域来放置图片、导航菜单、按钮、页面级别或应用级别文本,从而确保应用程序 的web页面的协调性、有好性。图11–5 显示了使用panelPage 控件开创建页面的例子。

---------------------------------------------------------------386----------------------------------------------------------------------------


图 11–5 使用PanelPage控件进行页面涉及

当你使用向导创建一个新的JSF页面时,JDeveloper在JSP/HTML可视编辑器中已经自动打开一个 空白的页面。为了编辑页面,你可以使用JDeveloper提供的你比较习惯的页面设计工具的任意组合: ■ Structure window ■ JSP/HTML Visual Editor ■ XML Source Editor ■ Property Inspector ■ Component Palette 当你使用某个设计工具修改一个页面时,其他的工具会自动的更新你做的修改。 11.4.1 怎样在 JSF 页面增加 UI 控件 在同一个JAF页面中,你可以既使用JSF控件标准,也使用ADF皮肤控件。例如,用JDeveloper 插入和使用panelPage控件来创建一个起动JSF页面,你必须遵循下面的流程: 在JSF页面中插入UI控件: 1. 如果还没有打开,在可视编辑器中双击应用导航中起动的JSF页面来打开; 2. 在控件面版中,从下拉列表中选择ADF Faces Core; 3. 将PanelPage从面版中拖拽到可视编辑器中; 当你在可视编辑器中拖动一个控件时,注意结构窗口在一个盒子轮廓中加亮h:form控件, 标志着这个h:form控件是一个目标控件。当拽下时表示目标控件就是要插入的源控件。 4. 在结构窗口,右击最近插入的af:panelPage或其他PanelPage facets,从Insert before、 Insert inside或Insert after菜单中选择某个来添加你期望的UI控件;

---------------------------------------------------------------387----------------------------------------------------------------------------


在panelPage控件中,你可以创建输入框、查询窗口、表格或其他页面包含部分内容。更多 关于panelPage和其他方面详细信息,请参考11.4.4节“使用PanelPage控件”。 5. 要编辑新插入控件的属性,可以双击结构窗口的控件打开一个特性编辑器,或选择该控件 然后使用Property Inspector。 提示:在结构窗口中使用上下文菜单来添加控件,需要确保你要添加的控件在当前目标位 置。你也可以从结构窗口中的控件面版中拖动控件。当你在结构窗口中拖动控件时, JDeveloper会加亮目标位置,通过使用盒子轮廓或者有箭头的直线来指出这个源组件将要 在放下时添加到目标位置。关于使用结构窗口添加控件的详细信息,具体可见11.4.3.1节 “在结构窗口中编辑”。 当你在新插入控件中进行页面设计时,你也可以使用数据控制面版来插入databound UI控件。 只要将目标从数据控制面版中拖出,放进页面设计的位置即可。关于使用数据控制面版的更多的信 息,请见第12章“在页面中显示数据”。 11.4.2 当第一次插入 ADF 皮肤控件时会发生什么 图11-6显示了在一个页面中第一次添加ADF皮肤控件时,视图控制器工程的应用导航视图。 图11–6 第一次添加ADF皮肤控件后,在导航中的视图控制器

---------------------------------------------------------------388----------------------------------------------------------------------------


当你首次向一个JSF页面添加ADF皮肤组件时,JDeveloper自动做如下事情: z 在页面中引入ADF皮肤内核和HTML标签库(如果他们没有被引用),参见例11-4。 z 用afh:html, afh:head, 和afh:body分别替换html, head, and body,参见例11-5。 z 在web.xml中添加ADF皮肤分离和映射配置设置,参见11.4.2.1章节——” web.xml文件的 更多相关内容”。 z 在face-config.xml中添加ADF皮肤打包工具箱配置设置,参见11.4.2.2章节—— “face-config.xml文件的更多相关内容”。 z 在视图控制器的/WEB-INF下创建一个启动器adf-face-config.xml,参见11.4.2.3章节—— “启动器:adf-faces-config.xml文件”。 z 在文件系统中创建/ViewController/public_html/WEB-INF/temp/adf文件夹,该文件夹中 包含ADF皮肤组件需要的图片和字体。但是可能直到你关闭或是重新打开这个工作空间,在 应用程序导航器中都看不到这个文件夹。 提示:WEB-INF/lib 和 WEB-INF/temp/adf两个文件夹只在JDeveloper运行时使用,为了在应用 程序导航器中减少混淆,你必须将他们置于视图控制器工程之外。双击视图控制器可以打开工 程熟悉对话框。在工程目录下,选择Web应用,使用不包含标签添加你想要排除在外的文件夹。 示例11-5,添加首个ADF皮肤组件后的JSF JSP文档 <?xml version='1.0' encoding='windows-1252'?> <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:afh="http://xmlns.oracle.com/adf/faces/html" xmlns:af="http://xmlns.oracle.com/adf/faces"> <jsp:output omit-xml-declaration="true" doctype-root-element="HTML" doctype-system="http://www.w3.org/TR/html4/loose.dtd" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/> <jsp:directive.page contentType="text/html;charset=windows-1252"/> <f:view> <afh:html> <afh:head title="untitled1"> <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"/> </afh:head> <afh:body> <h:form> <af:panelPage title="Title 1"> <f:facet name="menu1"/> <f:facet name="menuGlobal"/> <f:facet name="branding"/> <f:facet name="brandingApp"/> <f:facet name="appCopyright"/> <f:facet name="appPrivacy"/> <f:facet name="appAbout"/> ---------------------------------------------------------------389----------------------------------------------------------------------------


</af:panelPage> </h:form> </afh:body> </afh:html> </f:view> </jsp:root> 11.4.2.1 web.xml 文件的更多相关内容 当你首次在JSF页面中插入ADF皮肤组件时,JDeveloper自动向web.xml中插入如下的ADF皮肤配 置设置: z ADF皮肤过滤器:Installsoracle.adf.view.faces.webapp.AdfFacesFilter是一个servlet 过滤器,在AdfFacesContext对象创建时,用来确保ADF皮肤正确初始化的。处理文件上传 时AdfFacesFilter也是必须的,配置设置将AdfFacesFilter映射成一个符号名称; z ADF皮肤过滤器映射:将JSF servlet的符号名称映射到ADF皮肤过滤器; z ADF皮肤资源servlet:Installs oracle.adf.view.faces.webapp.ResourceServlet, 通过 授权一个ResourceLoader提供web应用资源(诸如图片、字体和JavaScript库)。配置设置 将ResourceServlet映射为一个符号名称 z ADF皮肤资源映射:将URL模式映射到ADF皮肤资源servlet符号名称。示例11-6提供了一个 首次添加ADF皮肤组件后的web.xml文件 示例:11-6:在web.xml文件中配置ADF皮肤 <?xml version = '1.0' encoding = 'windows-1252'?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"> <description>Empty web.xml file for Web Application</description> <!-- Installs the ADF Faces filter -- > <filter> <filter-name>adfFaces</filter-name> <filter-class>oracle.adf.view.faces.webapp.AdfFacesFilter</filter-class> </filter> <!-- Adds the mapping to ADF Faces filter -- > <filter-mapping> <filter-name>adfFaces</filter-name> <servlet-name>Faces Servlet</servlet-name> </filter-mapping>

---------------------------------------------------------------390----------------------------------------------------------------------------


<servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- Installs the ADF Faces ResourceServlet -- > <servlet> <servlet-name>resources</servlet-name> <servlet-class>oracle.adf.view.faces.webapp.ResourceServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>/faces/*</url-pattern> </servlet-mapping> <!-- Maps URL pattern to the ResourceServlet's symbolic name --> <servlet-mapping> <servlet-name>resources</servlet-name> <url-pattern>/adf/*</url-pattern> </servlet-mapping> ... </web-app> 当你使用ADF皮肤时,web.xml中可用的配置元的参考信息,请参见A.7.1章节——“web.xml文 件的任务支撑”。 提示:如果你在应用程序中使用复合过滤器,一定要保证在你想要运行的程序中的web.xml 按顺序列出这些过滤器。运行时,这些过滤器按照在文件中列出的那样顺序被调用。 11.4.2.2 face-config.xml 文件的更多相关内容 正如前面提到的,当你使用JSF技术创建一个新的工程时,JDeveloper就为你创建了一个空的 faces-config.xml文件。当你首次向JSF页面插入一个ADF组件时,JDeveloper自动向 faces-config.xml中为ADF组件插入默认的打包工具箱,如示例11-7所示:

---------------------------------------------------------------391----------------------------------------------------------------------------



ADF BC develop guide 中文版