基于 Ajax 的持久对象映射(reship)

基于 Ajax 的持久对象映射

用 Ajax 和 Persevere 将 JavaScript 对象映射到服务器数据

 

2007 年 12 月 06 日

Persevere 持久对象框架为浏览器 JavaScript 环境带来了持久对象映射功能。对象的持久性在 Java™ 编程和 Ruby 领域中很流行,并且动态 JavaScript 语言在本质上就很适合将对象映射到持久数据。除了能在很大程度上简化开发难度之外,通过提供可管理的数据模型、透明的客户机-服务器 Ajax 交换、自动的状态更改存储和隐式事务管理,Persevere 还能自动化基于 Asynchronous JavaScript + XML(Ajax)的 Web 应用程序中的映射和通信。

实际上,所有的应用程序都使用某种形式的持久性;这就是说,它们能保存信息以备将来执行。通常,为将来检索而持久化信息的能力是应用程序的一个重要方面,而且由于 Web 应用程序越来越多地集成了用户交互和贡献,持久性就变得更为重要。然而,持久性经常需要用一种与程序执行过程中的数据保存方式不同的方法来保存状态信息。

在程序执行过程中,状态信息通常都保存在对象中(至少,在面向对象的程序中如此),持久化处理后,则保存到数据库中或转换为基于文本或字符的格式。状态信息在这两种范例间的相互转换通常需要大量的开发工作并很可能会出错。持久对象映射策略可以通过将对象映射到持久数据来自动化状态存储和检索。这种映射也可以提供一种访问持久状态和保存该状态的简单机制。

持久化信息
Web 2.0 应用程序来说,为了便于今后检索而具有持久化信息的能力十分关键。通过本文,了解 Persevere 持久对象框架以及它是如何在基于 Ajax 的 Web 应用程序中进行自动映射和通信的。

由于持久对象处理的可管理性得到了改进,因此持久性映射在其他语言中(比如 Ruby on Rails 中的 Active Record 和 Java 语言中的 Hibernate)也很受欢迎。对于基于 Ajax 的应用程序,保存浏览器的数据信息的过程更为复杂,因为必须将数据序列化并发送回服务器。服务器然后必须检索此数据,并执行存储处理将数据存储到其持久存储区。

正交持久性(orthogonal persistence)通过自动化整个存储过程进一步简化了持久性。任何对持久映射对象的更改都会自动传送到持久存储区:无需任何手动保存。

工具

本文将向您展示如何使用 Persevere JavaScript 框架,它是一种面向支持正交持久性的 JavaScript 语言的远程持久对象映射框架。Persevere 将 JavaScript 对象映射到远程持久化的数据存储区,其方法是使用 JavaScript Object Notation (JSON) 和符合 JSON for persistence (JSPON) 规范的标准具像状态传输(Representational State Transfer,REST)HTTP 方法。您还将了解如何联合使用 Persevere JavaScript 客户机和 Persevere 服务器,该服务器自动化了将数据库和其他持久存储作为 JSON REST 服务加以公开的过程。借助 Persevere,通过在浏览器中访问和修改 JavaScript 对象就可以访问和修改服务器上的数据。Persevere 会对必要的序列化、Ajax 调用和去序列化进行自动化处理。

本文随附的一个演示给出了如何在 JavaScript 代码中使用持久对象映射来构建一个简单的 blog。要创建 blog,需要联合使用 Persevere 客户机框架和 Persevere 服务器。借助 Persevere,可以使用常规的 JavaScript 对象和属性来访问和操纵持久化的数据。持久数据源是 Persevere 服务器提供的默认数据库存储区。Persevere 服务器还提供了在服务器上将数据库映射为 JSON 格式的功能,Persevere 客户机和服务器通过这种格式相互通信。通过使用简单的 JavaScript 代码和一个 API,就可以借助持久对象映射进行主要的对象访问和修改(在 参考资料 部分,可以找到本文所涉及工具的全部链接。您也可以从本文的 下载 部分下载 blog.zip 包)。图 1 展示了这个持久 SOA(Service-Oriented Architecture):


图 1. Persevere 持久 JSON SOA

注意: Persevere 用 JavaScript 实现了这个针对持久性的开放 API,该 API 在 persistentjavascript.org 中定义(有关链接,请参见 参考资料)。

立即开始

首先,下载和设置 Persevere(要获得这些文件的链接,请参见 参考资料)。Persevere 服务器附带了客户机,所以只需下载 Persevere 服务器。您可以下载带有 Apache Tomcat 的 Persevere 服务器或者只下载来当作 Java 2 Platform, Enterprise Edition (J2EE) Web 应用程序。如果下载 Tomcat 安装程序,需要转到 bin 文件夹并运行 startup 以启动服务器。

有了持久对象图,就可以访问和修改对象了,所做的更改将会是持久的。Persevere 含有持久对象浏览器(JSPON 浏览器),可用于此目的。此外,Persevere 服务器还支持动态可扩展对象持久存储,因此无需创建任何表就可以生成 blog。要开始构造 blog,打开浏览器(如果使用的是默认的 Tomcat 设置,那么地址为 http://localhost:8080/browser.html),然后再创建一个 blog 数组对象,如图 2 所示(我喜欢将 blog 对象作为 root 对象的属性):


图 2. 创建 blog 数组对象

访问 Persevere 的持久性功能

有几种方式可用来访问 Persevere 的持久功能。只有在两种情况下需要进行预处理,其一是通过标准 JavaScript 属性语法支持属性的延迟加载时,其二是需要自动保存属性更改时。然而,即使没有使用 持久 JavaScript API 进行过预处理,具有显式保存和延迟加载功能的完全持久对象映射仍可用。而且,您可以在服务器上或构建过程中执行预处理,为了获得最佳性能,应该对最终的应用程序这么做。或者,也可以在 JavaScript V1.7(Firefox V2.0 或更高版本)中本地运行 Persevere 的全部特性(除了预处理),而对不具有 JavaScript V1.7 支持的浏览器则可以使用预处理。请参阅 Persevere 文档(有关链接,参见 参考资料)以获得使用这些开发方法和构建过程的更多信息。

root 对象是固定对象,可以从中获得这些动态对象数据库的全部持久对象图。为实现此目的:

  1. 在浏览器中打开 http://localhost:8080/browser.html?id=root(也可以打开 http://localhost:8080/browser.html,单击 Load New Object,然后再键入 root)。
  2. 单击 New Property,向 root 对象添加属性。使用字段名 blog,然后创建一个数组对象。该对象只简单代表 blog post 的列表。
  3. 单击 blog 对象,然后单击 New Property;选择属性名 name,然后再键入 blog 的名称。
  4. 保存修改。

支持持久性的 JavaScript 代码

可以使用 Persevere 框架访问持久对象。如果使用 Persevere 框架,就必须预处理 JavaScript 代码以添加延迟加载和正交持久性支持。最简便的实现方法是调用 persevere.loadScript 来加载 JavaScript 文件(其他实现方式,可以参见侧栏 访问 Persevere 的持久性功能)。对于 Persevere,可以使用 .pjs(而非 .js)扩展名来表明该文件是一个具备全部持久性支持的 JavaScript 文件。loadScript() 函数然后执行必要的处理。

在本例中,可以使用默认的 loadScript 功能,它在客户端执行预处理。要展示正交持久性的功能,应该编写一个可以更改 blog 名称的函数,如 清单 1 所示:


清单 1. 为单个 blog post 创建 HTML 代码
                        function changeBlogName(blog) {

                        blog.name = prompt("Enter a new name for our blog");

                        }

                        

创建 blog

接下来,可以创建一个简单的 HTML/JavaScript 接口来为 blog 生成一个新的 post。首先,创建一个文件,名为 blog.html,并将其放入 Web 应用程序的根目录(即默认 Tomcat 安装中的 webapps/ROOT/)。然后,创建名为 blog.pjs 的文件,内含具持久性的所有 JavaScript 代码。清单 2 显示了针对这个 blog 应用程序的 HTML 代码:


清单 2. blog.html
                        <html>

                        <head>

                        <script src="js/persevere.js"></script> <!--include the persistence framework -->

                        <link rel="stylesheet" type="text/css" href="blog.css"/>

                        </head>

                        <body>

                        <a href="#" onclick="renderBlog()">Home</a>

                        <div id="blogDiv"></div> <!-- we render the blog in this div later -->

                        <div id="newPost">

                        <h3>Enter a new post</h3>

                        <div>

                        Content:<textarea id="newPostContent">new post</textarea>

                        </div>

                        <button onclick="addBlogPost()">Post</button>

                        </div>

                        <script>

                        persevere.loadScript("blog.pjs");

                        </script>

                        </body>

                        </html>

                        

现在,可以在 blog.pjs 中编写简单的 JavaScript 函数来添加新的 post。添加新 post 会创建一个代表该 post 的 JavaScript 对象。之后,可以将新的 post 添加到持久化的 blog 数组对象。虽然很多工作都是用标准 JavaScript 代码实现的,但还需要允许 root 对象访问此 blog 对象。可以通过调用 pjs.load("root") 实现此目的。load() 函数由 Persistent JavaScript API 定义,以便根据 ID 加载对象;root 是对象数据库的 root 对象的变相的 ID。清单 3 所示的函数可用来添加一个新 blog post:


清单 3. addBlogPost() 函数
                        function addBlogPost() {

                        var blog = pjs.load("root").blog;

                        var newPost = {

                        title : "new post",

                        content : document.getElementById('newPostContent').value,

                        created : new Date(),

                        comments : [] // an empty array of comments

                        };

                        blog.push(newPost); // blog is an array, so we can add a new post to the top

                        // of the array using standard array functions

                        newPost.title = prompt("What would you like to title the new post?",newPost.title);

                        }

                        

在调用 addBlogPost() 时,新的 post 会被添加到 blog;Persevere 框架会自动将整个操作封装在事务内并将其发送给服务器以便被持久化。在使用正交持久性时,无需调用任何保存或持久化方法;属性和数组修改会被自动保存。

接下来,编写一个函数显示 blog 中的这些 post。要模块化代码,必须编写两个函数:一个用来呈现整个 blog;一个用来呈现单个 post。清单 4 所示的 JavaScript 代码可用来显示这些 post:


清单 4. 为 blog 创建 HTML 代码
                        function renderBlog() {

                        var blog = pjs.load("root").blog; // get the blog object, the list of the postings

                        blogElement = document.getElementById("blogDiv");

                        blogElement.innerHTML = ""; // clear it out

                        for (var i =0; i < blog.length;i++) // iterate through blog posts, render each one

                        renderPost(blog[i],blogElement.appendChild(document.createElement('div')));

                        }

                        

可以使用简单的 for 循环遍历 blog 中的全部 post。Persevere 会执行延迟加载以便只有需要的数据才会被下载。当访问未初始化的属性时,Persevere 会自动从服务器请求正确的数据,然后再继续执行。Persevere 使用延续(continuation)来挂起和继续执行,以便能够发起异步请求从服务器检索数据(这样一来,浏览器就不会由于同步请求而锁定)。

现在,要呈现每个 post,输入清单 5 所示的代码:


清单 5. 为单个 blog post 创建 HTML 代码
                        function renderPost(post,postDiv) {

                        var html = "<h2>" + post.title + "</h2>"

                        + "<h4>" + post.created + "</h4><p>" + post.content + "</p>";

                        postDiv.innerHTML = html;

                        }

                        

为了新的 post 能立即显示,当页面加载以及调用 addBlogPost 时,需要在 addBlogPost() 函数末尾和 blog.pjs 末尾添加对 renderBlog() 的调用。而且,可能还需要使用 unshift() 数组方法来代替 push() 方法,以将此 post 添加到列表的顶部而非底部。

现在,使用新的 blog 接口将 post 添加到 blog,这样一来,此 blog 就具备一定的功能性了。添加几个 post 之后,blog 应该如图 3 所示:


图 3. 新 blog

回到持久对象浏览器查看所保存的数据。图 4 显示了此浏览器:


图 4. blog post 的持久化的数据

在 blog 条目上张贴评论

还应该添加在 blog post 上张贴评论的功能。为此,添加 onclick 事件以便能单击 blog post,这会显示出一个 blog post 及其评论。然后,为单个 post 视图添加一条评论。接下来,用与向 blog 对象添加 post 相同的方式向 blog post 添加评论。从本文的 下载 部分可以获得添加评论功能所需的代码。也可以参阅 Persevere 文档来了解 Persevere 何时将属性视为暂时的(即未保存的)或持久的。

通过使用 Persevere 的持久对象映射功能和 Persevere 服务器的动态对象创建功能,可以很容易地创建 blog 所需的持久性。请注意如果要创建 blog,有些步骤是无需 执行的:无需创建数据库表、编写任何 Ajax 调用、编写任何 SQL(Structured Query Language)代码、修改任何配置文件或创建任何控制器。构建此 blog 所需的只是简单直观的 HTML 和 JavaScript 代码。

安全性

我们所构建的这个 blog 还没有任何安全性:任何人都可以向其中发布信息。在大多数 Web 应用程序中,都会希望限制这种访问和其他形式的数据修改。blog 的逻辑均处在客户机端的 JavaScript 脚本中,而安全性决不能只在客户机端实现。

Persevere 服务器具有内置的、以数据为中心的安全性,这类安全性在服务器上实施。在大多数 Web 应用程序开发场景中,开发人员通常都会出于便利性的考虑而在他们的应用程序逻辑中实现安全性。然而,这对大型 Web 应用程序来说是一种很危险的方法,因为,随着应用程序逻辑的复杂性的增长,受攻击的可能性也会随之增长。应用程序逻辑越复杂,攻击者找到漏洞的机会也就越多。通过使用以数据为中心的安全性,就可以从应用程序逻辑中分离出安全性,从而使安全性变得更加易于管理。

初次安装后,Persevere 服务器会工作于不安全的模式。要启动安全性,打开持久对象浏览器(http://localhost:8080/browser.html)并单击 Sign in,创建一个新用户。新用户创建后,安全性就会启动,这个新用户会被视为超级用户。安全性启动后,默认的安全性等级是使持久对象对公众可读,对超级用户(即新创建的用户)可写。要想获得关于在不同的持久化的对象上指定访问等级的更多信息,参阅 Persevere 服务器文档(参见 参考资料)。

关系数据库

持久对象映射的最常见的形式恐怕就是对象关系映射。您已经在本文中了解了客户机所创建的数据结构的持久对象映射,Persevere 服务器支持传统的关系数据库,也支持通过 JSON REST 服务公开 SQL 数据库表数据,以便用 Persevere 进行持久对象映射。要查看 SQL 数据库表的例子,可以打开持久对象浏览器并加载 exampledatabasetable/ 对象。可以利用 load() 函数从 JavaScript 文件载入该数据,并且此表可以作为一个常规的 JavaScript 数组被访问。通过清单 6 中所示的 JavaScript 代码可以很方便地修改此表中的数据。


清单 6. JavaScript 与关系数据库的交互
                        function changeNameForFirstRow() {

                        pjs.load("exampledatabasetable/")[0].name="New name";

                        }

                        

该语句从表中加载数据并将第一行中的名称栏的值改为一个新值。用关系数据库存储代替 blog 示例中的对象数据库存储也是可能的。如果关系数据库存储被正确设置,由于对象的抽象性,可以将 blog 属性更改为指向关系数据库存储,并且这个 blog 应用程序也会正常工作,无需更改任何代码。

Persevere 的其他功能

Persevere 包括对定义 JSON 模式的支持。JSON 模式 定义属性值可接受的类型。例如,可以指定 title 属性必须是字符串或指定一个评论项必须有 authorcreated 以及 content 属性。这与 XML 中的 Document Type Definition (DTD) 或面向对象语言中的类十分类似。结构也可以定义属性是持久的还是临时的(也就是说,更改时不会加以保存)。请参阅 Persevere 文档(参见 参考资料),理解 Persevere 在持久化属性时所采用的规则很重要。

用 Persevere 访问跨域数据

Persevere 支持构建跨域的对象图,可以包括来自不同服务器的持久化的对象图。从持久对象浏览器,创建一个新属性,选择 Existing by ID,然后为 JSON 对象键入一个 URL。

事务

Persevere 隐式地将修改组合到事务中。所有在一个事件处理中发生的修改(例如因为 onclick 事件而在 addBlogPost() 中所做的修改)均被组合到一个事务中。因为浏览器内所有的 JavaScript 处理均在一个事件的上下文中发生,所以此行为提供了一种既方便又合理的方法来处理事务。

然而,有时对事务进行进一步的控制也是很必要的。Persistent JavaScript API 为控制事务定义了两种方法:pjs.commit()pjs.rollback()commit() 方法会立即尝试提交已发生的改变。rollback() 方法则取消从事件开始以来或最后一次提交的所有更改。例如,可以在 addBlog() 函数的结尾添加一个确认,它将撤销此发布,如清单 7 所示:


清单 7. 控制事务
                        function addBlogPost() {

                        ...

                        if (!confirm("Are you sure you want to create this post?"))

                        pjs.rollback();

                        }

                        

备用服务器

Persevere JavaScript 客户机并不依赖于 Persevere 服务器。因为所有与服务器的通信都是建立在标准 HTTP 方法和 JSON 格式基础上的,所以利用 Persevere 通过 PHP 、Ruby 或其他技术进行持久对象映射并不困难。然而,本文不会详细介绍有关 HTTP 和 JSPON 的内容,服务器只需根据 JSPON 规范传送 JSON 并处理标准 HTTP(PUT、POST 和 DELETE)修改请求。

持久化的函数和分布式计算

Persevere 支持将函数或方法持久化到动态持久对象中。对于 blog 应用程序,可以将编写的所有函数创建为 blog 持久对象的方法。使用这种方法,整个应用程序逻辑将作为数据被保存在相同的持久存储中。因为 Persevere 服务器使用了一个内部 JavaScript 环境(参见 图 1)来映射持久化的对象,所以持久化的函数可以在服务器上执行,就像它们能在客户端上被执行一样。

因为 Persevere 服务器也实现了 Persistent JavaScript(参见 参考资料),所以这些函数可以使用与在客户端上相同的方式访问相同的持久化的数据,而且通过 JSON-RPC,甚至可以用标准的 JavaScript 调用语句透明地调用远程环境中的函数。由于为逻辑和数据使用了单个存储媒介,所以这种功能可以为应用程序提供更一致的环境。它同时也为编程提供了一个 “live” 实时。可在持久对象浏览器中动态编写、测试以及保存函数。

Ajax 资源中心
仔细阅读 developerWorks Ajax 资源中心,在这里可以找到开发 Ajax 应用程序所需的免费工具、代码和信息。活跃的 讨论论坛 可能有针对您的疑问的答案。

结束语

通过使用正交持久对象映射,可以用简单熟悉的 JavaScript 代码快速地开发功能强大的 Ajax 应用程序。编写 Ajax 请求、序列化和数据交互等的复杂性可以很容易地用 Persevere 加以处理,以便提供对持久化的数据的面向对象访问,从而加快应用程序的开发。

你可能感兴趣的:(Ajax)