深入 web2.0 应用框架 zk

来源:百度文库 编辑:神马文学网 时间:2024/06/13 12:08:46
 


2009 年 9 月 25 日

本文将介绍基于 Web2.0 的 OpenSource Framework ZK 。具体阐述 ZK 框架的搭建、在 ZK 中使用 JDBC 接口与数据库连接池操作,以及如何整合 Hibernate。

简介

ZK 是一个事件驱动 (event-driven) 的,基于组件 (component-based) 的,用以丰富网络程序中用户界面的框架。 ZK 包括一个基于 AJAX 事件驱动的引擎 (engine),一套丰富的 XUL 和 XHTML,以及一种被称为 ZUML(ZK User Interface Markup Language,ZK 用户界面标记语言 ) 的标记语言。

用户可以利用 XUL 和 XHTML 的丰富特性来呈现您的 Web 应用,操纵它们来处理因用户活动而引发的事件,就像在桌面应用程序中那样。不同于大多数其它框架,就 ZK 而言,AJAX 是一种幕后 (behind-the-scene) 技术,组件内容的同步和流水线事件 (pipelining of events) 都由 ZK 引擎自动完成。

这样大大简化了应用程序的开发周期。同时又使得前端程序美观,易用;

除了简单的模型和丰富的组件,ZK 也支持一种文本标记语言,称为 ZUML 。 ZUML,感兴趣的朋友可以深入了解这种语言,从中体会到他的强大。

ZK 的安装

作为一个基于 java 的 web framework,再使用 ZK 之前,我们需要成功安装 JDK 和 tomcat5.5 或者更高的版本。 在这之后,我们可以去 ZK 的官方网站(http://www.zkoss.org)上 download 我们开发所需要的 library 。

创建第一个工程“ Hello World ”

创建一个新的工程对于 ZK 来说非常的简单,在我们的 tomcat 的目录下($TOMCAT_HOME/webapps),建立新的目录 zkdemo 。其目录结构如下所示:

+zkdemo                                    +WEB-INF                                    web.xml                                    index.zul

解压缩我们刚才 download 的 library 并将 jar 文件 copy 到我们的目录中, $TOMCAT_HOME/webapps/$PROJECT_NAME/WEB 。结果如下,

dist/lib/*.jar                                    dist/lib/zkforge/*.jar                                    dist/lib/ext/*.jar

编辑 web.xml,将如下代码加入其中:

                                                                        Used to clean up when a session is destroyed                                    ZK Session Cleaner                                    org.zkoss.zk.ui.http.HttpSessionListener                                                                                                            ZK loader for ZUML pages                                    zkLoader                                    org.zkoss.zk.ui.http.DHtmlLayoutServlet                                                                        update-uri                                    /zkau                                                                        1                                                                                                            zkLoader                                    *.zul                                                                                                            zkLoader                                    *.zhtml                                                                                                            The asynchronous update engine for ZK                                    auEngine                                    org.zkoss.zk.au.http.DHtmlUpdateServlet                                                                                                            auEngine                                    /zkau/*                                    

使用 zk 标记,创建 web 页面 index.zul 。加入代码:

                                    Hello, World!                                    

重启 Tomcat

打开浏览器,键入 URL:http://localhost:8080/zkdemo/index.zul。此时 , 您就能看到我们的第一个 ZK 的 web 程序。

当然,我们也可以添加一些互动的元素来扩展程序的功能。

                                    


图 1: 第一个 ZK 程序





回页首

基于 ZK 的数据库应用实例

下面我们通过一个实例来开发一个基于数据库的 Web 2.0 应用。在此实例中,我们使用简单的 JDBC 方式作为数据库连接。使用 java.sql.DriverManager 。在此,我们选择的数据库是文本型的 hsqldb,数据表是一个事物记录,由 3 个字段组成(item:事件名; priority:事件优先级; Date: 事件时间)

事前的准备

与所有的 application 类似,在我们开始动手之前,需要下载我们这个应用所必须的 jar package 。幸运的是,这些 package 我们都可以在 zk5.2 的 image 中找到。并且将这些 package 拷贝到我们的 application 的目录中,${tomcat_home}/webapps/${Application_name}/WEB-INF/lib 。

DAO 对象

当然,我们还是需要把 JDBC 访问写入到我们的 DAO 对象中,与大多数 java 的 jdbc programming 类似,请参考下面的代码:

public class EventDAO                                    {                                    private String url = "jdbc:hsqldb:file:/hsqldb/event";                                    private String user = "sa";                                    private String pwd = "";                                    public EventDAO() {                                    try                                    {                                    Class.forName("org.hsqldb.jdbcDriver");                                    }                                    catch (ClassNotFoundException e)                                    {                                    e.printStackTrace();                                    }                                    }                                    public List findAll()                                    {                                    Statement stmt = null;                                    Connection conn = null;                                    List allEvents = new ArrayList();                                    try {                                    conn = DriverManager.getConnection(url, user, pwd);                                    stmt = conn.createStatement();                                    ResultSet rs = stmt.executeQuery("select * from event");                                    Event evt;                                    while (rs.next()) {                                    evt = new Event();                                    evt.setId(rs.getString(1));                                    evt.setName(rs.getString(2));                                    evt.setPriority(rs.getInt(3));                                    evt.setDate(rs.getDate(4));                                    allEvents.add(evt);                                    }                                    } catch (SQLException e) {                                    e.printStackTrace();                                    }finally{                                    try {                                    stmt.close();                                    }                                    catch (SQLException e) {                                    e.printStackTrace();                                    }                                    try {                                    conn.close();                                    } catch (SQLException e) {                                    e.printStackTrace();                                    }                                    }                                    return allEvents;                                    }                                    public boolean delete(Event evt){                                    Connection conn = null;                                    Statement stmt = null;                                    boolean result = false;                                    try {                                    conn = DriverManager.getConnection(url, user, pwd);                                    stmt = conn.createStatement();                                    if (stmt.executeUpdate("delete from event where id = '"                                    + evt.getId() + "'") > 0);                                    result = true;                                    } catch (SQLException e) {                                    e.printStackTrace();                                    }finally {                                    try {                                    stmt.close();                                    } catch (SQLException e) {                                    e.printStackTrace();                                    }                                    try {                                    conn.close();                                    } catch (SQLException e) {                                    e.printStackTrace();                                    }                                    }                                    return result;                                    }                                    public boolean insert(Event evt){                                    Connection conn = null;                                    Statement stmt = null;                                    boolean result = false;                                    try {                                    conn = DriverManager.getConnection(url, user, pwd);                                    stmt = conn.createStatement();                                    if (stmt.executeUpdate("insert into event(id,name,priority,date) " +                                    "values ('" + UUID.randomUUID().toString() + "','" + evt.getName() +                                    "'," + evt.getPriority() + ",'" + new SimpleDateFormat("yyyy-MM-dd                                    HH:mm:ss").format(evt.getDate()) + "')") > 0);                                    result = true;                                    } catch (SQLException e) {                                    e.printStackTrace();                                    }finally{                                    try {                                    stmt.close();                                    }                                    catch (SQLException e) {                                    e.printStackTrace();                                    }                                    try {                                    conn.close();                                    } catch (SQLException e) {                                    e.printStackTrace();                                    }                                    }                                    return result;                                    }                                    public boolean update(Event evt){                                    Connection conn = null;                                    Statement stmt = null;                                    boolean result = false;                                    try {                                    conn = DriverManager.getConnection(url, user, pwd);                                    stmt = conn.createStatement();                                    if (stmt.executeUpdate("update event set name = '" + evt.getName() +                                    "', priority = " + evt.getPriority() + ", date = '" +                                    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(evt.getDate())+                                    "' where id = '" + evt.getId() + "'") > 0);                                    result = true;                                    } catch (SQLException e) {                                    e.printStackTrace();                                    }finally{                                    try {                                    stmt.close();                                    } catch (SQLException e) {                                    e.printStackTrace();                                    }                                    try {                                    conn.close();                                    } catch (SQLException e) {                                    e.printStackTrace();                                    }                                    }                                    return result;                                    }                                    }

编译并将 class 文件拷贝到 ${tomcat_home}/webapps/${application_name}/WEB-INF/classes/${package_name} 。

页面 ZUL 脚本

现在我们框架已经被建立起来了,下面我们需要实现的就是前端页面的展示以及对数据库的操作。页面代码如下所示:

                                                                        import org.zkforge.todo.event.Event;                                    import org.zkforge.todo.event.EventDAO;                                    import java.util.ArrayList;                                    import java.text.SimpleDateFormat;                                    import java.util.UUID;                                    //fetch all events from database                                    EventDAO evtdao = new EventDAO();                                    List allEvents = evtdao.findAll();                                    // 添加一条记录                                    void add(){                                    //insert into database                                    Event newEvt = new Event(UUID.randomUUID().toString(),                                    name.value,priority.value.intValue(),date.value);                                    evtdao.insert(newEvt);                                    //synchronized data with database                                    allEvents = evtdao.findAll();                                    //insert a listEvent into the listbox                                    Listitem li = new Listitem();                                    li.setValue(newEvt);                                    li.appendChild(new Listcell(name.value));                                    li.appendChild(new Listcell(priority.value.toString()));                                    li.appendChild(new Listcell(new SimpleDateFormat("yyyy-MM-dd").format(date.value)));                                    box.appendChild(li);                                    }                                    // 修改一条记录                                    void update(){                                    //update database                                    Event editEvt = (Event)box.selectedItem.value;                                    editEvt.setName(name.value);                                    editEvt.setPriority(priority.value);                                    editEvt.setDate(date.value);                                    evtdao.update(editEvt);                                    //update listbox                                    List children = box.selectedItem.children;                                    ((Listcell)children.get(0)).label = name.value;                                    ((Listcell)children.get(1)).label = priority.value.toString();                                    ((Listcell)children.get(2)).label =                                    new SimpleDateFormat("yyyy-MM-dd").format(date.value);                                    }                                    // 删除一条记录                                    void delete(){                                    evtdao.delete((Event)box.selectedItem.value);                                    box.removeItemAt(box.getSelectedIndex());                                    cleargb();                                    }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                Item:                                     Priority:                                     Date:                                     

编写完成之后将其存储为 todu.zul 拷贝到我们的 application 目录下,我的本机目录是:/${tomcat_home}/webapps/todo

打开我们的浏览器,输入 http://localhost:8080/todo/todo.zul


图 2:通过 JDBC 获取的数据

Add 用来增加一条记录,Update 用来修改,Delete 用于删除。点击任何按钮我们可以对这个数据表实现无刷新的操作。而不需要去编写复杂的 javascript 与 Ajax 特效脚本。 ZK 已经帮助我们 integrate 到 Framework 中了。

除以上办法外,所有的 J2EE 框架和 Web 服务器都支持连接池操作。这将有助于节约我们有限的 web 资源,减少系统开销。

优化:配置连接池

这个修改其实非常简单,但是对于程序的效率却有相当的提高,尤其是规模的访问来说。我们只需要在 tomcat 中去配置一下就好。

编辑 $Tomcat/conf/context.xml,加入如下的内容。

                                                                        

重启 TOMCAT. 验证一下我们的修改。还是打开我们的浏览器,输入 http://localhost:8080/todo/todo.zul


图 3:通过数据连接池获得的数据

自此我们这个基于 ZK 的数据库应用就已经完成了。

ZK 还有许多强大的功能有待于我们去挖掘。下面我们来看看 ZK 是如何整合 Hiberate 的。





回页首

ZK 深层次应用 : 整合 Hibernate

Hibernate

Hibernate 是一个对象关系映射 (ORM) 解决方案,专门针对 Java 语言。 Hibernate 的主要特点是简化了对于数据库的访问。

Hibernate 的安装与 ZK 的配置

在使用 Hibernate 之前,我们要下载 Hibernate(http://www.hibernate.org),将所有的 jar 文件放到 $myApp/WEB-INF/lib/ 下。

为使 ZK 和 Hibernate 顺利的工作,你需要完成下面的工作。

  1. 在 $myApp/WEB-INF/lib/ 下创建 zk.xml;
  2. 在 zk.xml 中加入如下的内容:
                                                                        Hibernate SessionFactory lifecycle                                    org.zkoss.zkplus.hibernate.HibernateSessionFactoryListener                                                                                                                                                                                    Hibernate Open Session In View life-cycle                                    org.zkoss.zkplus.hibernate.OpenSessionInViewListener                                                                                                                                                                                    Hibernate thread session context handler                                    org.zkoss.zkplus.hibernate.HibernateSessionContextListener                                                                        

创建 Java 对象

package events;                                    import java.util.Date;                                    public class Event {                                    private Long id;                                    private String title;                                    private Date date;                                    public Event() {}                                    public Long getId() {                                    return id;                                    }                                    private void setId(Long id) {                                    this.id = id;                                    }                                    public Date getDate() {                                    return date;                                    }                                    public void setDate(Date date) {                                    this.date = date;                                    }                                    public String getTitle() {                                    return title;                                    }                                    public void setTitle(String title) {                                    this.title = title;                                    }                                    }

创建你的首个 Java 类 (Event.java), 之后,编译 Java 源文件,然后将类文件放到 Web 部署文件夹的 classes 目录下,要保证包名正确。 ( 例如,$myApp/WEB-INF/classes/event/Event.class)

映射 Java 对象

有两种方式告诉 Hibernate 如何加载和存储持久类对象,第一种方式是使用 Hibernate 映射文件,另一种方式是使用 Java 注释。

使用映射文件

简单的为持久类 Event.java 创建一个 Event.hbm.xml 文件。

                                                                                                                                                                                                                                                                                                                                                                        

将这个 Event.hbm.xml 文件放到部署文件夹的目录 src 下,并且保证正确的包名。 ( 例如 ,$myApp/WEB-INF/src/event/Event.hbm.xml)

使用 Java 注释

使用 Java 注释的好处是不用创建额外的 Hibernate 配置文件。为你的 Java 类简单的添加注释来告诉如何关联 Hibernate 。

import javax.persistence.Entity;                                    import javax.persistence.GeneratedValue;                                    import javax.persistence.GenerationType;                                    import javax.persistence.Id;                                    import javax.persistence.Table;                                    @Entity                                    @Table(name="EVENTS")                                    public class Event {                                    private Long id;                                    private String title;                                    private Date date;                                    @Id                                    @GeneratedValue(strategy=GenerationType.SEQUENCE)                                    @Column(name = "EVENT_ID")                                    public Long getId() {                                    return id;                                    }                                    private void setId(Long id) {                                    this.id = id;                                    }                                    @Column(name = "EVENT_DATE")                                    public Date getDate() {                                    return date;                                    }                                    public void setDate(Date date) {                                    this.date = date;                                    }                                    public String getTitle() {                                    return title;                                    }                                    public void setTitle(String title) {                                    this.title = title;                                    }                                    }                                    1.      @Entity 声明这个类为一个持久对象                                    2.      @Table(name = "EVENTS") 注释表示这个实体是和数据库的 EVENTS 表映射的。                                    3.      @Column 元素备用与映射实体属性和数据库表的字段。                                    4.      @Id 元素定义了映射主键字段的属性。

创建 Hibernate 配置文件

下一步是安装 Hibernate 来使用一个数据库。 HSQL DB , 一个基于 Java 的 SQL 数据库管理系统 (DBMS),可以在 HSQL DB 网站 (http://hsqldb.org/) 下载到。

解压其到一个目录,例如 c:/hsqld 。

打开一个命令框 (command box),切换到 c:/hsqldb 目录。

在命令提示 (command prompt) 下,执行 java -cp lib/hsqldb.jar org.hsqldb.Server 。

安装完数据库之后,我们需要安装 Hibernate 配置文件。在部署文件夹的目录 src 下创建

hibernate.cfg.xml

文件 ( 例如,$myApp/WEB-INF/src/hibernate.cfg.xml) 。将下面的内容复制到你的 hibernate.cfg.xml 。这依赖于你如何映射 Java 对象。


使用映射文件
                                                                                                                                                                                    org.hsqldb.jdbcDriver                                    jdbc:hsqldb:hsql://localhost                                    sa                                                                                                            1                                                                        org.hibernate.dialect.HSQLDialect                                                                        thread                                                                        org.hibernate.cache.NoCacheProvider                                                                        true                                                                        create                                                                                                            


使用 Java 注释
                                                                                                                                                                                    org.hsqldb.jdbcDriver                                    jdbc:hsqldb:hsql://localhost                                    sa                                                                                                            1                                                                        org.hibernate.dialect.HSQLDialect                                                                        thread                                                                        org.hibernate.cache.NoCacheProvider                                                                        true                                                                        create                                                                                                            

创建 DAO 对象

创建 EventDAO.java                                    import java.util.Date;                                    import java.util.List;                                    import org.hibernate.Session;                                    import org.zkoss.zkplus.hibernate.HibernateUtil;                                    public class EventDAO {                                    Session currentSession() {                                    return HibernateUtil.currentSession();                                    }                                    public void saveOrUpdate(Event anEvent, String title, Date date) {                                    Session sess =  currentSession();                                    anEvent.setTitle(title);                                    anEvent.setDate(date);                                    sess.saveOrUpdate(anEvent);                                    }                                    public void delete(Event anEvent) {                                    Session sess =  currentSession();                                    sess.delete(anEvent);                                    }                                    public Event findById(Long id) {                                    Session sess =  currentSession();                                    return (Event) sess.load(Event.class, id);                                    }                                    public List findAll() {                                    Session sess =  currentSession();                                    return sess.createQuery("from Event").list();                                    }                                    }

编译并部署该文件到 class 目录中。

ZUML 页面访问持久对象

创建 event.zul 文件,其内容如下:

package events;                                                                                                                                                                                                                                                                                                                                                                        String datestr =                                    new SimpleDateFormat("yyyy/MM/dd").format(each.date);                                                                                                                                                

这样我们就可以在浏览器中访问该 ZUL 了。

至此,我们在文章中已经看到了 ZK 的基本与高级应用。当然,ZK 还有许多强大的功能有待我们去挖掘,希望各位朋友能使用 ZK 开发出美观且功能强大的 web 应用。



参考资料

  • SourceForge 是开源软件的开发者进行开发管理的集中式场所,也是全球最大开源软件开发平台和仓库。

  • 参考 ZK 网站:http://www.zkoss.org/,获取相关资料与 demo。

  • 访问 developerWorks 开放源码专区,获得丰富的 how-to 信息、工具和项目更新,帮助您用开放源码技术进行开发,并与 IBM 产品结合使用。


作者简介

 

杨凡,IBM 软件工程师,具有丰富的 globalization 领域项目开发与管理经验,参与多个产品的全球化开发,也是开源社区的常客。


 

郑霞锦,IBM 软件工程师 , 五年 IT 从业经验,具有广泛的工程北京,丰富的产品自动化测试经验,对于 opensource 领域一向非常关注。