深入 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 顺利的工作,你需要完成下面的工作。
- 在 $myApp/WEB-INF/lib/ 下创建 zk.xml;
- 在 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 领域一向非常关注。