如何在 XPages 中实现适当的层分离(即与 Java 对象交谈,而不是 DominoViews 和 DominoDocuments)

How to implement a proper layer separation in XPages (i.e. talk to Java objects, not DominoViews and DominoDocuments)

我正在尝试在我的 XPage 项目中实现适当的层分离。理想情况下,我试图达到 XPage 中的 XML 不包含 SSJS 并且仅使用 EL 访问 Java 对象的地步。

到目前为止,我已经弄清楚如何将我的所有数据从 domino 数据库加载到 Java Beans(其中 1 个文档 = 1 个对象,或多或少),我正在将视图内容读入 Java 地图或列表,我已经设法在重复控件中显示这些集合的内容。

我不确定的是如何在不引用 domino 文档的情况下显示单个文档的 'form' 的内容。特别是,我不确定如何处理 'new document' 案例。我想我创建了一个空对象,然后将该对象设置为 Xpage 的数据源。

我知道我必须为此使用 ObjectDataSource,但我不确定实际存储它的位置。我读了 Stephan Wissel 的一篇文章说不应该把它们放在托管 bean 中,那么我可以把它放在哪里?在像 viewScope?

这样的作用域变量之一中

现在我已经写了一个 'ApplicationBean',它是一个会话范围的托管 Bean,我在其中存储我的所有对象。

最佳做法是什么?似乎有许多不同的方法可以实现该目标。目前我正在研究 Christian Güdemann 的 XPages Toolkit,它听起来很有前途。我知道 Samir Pipalia、John Daalsgard 和 Frank van der Linden 已经制定了他们自己的框架。

那我该怎么办呢?有什么陷阱吗?

Jesse Gallagher 的脚手架框架也访问 Java 对象而不是 dominoDocument 数据源。我在 NotesIn9 的 "Using Java in XPages series" 中使用(并修改了)Tim Tripcony 的 Java 对象(第一个是 episode 132. The source code for what he does is on BitBucket。这基本上将 dominoDocument 转换为 Map。它不包括 MIME (富文本)或附件。如果它有用,我可以分享我所做的修改(例如,使字段名称全部相同,以确保它仍然适用于现有文档,其中字段可能是使用 LotusScript 创建的)。

这确实是一个很大的话题。正如 Paul 所提到的,Tim 的文档模型 类 是一个很好的例子,说明了如何清楚地做到这一点,Tim 在该 NotesIn9 系列的后续章节中进行了更详细的介绍。我自己的 Framework 的模型对象非常相似,尽管我还添加了 collection managers 来处理访问视图的肮脏业务。无论好坏,几乎每个 XPage 开发人员都以独特的方式解决这个问题。

您可以通过多种方式来实现此类事情,并且在正常情况下,有些差异并不是非常重要(例如,您是否在构建模型时预加载了文档中的所有数据对象或只在需要时延迟获取到后端),但肯定有几个首要问题需要解决。

模型访问

正如您在问题中提到的,最大的问题之一是您实际上如何从 XPage 访问模型对象 - 如何从 DB 中获取对象或重新创建对象。我的框架的模型对象使用自负的 "Manager" 对象,这些对象是应用程序范围的 beans,允许获取命名集合(映射到视图)、UNID 模型对象或通过关键字 [=52 获取新模型对象=].通常,这些模型(可序列化)然后通过 <xp:dataContext/><xe:objectData/> 或框架自己的 <ff:modelObjectData/>.

使用它们存储在页面的视图范围内

我发现避免使用托管 bean 来表示单个对象(例如 "CurrentWhatever" 然后在每个页面上填充数据)是非常明智的,因为这会混淆您的 faces-config case 或 运行s 在最坏的情况下进入会话问题(如果你把它放在我很少使用的会话范围内)。

你如何实现 "new" 与 "fetched" 模型对象主要取决于你一开始编写模型所采取的策略,但大多数归结为有两个构造函数:一个用于一个 UNID(至少)指向现有文档,一个用于创建新文档。如果你走 "write every properly explicitly in the object with getters and setters" 路线,后者也会用默认值初始化所有字段,而不是从文档中读取它们。在内部,您应该有字段来存储文档的 UNID,这可以指示它是否是新的 - 然后,您的保存方法可以检查此字段是否为空并在需要时创建一个新文档(然后存储新文档的 UNID在外地)。

观看次数

听起来您已经在将您的模型集读入 Lists,这很好。不过,不利的一面是可伸缩性:对于小型(少于 100 个)集合,您可能不会 运行 遇到任何加载速度问题,但之后初始页面加载速度会变慢,因为您的代码会提前读取整个视图。您可以使用 efficient view reading 稍微减轻这种情况,但有一个限制。内置视图通常速度很快,因为它们只在需要时加载数据(它们也会竭尽全力这样做,但这是另一个问题)。

这是为自己设定的崇高目标,但这样做是为了涵盖所有情况no small feat: you end up running into questions of FT searching, column resorting, efficient data preloading (you don't want to re-open the View object only to read in one entry at a time, but you also don't want to read the whole thing), use in viewPanel and maybe others (which require specialized interfaces), expanding/collapsing categories, and so forth. It's a large sub-topic on its own

深奥

您还可能 运行 遇到比您最初想象的更困难的其他领域,例如 "proper" 富文本处理和文件附件。特别是附件,需要 direct conflict with the XSP framework 才能使用自定义模型对象和标准 upload/download 控件正常运行。

字段名称区分大小写是另一个潜在的问题领域。如果您正在为所有字段编写 getter 和 setter,这是一个有争议的问题,但如果您要走 "thin wrapper" 路线(我更喜欢),那么在 caches/lookups 中编写任何中介代码很重要一种处理 "FOO" 和 "foo"(基本上)与 Notes 的项目名称相同,但在 Java 中不同的方法。我采取的策略是 extensive use of TreeMaps:如果您将 String.CASE_INSENSITIVE_ORDER 作为参数传递给构造函数,它会将字符串用作键时通常不区分大小写。

让你的模型对象与所有类似的标准控件一起工作可能是也可能不是优先事项 - 我发现它非常有价值,所以我做了很多跑腿工作来实现我的框架,但如果你'我们只是要做一些基本的字符串和数字模型,您不必担心。


所以...是的,这是个大话题!根据您对 Java 和 XPages undergirdings 的信心程度,我建议为您的对象选择相当简单的 "beans with getters and setters" 路线,或者查看现有框架之一的实现细节(我自己的或你提到的那些)。可悲的是,随着您的代码变得越来越复杂,会出现很多小问题,其中许多问题处理起来并不明显。

Andrew - Jesse 是这里的专家之一,所以我会仔细阅读他的回复。

我所做的是我采用了 Jesses 更大框架的关键部分之一 - "pageControllers" 并且我大量使用它。所以我最终为每个 XPage 作为控制器使用 Java Class。 "All" Jesse 的页面控制器框架确实让它更易于使用。因此,您可以在每个页面上将其引用为 "controller" 并且不需要为它们创建单独的托管 bean。

如果我真的需要按钮事件之类的东西,我仍然会在 XPage 上使用 SOMES SSJS。一些没有适当的 getter 和 setter 的方法。例如 HashMap.size()。但是绝大部分代码都进入了 Java Class。也不再需要 viewScope 变量了。

在 "New Document" 的情况下。在控制器中,我将创建一个新的 Java 对象来表示 "Current document"。我会将所有字段绑定到那个。如果它是新的,我创建一个新对象并将其分配给私有变量。如果我在某处加载表单,那么我会获取该变量并加载我想要的文档。

我已经开始真正尝试并在最近的 NotesIn9 中对此进行详细说明。特别是针对 Xpages 开发人员的 Java 小系列。我想我已经讲得够多了,可以向您展示您需要了解的内容。我确实计划尽快就这个主题做更多的事情。