内容


使用 JAX-RS、JPA 和 Dojo 创建丰富的以数据为中心的 web 应用程序

开发用于管理青年足球联赛的应用程序

准备开始

在本文中,将使用最新的服务器端 Java 技术开发以数据为中心的 web 应用程序,以及使用 Dojo 工具箱创建富用户界面。这些技术将极大地减少了需要在服务器端和客户端编写的代码。熟悉 Java 和 JavaScript 能更有效地理解本文。您将需要使用 Java 1.6 JDK 来编译和运行代码;本文中使用 JDK 1.6.0_20。您还需要 Java Web 容器;本文中使用 Apache Tomcat 6.0.14。为保持数据一致,可使用任何带有 JDCB 2.0 兼容驱动程序的数据库。为简单起见,使用的是嵌入式数据库 Apache Derby 10.6.1。本文使用 Java API for RESTful Web Services (JAX-RS),以及用于 JAX-RS 实现的 Jersey 1.3 。实现还要用到 Java Persistence API (JPA) 及 Hibernate 3.5.3。最后,还用到 Dojo toolkit 1.4。见 参考资料 中这些工具的链接。

使用 Java Persistence API 运行数据

很多 web 应用程序是以数据为中心 — 它们显示数据并允许用户新建或更新数据。这听上去很简单,但真到了一些基本操作,如在数据库中读写数据,情况却非常糟糕。尽管如此,Java Persistence API (JPA) 却极大地减少了必须编写的冗长的样板式代码。我们将看一个使用 JPA 的简单例子。

本文中,将开发一个简单的管理青年足球联赛的应用程序。开始时将建立一个简单的数据模型,用于跟踪球队及其队员。将使用 JPA 完全访问这些数据。以第一个数据模型 Team 开始。清单 1 显示了此类。

清单 1. Team 数据模型类
@Entity
public class Team {
....
....@Id 
....@GeneratedValue(strategy = GenerationType.IDENTITY)
....private long id;
....
....private String name;
....
....@OneToMany
....private Collection<Player> players;
....
     // getters and setters........
}

这是典型的 JPA 注释类。使用 @Entity 注释来声明该类将映射到数据库。可以选择指定类的表名,或者在使用与类相同的名称时实现约定。其次,您要注释该类的 id 字段。想要它成为表的主键,使用 @Id 注释来声明。从逻辑角度来看,id 并不重要;只将其用于数据库。因为想要数据库充分发挥其价值,使用 @GeneratedValue 注释。

清单 1 中,还要声明另一个字段,name 字段。它是球队的名字。请注意该字段没有 JPA 注释。默认情况下,这会映射到同名列,这已符合本文意图。最后,每个球队都有多名队员与之关联。使用 @OneToMany 注释让 JPA 运行时知道这是一个管理关系,球队有多名球员。在 Java 类中,这是 Player 对象的 java.util.Collection清单 2 显示 Player 类被引用。

清单 2. Player 数据模型类
@Entity
public class Player {
....
....@Id 
....@GeneratedValue(strategy = GenerationType.IDENTITY)
....private long id;
....
....private String firstName;
....
....private String lastName;
....
....private int age;
....
....@ManyToOne (cascade=CascadeType.ALL)
....private Team team;
....
     // getters and setters
}

清单 2 中显示的 Player 类与 清单 1 中的 Team 类类似。它的字段更多,但大多数情况下不必担心注释这些字段。JPA 将帮助您正确完成。清单 1清单 2 的一个不同之处是如何指定 Player 类与 Team 类的关系。本例中使用 @ManyToOne 注释,因为一个 Team 中有多个 Players。请注意,还指定了一个级联策略(cascade policy)。参考 JPA 文档,选择适用于您的本应用程序的级联策略。本例中,使用该级联策略可以同时创建新的 TeamPlayer,JPA 将保存两者,这将方便应用程序开发。

既然已经声明两个类,只要告诉 JPA 运行时如何连接数据库。通过创建 persistence.xml 文件来完成。JPA 运行时需要找到该文件,并使用其中的元数据。最简单的方法是放入 /META-INF 目录,这是源代码的子目录(需要将它放到输出编译类的根目录中)。清单 3 显示 persistence.xml 文件。

清单 3. 足球应用程序的 persistence.xml
<persistence version="1.0"
....xmlns="http://java.sun.com/xml/ns/persistence"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
....xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
 http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
....<persistence-unit name="soccer">
........<class>org.developerworks.soccer.model.Team</class>
........<class>org.developerworks.soccer.model.Player</class>
........<properties>
............<property name="hibernate.dialect" 
                      value="org.hibernate.dialect.DerbyDialect" />
............<property name="hibernate.connection.driver_class"....
                      value="org.apache.derby.jdbc.EmbeddedDriver" />
............<property name="hibernate.connection.url" 
                       value="jdbc:derby:soccerorgdb;create=true" />
............<property name="hibernate.hbm2ddl.auto" value="update" />
............<property name="hibernate.show_sql" value="true" />
............<property name="hibernate.connection.characterEncoding" 
                      value="UTF-8" />
............<property name="hibernate.connection.useUnicode" 
                       value="true" />
........</properties>
....</persistence-unit>
</persistence>

回头看看 清单 1清单 2,所有代码都是通用的 JPA 代码。实际上曾使用的都是 JPA 注释和它的常量。对已使用的数据库和 JPA 实现没有什么特别的。正如在 清单 3 中看到的,persistence.xml 文件正是存放这些特定内容的地方。有一些出色的 JPA 实现可用,包括 OpenJPA 和 TopLink(见 参考资料)。已经使用过古老的 Hibernate,因此有几个 Hibernate 相关属性已经指定。这些多数都是简单易懂的,如 JDBC 驱动器和 URL,而且是很有用的,如通知 Hibernate 登陆到正在执行的 SQL (一些您肯定不想放在产品环境中的内容,但对于调试却很重要)。

清单 3 中,您还会注意到使用了 Apache Derby 数据库。实际上,使用的是数据库的嵌入式版本。您不必单独启动数据库,且担心配置。此外,在连接 URL 中您也指出应该自动创建数据库,并且您也通知 Hibernate 自动创建模式(这是 hibernate.hbm2ddl.auto 属性)。因此如果只运行应用程序,可以创建数据库及表。这有益与开发,但当然您会希望产品系统设置不一样。现在数据模型代码已经生成,并可通过 JPA 访问,我们将查看开放该数据,以便应用程序能使用它。

使用 JAX-RS 对数据的 RESTful 访问

如果是五年前创建该应用程序,现在可能会使用一些 Java Server Pages (JSPs) 或 Java Server Faces (JSFs) 或一些其他模板技术。不需要服务器上为该应用程序创建 UI,而是使用 Dojo 在客户端创建。所需要做的就是让客户端代码使用 Ajax 来访问数据。也可使用模板解决方案来解决这类问题,但使用 Java API for RESTful Web Services (JAX-RS) 要简单得多。一开始我们创建一个类,用于读取数据库中所有 Teams 以及创建新的 Teams清单 4 显示该类。

清单 4. Teams 的数据访问类
@Path("/teams")
public class TeamDao {
....
....private EntityManager mgr = 
          DaoHelper.getInstance().getEntityManager();
....
....@GET
....@Produces("application/json")....
....public Collection<Team> getAll(){
........TypedQuery<Team> query = 
                mgr.createQuery("SELECT t FROM Team t", Team.class);
........return query.getResultList();
....}
....
....@POST
....@Consumes("application/x-www-form-urlencoded")
....@Produces("application/json")
....public Team createTeam(@FormParam("teamName") String teamName){
........Team team = new Team();
........team.setName(teamName);
........EntityTransaction txn = mgr.getTransaction();
........txn.begin();
........mgr.persist(team);
........txn.commit();
........return team;
....}
}

清单 4 显示的是类数据访问对象类,名为 TeamDao。随后我们将注释这个类,但首先我们讲解一下数据访问。该类引用 JPA 类 EntityManager。这是 JPA 的中心类,并提供对底层数据库的访问。作为检索联赛中所有球队的首选方法,使用 EntityManager 来创建查询。查询使用 JPA 的查询语言,它与 SQL 相似。该查询获取所有 Teams。对于第二个方法,只要用传入的球队名创建新的 Team,用 EntityManager 创建事务、保存新球队并执行事务。所有代码都是普通的 JPA 代码,因为所有类和接口都是基础 API 的一部分。

现在已经理解了 清单 4 中 JPA 部分,我们再探讨其中的 JAX-RS 部分。您首先会注意到的是使用 @Path 注释来向基于 HTTP 的客户端公开此类。/teams 字符串指定该类的相对路径。完整的 URL 路径是 <host>/SoccerOrg/resources/teams/SoccerOrg 将会指定您的 web 应用程序的路径(当然,您可以配置成不同的,也可完全移除)。/resources 部分将用于指定 JAX-RS 终端。/teams 对应 @Path 注释并指定使用哪些 JAX-RS 类。

接下来,第一个方法 getAll 有一个相关的 @GET 注释。它指定如果接收到 HTTP GET 请求,则调用该方法。接着,该方法有个 @Produces 注释。它声明响应的 MIME 类型。本例中,您要生成 JSON,因为它是基于 JavaScript 的客户端最易使用的。

这就是要使用 JAX-RS 将类公开给客户端所要做的所有事情。尽管如此,您可能会问自己:如果该方法返回 Team 对象的 java.util.Collection,又如何发送给 web 客户端?@Produces 注释声明您想以 JSON 方式发送,但 JAX-RS 如何将其序列化成 JSON?其实要完成这步您只需在 Team 类中多加一个注释,如 清单 5 中所示。

清单 5. 修改过的 Team
@XmlRootElement
@Entity
public class Team {
....
// unchanged from Listing 1
........
}

通过添加 @XmlRootElement 注释,JAX-RS 现在可以将这个类转换成 JSON 对象。您也许记得这个类。它不是 JAX-RS 的一部分;而是 Java Architecture for XML Binding (JAXB) API 的一部分,而这又是核心 Java 1.6 平台的核心部分。该注释看上去像指出它用于 XML,但实际上可用于各种 JAXB 输出,包括 JSON。还有很多其他 JAXB 注释,但您在本例中只要用到这个。它只使用常用方法将 Team 类所有字段序列化成 JSON。

现在回到 清单 4 看看类的第二个方法,createTeam 方法。该方法使用 @POST 注释来指定当接收到 HTTP POST 请求时调用该方法。下一步,它用 @Consumes 注释来声明它能使用 哪种 POST 请求。此处指定的值对应于内容类型头部的 HTTP 请求。本例中,它指定为 x-www-form-urlencoded。它是提交 HTML 表单时将会接收到的类型。因此,该方法会在 /SoccerOrg/resources/teams 终端提交 HTML 表单时调用。最后,请注意该方法只用一个输入参数,一个名为 teamName 的字符串。请注意该参数用 @FormParam 注释声明。它告诉 JAX-RS 运行时在请求主体内查找一个名为 teamName 的表单参数(注释值)并将其绑定到传入方法注释的变量中。有了这些,就可以轻松处理表单提交并且将其嵌入到代码中。如果提交很多数据,可能会导致混乱。本例中,您也许想使用更结构化的方法。清单 6 显示的是创建 Player 对象的简单例子。

清单 6. 使用 JAX-RS 处理结构化的 POST 数据
@Path("/players")
public class PlayerDao {
....private EntityManager mgr = 
          DaoHelper.getInstance().getEntityManager();
....
....@POST
....@Consumes("application/json")
....@Produces("application/json")
....public Player addPlayer(JAXBElement<Player> player){
........Player p = player.getValue();
........EntityTransaction txn = mgr.getTransaction();
........txn.begin();
........Team t = p.getTeam();
........Team mt = mgr.merge(t);
........p.setTeam(mt);
........mgr.persist(p);
........txn.commit();
........return p;
....}
....
....@GET
....@Produces("application/json")
....public List<Player> getAllPlayers(){
........TypedQuery<Player> query = 
............mgr.createQuery("SELECT p FROM Player p", Player.class);
........return query.getResultList();
....}
}

清单 6 中的 PlayerDao 类与 清单 5 中的 TeamDao 类很像。需要检查的主要要差异是它的 addPlayer 方法。该方法处理 HTTP POST 请求,类似于 TeamDao 中的 createTeam 方法。然而,它使用 application/json — 也就是说它希望获得的是 JSON 数据。这有两个含义,首先,请求需要指定 application/json 的内容类型,从而可以调用该方法。其次,post 的主体应该是 JSON 数据。请注意该方法的输入参数是 JAXBElement<Player> 类型,也就是说它是 Player 对象的 JAXB 包装。它告诉 JAX-RS 自动将传送的数据解析成 JAXBElement 包装,所以您不必编写任何解析代码。请注意,在方法的主体中,只用一行代码获取完整的 Player 对象,然后它可使用 JPA 将新的 Player 保存到数据库。

要完成 JAX-RS 需要做的最后一件事是显示用于包装所有内容的配置。为此,只需修改应用程序的 web.xml 文件。清单 7 显示的是应用程序的 web.xml。

清单 7. 应用程序的 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
....xmlns="http://java.sun.com/xml/ns/javaee" 
....xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
....xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
....id="Soccer_Org" version="2.5">
  <display-name>SoccerOrg</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  <servlet>
    <servlet-name>JAXRS-Servlet</servlet-name>
   
 <servlet-class>com.sun.jersey.spi.container.servlet.
ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
     
 <param-value>org.developerworks.soccer.model;org.developerworks.
soccer.web</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>JAXRS-Servlet</servlet-name>
    <url-pattern>/resources/*</url-pattern>
  </servlet-mapping>
</web-app>

如您在 清单 7 中所见,应用程序中只有一个 servlet 声明。该 servlet 是由 Jersey 所提供的,这是使用的 JAX-RS 实现。传递一个初始化参数给 servlet — 包中含有您希望 JAX-RS 了解的所有类。本例中,有一个存放数据模型的包和一个存放数据访问对象的包。您希望能找到这些模型,以便 JAX-RS 能够将其转换为 JSON。当然,需要找到 DAO 以便 JAX-RS 能将请求路由到它们。最后,请注意 servlet 映射。这里指定了您的 URL 路径的 /resources 部分。现在已经准备好在客户端使用所有后台代码创建一个使用 Dojo 的 UI。

利用客户端的 REST 和 Dojo

Dojo 工具箱提供了几乎所有构造 web 应用程序客户端所需的库或工具。您会看到它在使用 Ajax、表单、JSON 和创建 UI 小部件时如何提供帮助。(它可以做的更多,而这恰好是在该简单例子中您所需要的。)它是如此庞大的一个系统,您可能想要下载完整的工具箱并根据您应用程序所需自定义构造。对于本应用程序示例,您可以换用 Google Ajax API 来访问您所需要的工具箱的各个部分。这很方便,而且具有性能优势,因为 Google 的 Dojo 版本是通过 Google 自己的高效内容发布网站(CDN)提供的。

您的应用程序是以数据为中心的,因此开始时可以加入一些数据。我们将使用 Dojo 创建 UI 来添加 Teams清单 8 显示所有您所需要的代码。

清单 8. 使用 Dojo 添加 Teams
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Harness</title>
        <link rel="stylesheet" type="text/css" 
       
 ....href="http://ajax.googleapis.com/ajax/libs/dojo/1.4/dijit
/themes/soria/soria.css"/>
        <script type="text/javascript"
 src="//ajax.googleapis.com/ajax/libs/dojo/1.4/dojo/dojo.xd.js"
 djConfig="parseOnLoad: true"></script>
<script type="text/javascript">
....function init(){
....    var btn = dijit.byId("addTeamBtn");
....    dojo.connect(btn, "onClick", function(event){
....    ....event.preventDefault();
            event.stopPropagation();
            dojo.xhrPost({
                  form : dojo.byId("addTeamForm"),
            ....handleAs: "json",
            ....load : function(data){
            ........addTeam(data);
            ........alert("Team added");
            ....},
            ....error : function(error){
                ....    alert("Error adding team: " + error);
            ....}
            });
....    });
....}
</script>
</head>
<body class="soria">
....Add a Team<br/>
....<form method="POST" action="/SoccerOrg/resources/teams" id="addTeamForm">
........<label for="teamName">Team Name:</label>
........<input name="teamName" type="text" id="teamName"
 dojoType="dijit.form.TextBox"/>
........<button type="submit" id="addTeamBtn" dojoType=
"dijit.form.Button">Add Team</button>
....</form>
....<script type="text/javascript">
........dojo.require("dijit.form.Button");
....    dojo.require("dijit.form.TextBox");
....    dojo.addOnLoad(init);
....</script>
</body>
</html>

注意在 清单 8 中,您引用了来自 Google CDN 的基础 Dojo 库。完成之后,您就可以使用 dojo.require 函数请求 Dojo 额外部分(见 清单 8 中代码底部)。请注意您刚创建了一个普通 HTML 表单,但使用了一些额外的 Dojo 相关属性。这告诉 Dojo 在可视化元素中加入额外的样式,并在对应的 DOM 元素中加入额外功能。告诉 Dojo 一旦其他东西加载(所有 Dojo 组件)就执行 init 函数。此函数中,使用 dijit.byId 函数取得表单中按钮的事件处理函数。Dijit 是 Dojo 的小部件库。可以使用 dojo.byId 来根据 ID 引用任何 DOM 元素,但类似的 dijit.byId 会赋给小部件额外的功能(如果元素标记为 widget,则如 清单 8 中按钮所示)。

然后可以使用 Dojo 为按钮点击关联一个事件处理函数。事件处理函数停止表单提交,并使用在 dojo.xhrPost 函数中使用 Ajax。该函数使 POST HTML 表单很容易。它通过检测 HTML 表单的 action 属性区分 Ajax 客户端。它还读取所有的表单元素并将它们传递到 Ajax POST。当它接收到服务器返回的响应,它就调用传递给 xhrPostload 函数。请注意,通过设置传递给 xhrPost 函数的 handleAs 属性来返回 JSON。很快将看到 addTeam 函数,但可以直接传入数据对象,因为已将 JSON 数据安全解析成可用的 JavaScript 对象。该 addTeam 函数将与另一表单联合使用,来添加 Players清单 9 显示的是表单的 HTML。

清单 9. 添加 Player 表单
Add a Player<br/>
<form id="addPlayerForm" action="/SoccerOrg/resources/players">
....<label for="firstName">First Name:</label>
....<input name="firstName" id="firstName" type="text"
 dojoType="dijit.form.TextBox"/>
....<label for="lastName">Last Name:</label>
....<input type="text" name="lastName" id="lastName"
 dojoType="dijit.form.TextBox"/><br/>
....<label for="age">Age:</label>
....<input type="text" name="age" id="age"
 dojoType="dijit.form.TextBox"/><br/>
....<label for="team">Team:</label>
....<select id="team" name="team" dojoType="dijit.form.
Select"></select>
....<button type="submit" id="addPlayerBtn" dojoType=
"dijit.form.Button">Add Player</button>
</form>
<script type="text/javascript">
     dojo.require("dijit.form.Select");
     dojo.addOnLoad(loadTeams);
</script>

该表单,与 清单 8 中的一样,是一个有效的 HTML 表单。尽管如此,元素中却添加了 Dojo 特定属性。请注意,有一个 SELECT 元素,它将作为 Teams 的下拉列表,让用户可选择将 Player 加入哪个 Team。这是需要从服务器加载的动态数据。请注意在启动时还加入了另一个函数 —loadTeams 函数。它用于从服务器加载球队。清单 10 显示了该函数,以及 addTeam 函数,如您所见,它在 清单 9 曾被引用。

清单 10. loadTeamsaddTeam 函数
var teams = {};
function loadTeams(){
....var select = dijit.byId("team");
....dojo.xhrGet({
........url: "/SoccerOrg/resources/teams",
........handleAs:"json",
........load : function(data){
............var i = 0;
............for (i in data.team){
................addTeam(data.team[i]);
............}
........},
........error : function(error){
............alert("Error loading team data: " + error);
........}
....});
}
function addTeam(team){
....teams[team.id] = team;
....var select = dijit.byId("team");
....var opt = {"label":team.name, "value":team.id};
....select.addOption(opt);
}

这里,再次用到了 Dojo 的 Ajax 工具访问数据,此工具由先前创建的 JAX-RS 终端所提供。这次使用的是 dojo.xhrGet,它向 Ajax 终端发出 HTTP GET 请求。在此例中,需要指定它的 URL,否则它与 清单 9 中所看到的 xhrPost 一样。最后,是 addTeam 方法。这里再次使用 Dojo 小部件的额外功能向显示球队的下拉列表轻松添加新选项。现在已经看到球员表单是如何创建的,再看看处理提交的代码(见 清单 11)。

清单 11. 添加新 Player
var button = dijit.byId("addPlayerBtn");
dojo.connect(button, "onClick", function(event){
.... event.preventDefault();
       event.stopPropagation();
       var data = dojo.formToObject("addPlayerForm");
       var team = teams[data.team];
       data.team = team;
       data = dojo.toJson(data);
       var xhrArgs = {
           postData: data,
           handleAs: "json",
           load: function(data) {
               alert("Player added: " + data);
               dojo.byId("gridContainer").innerHTML = "";
               loadPlayers();
           },
           error: function(error) {
               alert("Error! " + error);
           },
           url: "/SoccerOrg/resources/players",
           headers: { "Content-Type": "application/json"}
       };
       var deferred = dojo.xhrPost(xhrArgs);
});

这段代码将向 PlayerDao.addPlayer 方法提交数据,该方法您已在前面的 清单 6 中见过。这段代码预计将 Player 对象序列化成 JSON 数据结构。首先,您再次使用 Dojo 来包装表单上按钮点击处理函数。下一步,使用 Dojo 的函数 dojo.formToObject 来将表单中数据转换成 JavaScript 对象。然后稍微修改 JavaScript 对象,以匹配服务器能接收的结构。然后使用 Dojo 的 dojo.toJson 函数将它转换成 JSON 字符串。现在它传给 dojo.xhrPost,与 addTeam 表单提交方式一样。请注意,您添加了 HTTP 头部 Content-Type 来保证它路由到 PlayerDao.addPlayer 方法。

xhrPost 也有一个 load 函数,它会在 Ajax 请求从服务器携带成功响应返回后调用。本例中,它会清空页面上名为 gridContainer 的元素,并调用名为 gridContainer 的元素,loadPlayers 的函数。这是另一个用来显示所有球员的 Dojo 小部件。清单 12 显示用于此目的的 HTML 和 JavaScript 。

清单 12. Player grid HTML 和 JavaScript
<style type="text/css">
    @import
 "http://ajax.googleapis.com/ajax/libs/dojo/1.4/dojox/grid/resources/Grid.css";
    @import
 "http://ajax.googleapis.com/ajax/libs/dojo/1.4/dojox/grid/resources/soriaGrid.css";
    .dojoxGrid table { margin: 0; } 
    html, body { width: 100%; height: 100%; margin: 0; }
</style>
<script type="text/javascript">
function loadPlayers(){
....var pStore = new dojox.data.JsonRestStore({
........target: "/SoccerOrg/resources/players"
....});
....pStore._processResults = function(data, deferred){
........return {totalCount:deferred.fullLength || data.player.length, 
                    items: data.player};
....};
       var pLayout = [{
           field: "firstName",
           name: "First Name",
           width: "200px"
       },
       {
           field: "lastName",
           name: "Last Name",
           width: "200px"
       },
       {
           field: "age",
           name: "Age",
           width: "100px"
       },
       {
           field : "teamName",
           name : "Team",
           width: "200px"
       }];

       var grid = new dojox.grid.DataGrid({
           store: pStore,
           clientSort: true,
           rowSelector: "20px",
           structure: pLayout
       }, document.createElement("div"));
       dojo.byId("gridContainer").appendChild(grid.domNode);
       grid.startup();
}
</script>
<div id="gridContainer" style="width: 100%; height: 100%;"></div>
<script type="text/javascript">
    dojo.require("dojox.grid.DataGrid");
    dojo.require("dojox.data.JsonRestStore");
    dojo.addOnLoad(loadPlayers);
</script>

清单 12 显示了 Dojo 的 DataGrid 小部件。它是 Dojo 中一个更丰富的小部件,因此也需要额外的 CSS。要新建一个 grid,要做两件事。首先,需要创建数据源。本例中,是来自服务器的 JSON 数据,因此创建一个新的 JsonRestStore 对象,并将它指向将会产生数据的服务器 URL。然后覆盖 _processResults。只需要这样做,因为它能预计接收 JSON 数组的数据,并且 JAX-RS 终端将会生成更复杂一点的对象(它有一个属性,名为 player,它的值是 JsonRestStore 预计接收的 JSON 数组)。grid 还需要布局元数据,以描述显示哪些列,以及 JavaScript 对象对应的属性是什么。然后就可以创建 grid 并将它放入 DOM 目录树中。

现在已完成足球应用程序示例,有很多方法显示联赛中的球员。您可以轻松扩展此处的简单示例,如添加球员编辑功能,对 grid 排序,甚至可以加入更多的数据,如比赛和结果。

结束语

本文演示了快速创建丰富的、以数据为中心的 web 应用程序的方法。使用了几项关键技术移除服务器端和客户端的繁杂的样板式代码:JPA、JAX-RS 和 Dojo。很多情况下,可以直接使用默认的惯例设置进一步减少 web 应用程序中的代码量。其结果是使用最少的代码创建非常先进的 web 应用程序。使用到的所有技术都是可扩展和具有量产品质的,因此您可放心扩展此应用程序示例(或您自己的应用程序),直接用于更加健壮的用例。更棒的是没有壁垒。使用的是服务器端的开放标准。例如,可以轻松转换数据库技术。在前台使用 REST 和 JSON,这意味着可以使用不同的 UI 套件,或者可以轻松连接上移动客户端。


下载资源


相关主题

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development, Java technology
ArticleID=659466
ArticleTitle=使用 JAX-RS、JPA 和 Dojo 创建丰富的以数据为中心的 web 应用程序
publish-date=05172011