`
xjk2131650
  • 浏览: 55306 次
  • 性别: Icon_minigender_1
  • 来自: 河北
社区版块
存档分类
最新评论

使用 Dojo 和 Grails 快速实现数据的增删改查(CRUD)

阅读更多

选择 Grails 和 Dojo 的原因

随着 web 的广泛应用,web application 的开发项目越来越多,而大部分的 web 开发归根结底都是对数据库的增删改查。对于一张数据库表的增删改查,采用基于 MVC(模型 - 视图 - 控制器)设计模式的开发往往需要 Domain class、 Controller、4 个增删改查的页面、form 输入数据的校验等。这样就免不了大量类似功能的复制粘贴修改。 如果你想从重复劳动中解放出来,Grails 是一个很好的选择。 使用 Grails 只需要写一个 domain class 就可以自动生成 CRUD 4 个页面和对应的 controller 方法来实现对数据库的增删改查,并实现服务器端的数据校验,从而大大地提高了开发效率。

类似的 web 快速开发框架还有 Rails、Django 和 TurboGears 等,与它们相比 Grails 有如下优势:

  • Grails 是一套用于快速 Web 应用开发的开源框架,它是由 15% 的 Groovy 代码和 85% 的 Java 代码写成,并建立在千锤百炼的经典框架(Spring,Hibernate,Sitemesh)之上,从而为大家带来一套能实现超高生产力的一站式框架。
  • Grails 学习快速,使用容易,如果你熟悉 Hibernate、Spring、SiteMesh 和 JSP,那么你一个小时之内就能学会 Grails。
  • Grails 运行于 JVM 之上,生成的 war 包可以部署在各种成熟的服务器 Tomcat, JBoss,Weblogic,Webspere 等。
  • 性能与 Rails2(100% Ruby) 相比要快很多。
  • Grails 被 SpringSource 收购,有强大的技术支持。

采用 Grails 的默认模板生成的 CRUD 页面有些简陋,实际项目中往往需要功能更加丰富,外观更加漂亮,使用更加方便的控件。例如页面布局控件,Tab 控件,树,菜单,日历,编辑器,进度条等等。现在有很多 JS 框架致力于 web UI, 例如 Dojo, Extjs, Jquery, YUI 等。我们之所以选择 Dojo, 因为 Dojo 具有以下的优势:

  1. 组件丰富,有强大的 UI(Dijit)。这样一来就可以减轻我们的大量沉重的工作,而且目前的开发非常活跃。
  2. UI 外的功能也很强大,Full Stack 的框架,扩展了 DHTML 的能力,例如:
    • 支持与浏览器 Back/Forward 按钮的集成。
    • Dojo Offline,一个跨平台的离线存储 API。
    • Chart 组件,可以方便地在浏览器端生成图表。
    • 基于 SVG/VML 的矢量图形库。
    • Google Maps、Yahoo! Maps 组件,方便开发 Mashup 应用。
    • Dojox Socket, 基于 WebSocket 或者 XHR 长连接的“服务器推”技术。
  3. Dojo 是一个很好的基础架构,具有面向对象的设计,统一的命名空间,包管理机制(The Package System and Custom Builds)可扩展性。Dojo 其实是一个组件模型,类似于 Spring,用来支持大规模的组件化开发。组件模型的作用就是增强代码的重用,这对于提高开发效率是至关重要的。
  4. Dojo 的背后有强大的支持 IBM、Oracle 等,这是非常重要的优势。Dojo 现在已经是众多开源框架的选择,包括:WebWork、Tapestry、Eclipse ATF、MyFaces。Dojo 的开发团队由 Alex Russell 领军,人数众多,力量非常强大。
  5. 开源,使用 BSD 软件许可。

快速搭建 CRUD 的框架 (MVC)

开发环境配置

首先需要安装 JDK,并设置好 JAVA_HOME。本文使用的是 JDK 1.6。

  1. Grails 官方网站下载并解压 grails.zip。本文使用的是 Grails1.3.7。
  2. 创建一个 GRAILS_HOME 环境变量,将 $GRAILS_HOME/bin 添加到 PATH 中。
  3. 习惯用 IDE 的可以选择 Intellij idea,Netbeans 和 Spring STS。本文以免费的 Netbeans7.0.1 为例,配置很简单,只需要在 Tools->Options->Miscellaneous->Groovy 下设置 Grails Home,就可以进行 Grails 项目的开发了。(见图 1)

图 1. Netbeans 中 Grails 的配置
图 1. Netbeans 中 Grails 的配置 

命令行创建 web application

  1. 首先,在一个空白目录下,输入 grails create-app DojoGrails。稍后,可以看到一个名为 DojoGrails 的目录 , 会建立一个标准的目录结构(如图 2)。很明显这是层次清晰的 MVC 模式。 

    图 2. Grails 的目录结
    图 2. Grails 的目录结 

  2. 进入 DojoGrails 目录,并输入 grails create-domain-class com.shuo.Employee,将得到两个新的文件:域类 grails-app\domain\com\shuo\Employee.groovy 和一个单元测试类。这里我们主要关注域类。一开始,域类里面什么都没有,我们为它添加一些字段和约束,见清单 1: 

    清单 1. 域类 Employee
    				
     class Employee { 
        String employeeNumber 
        String name 
        Integer age 
        Date onboardDate 
        
        static constraints = { 
            employeeNumber blank: false, unique: true 
            name blank: false 
            
        } 
        String toString(){ 
            return "${name}(${employeeNumber})"
        } 
     } 
    

    类似地我们创建一个与 Employee 关联的域类 Product。Product 的负责人 owner 是 Employee。见清单 2:



    清单 2. 域类 Product
    				
     class Product { 
        String name 
        String description 
        Float price    
        Employee owner   
    
        static constraints = { 
        } 
     } 
    

    这两个域类覆盖了所有常见的数据库数据类型(Date,String,Integer,Float)。

  3. 在命令行输入 grails create-controller com.shuo.Employee 生成域类对应的 Controller, 使用 grails 强大的脚手架 scaffold,见清单 3。运行时在内存中将动态地生成所有 CRUD 的 method 以及 CRUD 的页面。同样地生成 ProductController。 

    清单 3. 控制器代码
    				
     class EmployeeController { 
        def scaffold = true 
     } 
     class ProductController { 
        def scaffold = true 
     } 
    

  4. 到这里对两张表的 CRUD 功能全部完成,是时候看看效果了,在命令行输入 grails run-app。通过浏览器 http://localhost:8080/DojoGrails, 可以看到如图 3 所示的 Employee 的 List 页面。 

    图 3. Scaffold 默认的 List 页面
    图 3. Scaffold 默认的 List 页面 

    可以看出这个 list 页面已经具备分页和排序功能了。

    再来看看 Employee 的 Create 页面,如图 4 所示:



    图 4. Scaffold 默认的 create 页面
    图 4. Scaffold 默认的 create 页面 

    从图中可以看出添加后,服务器端会返回数据验证的结果,比如员工号必须唯一,名字不能为空,年龄必须是数字等。

    再来看看 Product 的 Edit 页面 , 如图 5:



    图 5. Product 的 Edit 页面
    图 5. Product 的 Edit 页面 

    这里我们主要关注 owner 字段 , scaffold 自动生成一个列出所有员工的下拉框,让用户从中选择出产品的负责人。

    另外值得一提的是,这个 web application 已经拥有了国际化多语言的支持,只需要对 grails-app/i18n/messages_zh_CN.properties 文件进行修改,就可以汉化整个 web 界面了。

    Grails 的 Scaffold 脚手架是不是很强大呢,一句 def scaffold = true 就实现了如此丰富的功能。

  5. 在命令行输入 grails generate-all com.shuo.Employee,grails 会根据默认模板在 EmployeeController 中生成 CRUD 的对应的 method 代码,并在 grails-app/views/employee 下生成 4 个 CRUD 的 gsp 页面。这些就是 scaffold 背后的代码。后面我们将对这些代码进行修改,引入 Dojo,来增强 CRUD 的用户体验。

使用 Dojo 增强 CRUD 用户体验

引入 Dojo

到 dojotoolkit.org 网站下载最新版本的 Dojo Toolkit Release。 本文用的是 dojo-release-1.6.1。

解压后将其拷入 web-app/js 中,目录结构如图 6:


图 6. Dojo 的目录结构
图 6. Dojo 的目录结构 

在 grails-app/views/layouts/main.gsp 中添加以下代码,见清单 4:


清单 4. 导入 Dojo
				
<link rel="stylesheet" 
href="${resource(dir:'js/dojo-release-1.6.1/dijit/themes/claro',file:'claro.css')}" 
/>    
<script src="${resource(dir:'js/dojo-release-1.6.1/dojo',file:'dojo.js')}" 
data-dojo-config="isDebug: true,parseOnLoad: true,locale:'zh'"></script> 
…
 <body class="claro"> 

使用 EnhancedGrid 增强 List 表格

Grails scaffold 默认的 List 表格(如图 3)功能较为简单,例如它不能像 excel 那样的跨行列合并单元格、冻结表头、单击单元格转成编辑状态等。而 Dojo 为我们提供了这样的控件:Dojox 的 DataGrid 像一个基于 Web 的 Excel 组件,足可以应付非常复杂的数据展示及数据操作。下面列出了 DataGrid 的一些特性:

  • 可以任意的增加和删除单元格、行、或者列;
  • 对行进行统计摘要,Grid 可以生成类似于 OLAP 分析的报表;
  • Grid 超越了二维表格的功能,它可以跨行或跨列合并单元格以满足不同的数据填充的需求;
  • 行列冻结功能,使得浏览数据更加灵活方便;
  • Grid 事件采用了钩子机制,我们可以通过 onStyle 钩子完成对样式的更改;
  • 单元格具备富操作,所有的 dijit 部件都可以在单元格中使用,并且单元格可以通过单击转换为编辑状态;
  • 可以为不同的单元格设置不同的上下文菜单;
  • Grid 嵌套,也就是说 Grid 可以在单元格中嵌套其他的 Grid,从而组成更为复杂的应用
  • 支持 Dnd 拖放和键盘 navigation,提高了 Accessibility。
  • 除此之外,Grid 还有具有其他很多特性,例如,非常实用的偶数行上色、灵活的选取功能、自动调整列宽、数据的展开/合闭等。

其中 DataGrid 有一个非常重要的特性就是虚拟滚动(Virtual Scroll),DataGrid 对付大数据源的时候,在滚动事件触发后才请求后面的数据并创建 DOM 结点,因此每次只需要显示很少的几行,从而加快了 Grid 的加载。 不得不承认这是一种很精巧的做法,在用户体验上也很自然,就好像所有数据本来就在那里一样。但它也有缺点,当数据源真的比较大的时候,滚动条就会非常小,再加上行高参差不齐的情况,要精确地滚动到某个位置就比较困难。这时候简单直观的分页机制就足够了。最新的 Dojo 1.6 版里,继承自 DataGrid 的 EnhancedGrid 引入了几个新的插件,其中就有 Pagination(分页浏览)插件。

首先在 list.gsp 中使用 EnhancedGrid, 见清单 5:


清单 5. Dojo 的 EnhancedGrid 代码
				
 <script type="text/javascript"> 
 dojo.require("dojox.grid.EnhancedGrid"); 
 dojo.require("dojox.grid.enhanced.plugins.Pagination"); 
 dojo.require("dojox.data.QueryReadStore"); 

 var myStore = new dojox.data.QueryReadStore({url:"listJson"}); 

 function onRowDblClick(e){ 
  var itemid = grid.getItem(e.rowIndex).i.id; 
  document.location.href="edit/"+itemid; 
  } 
 </script> 
 <style type="text/css"> 
@import "${resource(dir:'js/dojo-release-1.6.1/dojox/grid/enhanced/resources/claro',
file:'EnhancedGrid.css')}";    
 </style> 
…
    <table dojoType="dojox.grid.EnhancedGrid" jsId="grid" store="myStore" 
           rowsPerPage="5" clientSort="true" style="width: 100%; height: 280px;" 
           onRowDblClick= "onRowDblClick"
           rowSelector="20px" plugins="{ pagination: {  
           pageSizes:['5','10','20'], 
           maxPageStep: 5,  
           descTemplate: '${message(code: 'default.paginateDescTemplate')}', 
           description: true,  
           sizeSwitch: true, 
           pageStepper: true , 
           gotoButton: true 
           }}"> 
      <thead> 
        <tr> 
          <th width="50px" field="id" >ID</th> 
          <th width="100px" field="employeeNumber"> 员工号 </th> 
          <th width="100px" field="name"> 姓名 </th> 
          <th width="100px" field="age"> 年龄 </th> 
          <th width="100px" field="onboardDate"> 入职日期 </th> 
        </tr> 
      </thead> 
    </table> 

在 employeeController 中加入方法,见清单 6:


清单 6. EnhancedGrid 对应的控制器代码
				
    def listJson = {              
        if(params.start?.isInteger()){ 
            params.put("offset",params.int('start'))            
        } 
        params.put("max",params.count?params.int('count'):5) 
        if(params.sort?.startsWith("-")){ 
            params.put("sort",params.sort.substring(1)) 
            params.put("order","desc") 
        } 
        def total = Employee.count();      
        def results = Employee.list(params) 
        def jsonData = [identifier:"id",numRows: total,items: results] 
        render jsonData as JSON   
    } 

这样就完成了 EnhancedGrid 和服务器,数据库的交互。以上代码实现了分页、排序、双击表中一行进入修改界面,效果如图 7:


图 7. EnhancedGrid 界面
图 7. EnhancedGrid 界面 

以上代码有两点需要注意:

  1. 从图 3 可以看出默认的日期格式是 yyyy-MM-dd HH:mm:ss z,这是由 messages.properties 中的 default.date.format 定义的。而对于 JSON 中的日期数据,格式化需要在 config.groovy 中加入如清单 7 的代码: 

    清单 7. JSON 的日期格式化代码
    				
     import grails.converters.JSON; 
     class BootStrap { 
        def init = { servletContext -> 
            JSON.registerObjectMarshaller(Date) { 
                return it?.format("yyyy-MM-dd") 
            } 
        } 
        ... 
    

  2. 这段代码有个小技巧。因为 ${} 在 gsp 文件中是保留字,如果把 pagination 插件的 descTemplate 属性值“${2} - ${3} 共 ${1}${0}” 直接写在 gsp 页面上会出错,所以写到 messages 文件里。
     default.paginateDescTemplate=${2} - ${3}  共 ${1}${0} 
    

使用 FilteringSelect 增强下拉框

Grails scaffold 为外键关联的对象做了下拉框供用户选择,而这个下拉框是个简单的 HTML select 控件(如图 5),用户只能从一堆下拉选项中肉眼找出要选的关联对象。本文例子中,用户就需要从一大堆的企业员工中肉眼找出要选的产品负责人。企业员工常常是成百上千人,要从 select 下拉框中找出一个很难,而且一次性从数据库读取所有员工的姓名到下拉框也很耗时。Dojo 为我们提供了一个类似于 HTML 的 select 控件:FilteringSelect,但它可以动态输入,并且按照输入值列出匹配的可选项,甚至可以按需设置加载选项的数量,下拉列表的选项可以从数据库动态获取。这样用户就能通过输入关键字,让程序帮我们找出要选的对象。

Dojo 的 ComboBox 跟 FilteringSelect 非常类似,不过 FilteringSelect 不允许用户输入可选项之外的值,而 ComboBox 可以输入任意值。所以这里我们选择 FilteringSelect。

首先对 create.gsp 和 edit.gsp 做修改,如清单 8:


清单 8. Dojo 的 FilteringSelect 代码
				
 <script type="text/javascript"> 
 dojo.require("dijit.form.FilteringSelect"); 
 dojo.require("dojox.data.QueryReadStore"); 
 var employeeSelector= new dojox.data.QueryReadStore({
    url:"${resource(dir:'employee')}/combo"}); 
 </script> 
…
 <input name="owner.id" dojoType="dijit.form.FilteringSelect" 
 store="employeeSelector"  placeHolder="请选择员工" pageSize="5" 
 autocomplete="false" value="${productInstance?.owner?.id}" 
 queryExpr="${message(code: 'default.queryExpr')}"></input> 

再为 employeeController 增加方法,从数据库获取下拉选项,如清单 9:


清单 9. FilteringSelect 对应的控制器代码
				
 def combo ={ 
        def criteria='',results 
        if(params.id){ 
            results = Employee.get(params.id) 
        } 
        if(params.name){ 
            criteria = params.name.replace('*','%') 
            results = 
            Employee.findAllByNameLikeOrEmployeeNumberLike(
 criteria,criteria, [max:params.count?(params.count+1):1,offset:params.start]) 
        }  
        render(contentType: "text/json") { 
            identifier = "id"
            label = "name"
            items = array{ 
                results.each {w -> 
                    item("id":w.id,"name":w.name) 
                } 
            } 
        }  
    } 

以上代码实现了按照员工姓名模糊查找选择员工(如图 8 左上),按照员工号模糊查找选择员工(如图 8 中),查找结果分页(如图 8 右),如果输入框的值不在员工中,有出错提示(如图 8 左下)。


图 8. FilteringSelect 界面
图 8. FilteringSelect 界面 

使用 DatePicker 增强日期选择

Grails scaffold 提供下拉框让用户选择年月日(如图 4),从下拉框中选较为麻烦。常常我们喜欢直接输入日期或者是查看日历找到想填的日期。如图 9 的日期选择控件就满足了我们的需要。这个界面漂亮操作简单的日期选择控件就是 Dojo 的 DatePicker,我们可以通过在页面加入以下代码来实现,见清单 10:


图 9. DatePicker 界面
图 9. DatePicker 界面 

清单 10. Dojo 的 DateTextBox 代码
				
 <input type="text" name="onboardDate" 
 dojoType="dijit.form.DateTextBox" required="true" 
 value="<g:formatDate date='${employeeInstance?.onboardDate}'/>" /> 

因为默认的 scaffold 时间控件的年月日是分 3 个字段分别上传,和我们用一个字段上传的数据结构是不同的,所以 controller 中需要对其做特殊转换,见清单 11:


清单 11. 使用 DateTextBox 后,save 方法添加的日期转换代码
				
    def save = {        
        if(params.onboardDate!=null&&!params.onboardDate.isEmpty()){ 
            try{ 
                params.onboardDate =Date.parse("yyyy-MM-dd",params.onboardDate) 
            }catch(Exception e){ 
                employeeInstance.errors.rejectValue("onboardDate", 
                "typeMismatch.java.util.Date", 
                [message(code: 'employee.label', default: 'Employee')] as Object[],
                 "日期格式不对") 
                render(view: "create", model: [employeeInstance: employeeInstance]) 
            } 
        } 
        def employeeInstance = new Employee(params) 
        ... 
    } 

客户端数据校验

Grails 的脚手架已经具备了服务器端数据校验的功能,但是每次都将数据提交到服务器再进行校验,性能太低了。为减轻服务器的负载,在浏览器端做 Javascript 校验,就非常必要。Dojo 正好提供了一个很好的数据检验框架。

用户输入数据的同时 Dojo 就会进行数据检验,一旦出错立即高亮显示出错信息。

例如用 dijit.form.NumberTextBox 可以进行数字的校验,下面的代码要求价格是大于等于 0 的数字,见清单 12:


清单 12. NumberTextBox 的数据验证
				
 <g:textField name="price" 
 value="${fieldValue(bean: productInstance, field: 'price')}" 
 dojoType="dijit.form.NumberTextBox" constraints="{min:0}" 
 required="true" invalidMessage="大于 0 的数字 ." /> 

另外,当表单提交时可以判断表单是否有效,如果包含无效数据将不提交表单。见清单 13:


清单 13. 表单的数据验证
				
 function validateData(form1){ 
  if(!form1.validate()) { 
          alert('请先纠正无效数据,然后再提交!'); 
          return false; 
  } 
  return true; 
 } 
…
 <g:form method="post" dojoType="dijit.form.Form" onsubmit="return validateData(this);" > 

以上代码的效果类似于图 8(左下)中的红边框及出错提示。

结束语

从前面的文章我们已经能窥见 Dojo 结合 Grails 开发 web application 的强大能力以及效率。此外 Dojo 还有很多优秀的布局控件,对话框,图表,菜单,多文件上传控件等等,能够实现非常复杂的企业级应用。即将发布的 Grails2.0 也添加了很多很好的特性。我们有理由相信 Dojo 结合 Grails 的前途非常广阔。如果你们觉得文中的代码有更好的实现方法,欢迎与我联系(zhyjzhan@cn.ibm.com)。

分享到:
评论

相关推荐

    Struts2 Spring Jpa Ajax Dojo crud小例子

    struts2 spring jpa ajxa dojo crud,练习增删改查

    Ajax Development With Grails Dojo

    Ajax Development With Grails Dojo www.grails-exchange.com © Sven Haiges, Yahoo! Inc. GRAILS & DOJO 3 Goals You know how to get started! You know how AJAX is supported by Grails Tags, Libraries...

    Grails 中文参考手册

    6.7.2 用Dojo实现Ajax 6.7.3 用GWT实现Ajax 6.7.4 服务端的Ajax 6.8 内容协商 7. 验证 7.1 声明约束 7.2 验证约束 7.3 客户端验证 7.4 验证和国际化 8. 服务层 8.1 声明式事务 8.2 服务的作用域 8.3 依赖注入和服务 ...

    dojo enhancedGrid pagination 分页实现

    利用dojo的enhancedGrid实现分页,利用静态数据加载和json文件方式分别加载,也利用XHR方式获取json文件数据生成grid,另外利用fetch实现分页的过滤、排序等功能。

    dojo dojo实例 dojo例子 dojo资料 dojo项目 dojo实战 dojo模块 dojo编程

    dojo dojo实例 dojo例子 dojo资料 dojo项目 dojo实战 dojo模块 dojo编程

    dojo快速入门文档

    dojo快速入门文档,该文档可以使初学者快速入门,领略dojo的风采。

    Dojo框架使用教程

    Dojo框架使用教程

    dojo链接数据mySQL,使用post方式发送数组

    dojo1.9使用post方式发送数组,链接数据库,并将返回的数据进行解析。

    AJAX之Dojo实现登陆框

    AJAX之Dojo实现登陆框

    dojo类机制实现原理分析

    本文将会介绍dojo类机制幕后的知识,其中会涉及到dojo类机制的实现原理并对一些关键方法进行源码分析,当然在此之前希望您能够对JavaScript和dojo的使用有些基本的了解。

    DOJO 学习笔记 dojo

    Dojo的基础对象和方法) 6 四、 Dojo学习笔记(4. dojo.string & dojo.lang) 9 五、 Dojo学习笔记(5. dojo.lang.array & dojo.lang.func & dojo.string.extras) 13 六、 Dojo学习笔记(6. dojo.io.IO & dojo.io....

    dojo精品中文教程(包一)

    很不错的中文教程!文件太大分3个包! 目录如下: dojo精品中文教程 Dojo.1.0 Practice Note [1] ...使用 Dojo 工具包和 JSON-RPC 构建企业 SOA Ajax 客户端 利用Dojo实现拖动(Drag and Drop)效果

    DOjo中文使用手册

    DOjo中文使用手册,Dojo中文使用手册

    dojo精品中文教程(全)

    分三个包上传时,第三个包好像传不上去,我给整合了一下,打在一个包里上传了! dojo精品中文教程 ...使用 Dojo 工具包和 JSON-RPC 构建企业 SOA Ajax 客户端 利用Dojo实现拖动(Drag and Drop)效果

    dojo精品中文教程(包二)

    很不错的中文教程!文件太大分3个包! 目录如下: dojo精品中文教程 Dojo.1.0 Practice Note [1] ...使用 Dojo 工具包和 JSON-RPC 构建企业 SOA Ajax 客户端 利用Dojo实现拖动(Drag and Drop)效果

    DOJO TableContainer实现表单布局.js

    DOJO TableContainer实现表单布局.js

    精通Dojo by Dojo之父

    Dojo是一个非常强大的、面向对象的、开源的JavaScript工具箱,它为开发富客户端Ajax应用提供了一套完整的小部件和一些特效操作。曾经有人这样说:“对于一个Web开发者而言,如果没有Dojo,他将是一个“残废”的...

    dojo文档 dojo文档 dojo文档

    dojo文档 dojo文档 dojo文档 dojo文档 dojo文档 dojo文档 dojo文档 dojo文档 dojo文档 dojo文档 dojo文档 dojo文档

    使用Dojo框架

    Dojo是一个用JavaScript语言实现的开源DHTML工具包。它是在几个项目捐助基础上建立起来的(nWidgets, Burstlib, f(m)),这也是为什么叫它a unified toolkit的原因。Dojo的目标是解决开发DHTML应用程序遇到的那些、...

    dojo实现登陆框架

    dojo 实现登陆框架 浪曦的那个谁讲的我忘了饿

Global site tag (gtag.js) - Google Analytics