grails_Grails + EJB域模型

grails

Ruby on Rails继续在软件工程界引起很多争议,但是企业商店仍然相对缺乏说服力。 为什么? 我们想知道,“基于脚本语言构建的某些框架如何适合我的企业应用程序?!” 就Ruby on Rails而言,典型的论点是缺乏对企业服务(例如,分布式事务,消息传递等)的支持。 对于许多企业而言,没有这些服务的平台根本不是一个选择。

Grails旨在解决这些问题,并使企业的快速应用程序开发(RAD)合法化。 Grails基于Groovy构建,可与Java无缝集成。 它提供对业务所依赖的那些企业服务的直接访问,同时又为您的工具箱添加了功能强大的动态语言构造。

作为其企业集成能力的一个令人印象深刻的例子,Grails让您快速轻松地构建由现有EJB3实体bean支持的Web应用程序。 但是,它并不止于此。 Grails为您的实体bean提供了类固醇的丰收,但是它是完全动态的,而不会以任何方式更改您的EJB源代码。 Grails对象关系映射(GORM)建立在Hibernate 3上(但最终将提供对Java Persistence API的支持),并使用Groovy的元对象协议(MOP)将各种便捷的动态方法添加到静态对象Bean中。 这些方法不仅可以从Grails和Groovy中访问; 您的Java代码也可以调用这些方法! 我们突然拥有了JEE / EJB3的所有企业级功能以及RAD Web应用程序开发的好处!

因此,让我们看看构建由EJB3实体bean支持的Grails应用程序需要做什么。 在下面的步骤中,我们将创建一个新的Grails应用程序,将实体bean导入该应用程序,生成脚手架以快速为该实体bean构建默认的Web界面,然后探索Grails添加到其中的一些便捷的动态方法。实体bean。

首先,我们需要一个EJB3应用程序。 (好吧,Grails并不需要您以EJB3应用程序作为起点。但是,本文的一般假设是,您有兴趣将RAD Web开发并入您的EJB3项目中。)假设我们有一些EJB3为公司中的员工( EmployeeBean )和分配给这些员工的计算机( ComputerBean )建模的实体Bean。 (如果您在此过程中遇到任何问题,请确保参阅参考资料部分。在这里,您将找到Grails应用程序,使用这些实体bean的示例JEE应用程序以及其他有用工件的完整源代码。)

以下两个表支持我们的实体bean。 (在本示例中,我们将使用MySQL 5.0 。您可以使用此脚本来创建一个名为ejb3example的新数据库,并在其中填充这些表。)

让我们快速浏览一下我们公司的工蜂及其硬件的杂色阵容。

构建与该数据交互并与我们现有代码库集成的Web用户界面需要花费多长时间? 好吧,这不需要花费我们很长时间,但是我们肯定会接受这一过程需要大量的努力。 有了Grails,就不必那样了。

步骤1-安装Grails

由于EJB3依赖于JDK 5 ,因此您需要确保已安装JDK 5,并且JAVA_HOME环境变量指向JDK 5安装。

请按照以下快速步骤在系统上安装Grails。 (本文使用Grails 0.2.1-撰写本文时为当前稳定版本。)(此外,如果使用* nix系统,则在遇到安装问题时可能需要检出此线程 。)

第2步-“您好,Grails!”

  1. 在命令提示符下,导航到要创建Grails应用程序的目录。 然后,输入grails create-app。 当要求输入应用程序名称时,输入ejb3_grails
    jMac:~/dev jason$ grails create-app
    ...
    create-app:
        [input] Enter application name:
    ejb3_grails
    ...
    BUILD SUCCESSFUL
    Total time: 4 seconds
  2. 为了确保我们的环境一切正常,让我们启动我们的应用程序。 移至为我们的应用程序创建的新目录,然后输入grails run-app来启动该应用程序。
    jMac:~/dev jason$cd ejb3_grails
    jMac:~/dev/ejb3_grails jason$ grails run-app
    ...
    run-app:watch-context:
    
  3. 该应用程序现在正在等待我们的请求。 打开浏览器到http:// localhost:8080 / ejb3_grails / ,您应该看到此友好的消息,欢迎您使用Grails。

第3步-导入实体Bean

  1. Grails预先打包了HSQLDB,但是由于我们使用的是MySQL,因此我们有一些快速步骤可以告诉Grails如何与数据库进行对话。 首先,从http://www.mysql.com/products/connector/j/下载Java MySQL驱动程序。 我选择了当前可用于生产的版本,在撰写本文时,该版本为3.1.13。

  2. 打开zip文件,然后将mysql-connector-java-3.1.13-bin.jar文件解压缩到Grails应用程序的lib目录中-在本例中为ejb3_grails / lib 。 (请注意,根据您下载的驱动程序的版本,JAR文件的确切名称可能会有所不同。)

  3. 现在,我们准备告诉Grails在哪里可以找到我们的数据库。 在您选择的编辑器中打开ApplicationDataSource.groovy ,然后对其进行修改以匹配下面的设置。 您可以在ejb3_grails / grails-app / conf /中找到此文件。 (请注意,您需要将用户名密码更改为适用于您MySQL帐户的值。)

    import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration
    
    class ApplicationDataSource {
       def configClass = GrailsAnnotationConfiguration.class
       boolean pooling = true
       //String dbCreate = "create-drop" // one of 'create', 'create-drop','update'
       String url = "jdbc:mysql://localhost/ejb3example"
       String driverClassName = "com.mysql.jdbc.Driver"
       String username = "ejb3example"
       String password = "ejb3example"
    }

    除了指定连接设置之外,我们还需要定义configClass成员,以允许Grails支持在我们的实体bean中使用的注释。

    最后,我们要注释掉dbCreate设置。 此设置使您可以让Grails在运行时更新数据库架构,以使其与域类同步。 尽管这是一个强大的选项,但在本示例中我们根本不需要它。 通过注释掉此属性,我们指示Grails保留架构不变。

  4. 接下来,我们需要将实体bean- EmployeeBeanComputerBean-复制到我们的Grails项目中。 Grails在src / java目录中查找Java类。 确保创建与这些类的包名称匹配的完整目录结构。

    jMac:~/dev/ejb3_grails/src/java/com/jasonrudolph/ejb3example/entity jason$ ls
    ComputerBean.java       EmployeeBean.java

    长期而言,您将需要确保这些文件与这些类的正式副本保持同步(在JEE项目的源代码树中)。 为此,您可以轻松地指示构建脚本在构建时将这些文件从JEE项目复制到Grails项目中。

  5. Grails允许我们使用任何Java类,但是我们希望Grails对这些特定的Java类(即我们的实体bean)给予特殊对待。 我们希望Grails将这些类识别为我们的域类,并提供Grails域类随附的所有ORM和动态方法优点。 为此,我们将以下hibernate.cfg.xml文件添加到应用程序的hibernate目录中,以注册这些类。
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-configuration PUBLIC
    		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    		"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
    
    <hibernate-configuration>
        <session-factory>
            <mapping package="com.jasonrudolph.ejb3example.entity" />
            <mapping class="com.jasonrudolph.ejb3example.entity.EmployeeBean" />
            <mapping class="com.jasonrudolph.ejb3example.entity.ComputerBean" />
        </session-factory>
    </hibernate-configuration>

步骤4-产生脚手架

现在,我们准备开始进行实时节省。 到目前为止,我们所做的大多数事情都无法逃脱。 (无论您的框架多么聪明,您始终必须告诉它在哪里可以找到数据库。)但是,为用户界面建立一个良好且功能良好的起点不再是一项手动任务。

  1. 确保您位于项目的根目录中-在本例中为ejb3_grails 。 然后,输入grails generate-controller 。 当要求提供域名类名称时,请为我们的第一个实体Bean- com.jasonrudolph.ejb3example.entity.EmployeeBean输入完全限定的类名称
    jMac:~/dev/ejb3_grails jason$grails generate-controller
    ...
    input-domain-class:
        [input] Enter domain class name:
    com.jasonrudolph.ejb3example.entity.EmployeeBean
    ...
    [java] Generating controller for domain class
    [com.jasonrudolph.ejb3example.entity.EmployeeBean]
         [java] Controller generated at ./grails-
    app/controllers/EmployeeBeanController.groovy
    
    BUILD SUCCESSFUL
    Total time: 10 seconds
    
  2. 现在有了控制器,让我们生成相应的视图。 输入grails generate-views 。 当询问域名类名称时,输入com.jasonrudolph.ejb3example.entity.EmployeeBean
    jMac:~/dev/ejb3_grails jason$grails generate-views
    ...
    input-domain-class:
        [input] Enter domain class name:
    com.jasonrudolph.ejb3example.entity.EmployeeBean
    ...
    [java] Generating views for domain class [com.jasonrudolph.ejb3example.entity.EmployeeBean]
         [java] Generating list view for domain class
    [com.jasonrudolph.ejb3example.entity.EmployeeBean]
         [java] list view generated at /Users/jason/dev/ejb3_grails/./grails-
    app/views/employeeBean/list.gsp
         [java] Generating show view for domain class
    [com.jasonrudolph.ejb3example.entity.EmployeeBean]
         [java] Show view generated at /Users/jason/dev/ejb3_grails/./grails-
    app/views/employeeBean/show.gsp
         [java] Generating edit view for domain class
    [com.jasonrudolph.ejb3example.entity.EmployeeBean]
         [java] Edit view generated at /Users/jason/dev/ejb3_grails/./grails-
    app/views/employeeBean/edit.gsp
         [java] Generating create view for domain class
    [com.jasonrudolph.ejb3example.entity.EmployeeBean]
         [java] Create view generated at /Users/jason/dev/ejb3_grails/./grails-
    app/views/employeeBean/create.gsp
    
    BUILD SUCCESSFUL
    Total time: 11 seconds
    
  3. 重复此过程以为另一个实体bean(即com.jasonrudolph.ejb3example.entity.ComputerBean )生成控制器和视图。

  4. 现在,让我们运行我们的应用程序,看看只需付出很少的努力就能获得多少。 输入grails run-app ,我们就可以开始了。 打开浏览器到http:// localhost:8080 / ejb3_grails /。

  5. 好。 我们有两个控制器。 而且,正如此处的文字所建议的,我们最终将要用我们自己的自定义登录页面替换此页面。 现在,让我们继续到EmployeeBeanController

我们在这里看到了我们想要的大多数信息。 显然,“ 计算机”列需要做一些工作,否则,仅需进行一些外观上的更改,即可准备好此页面。

花一些时间浏览一下应用程序,看看到目前为止我们有什么。 尝试创建一个新员工,编辑一个员工,然后(如果您觉得自己特别有权力)终止其中一个可怜的人。 试用类似的功能来管理计算机。 在此过程中,记下您要更改的内容以及我们需要更改的内容。 同时,请务必考虑那些已经满足您需求的事物。 这些是您免费获得的功能!

...

做完了吗 好。 这是我拿的东西,我们需要在列表中,然后我们可以以后再谈那些可有可无的项目有。

  • 关系管理-脚手架显然在此领域中尝试过,但它不能满足我们的需求。 幸运的是,这很容易解决。 (哎呀。即使是Ruby on Rails脚手架也不能免费提供关系管理!)
  • 验证-它只是丢失。 干净利落。 但是,我们也可以快速添加它。

而已。 如果我们可以实施适当的关系管理和验证,我们将拥有一个功能齐全的Web应用程序来管理我们的实体bean。 之后,剩下的只是肉汁。 (尽管我们一定会在完成之前一定要品尝一些美味的肉汁。)

第5步-添加关系管理

对于关系管理,我们对应用程序有什么期望? 好吧,我想我们应该有能力...

  • 查看分配给员工的所有计算机。
  • 查看单台计算机的详细信息(包括其分配状态)。
  • 创建,更新和删除计算机(包括其分配状态)。

准备? 让我们开始吧。

  1. 在“ 雇员”页面上列出计算机确实没有任何意义,因此让我们删除该列。 打开grails-app / views / employeeBean / list.gsp ,然后删除该列。 现在,只需刷新浏览器,然后验证更新的页面即可。

  2. 接下来,单击显示链接以查看员工的详细信息。

    至少,我们需要清理为每台计算机列出的文本。 但是也许我们根本不想直接在此页面上看到计算机。 除了在此页面上不显示计算机之外,我们还包括指向该员工的计算机列表的链接。

    打开此页面的模板(即grails-app / views / employeeBean / show.gsp ),然后删除当前显示员工计算机的行。 然后,添加以下行以链接到显示该员工计算机的单独页面。

    <tr class="prop">
        <td colspan="2" align="left" class="name">
    	   <g:link controller='computerBean' action='showComputersByEmployee'
    	           id='${employeeBean.id}' >Show Computers</g:link>
        </td>
    </tr>

    我们正在使用Grails标记库来帮助我们。 link标记将生成一个到ComputerBeanController的链接,并调用一个我们需要定义的新动作showComputersByEmployee 。 该链接还将包含有关员工的ID。

    让我们刷新浏览器并查看我们的更改。

    好。 我们有我们的链接。 现在,我们需要为该链接定义新操作。 在编辑器中打开grails-app / controllers / ComputerBeanController.groovy 。 由于新操作将按员工搜索计算机,因此我们首先需要为EmployeeBean类添加import语句。

    import com.jasonrudolph.ejb3example.entity.EmployeeBean

    然后,我们只需添加新动作。

    def showComputersByEmployee = {
        render(view:'list', model:[ computerBeanList:
    	ComputerBean.findAllByEmployeeBean(EmployeeBean.get(params.id)) ])
    }

    此操作使我们可以轻松地在几行简洁的代码中很好地了解Groovy(和Grails)中可以说的内容。 在那几行中,我们告诉Grails,任何对showComputersByEmployee的调用都应...

    • 使用params.id从请求中获取员工ID
    • 使用EmployeeBean#get方法获取该员工ID的EmployeeBean
    • 使用ComputerBean#findAllByEmployeeBean方法查找分配给该员工的所有计算机
    • 将结果放入名为computerBeanList的对象中
    • 使用computerBeanList对象作为模型,在名为list的模板中呈现视图

    还记得在EmployeeBean中定义get方法和在ComputerBean中定义findAllByEmployeeBean方法吗? 没有? 你是对的。 这些方法只是Grails为您的域类提供的许多动态方法的一小部分。 我们将在以后进一步探讨这些项目。 在此期间,我们准备单击“ 显示计算机”链接。

    我们越来越近了。 我们仍然需要更改Employee Bean列中的文本。 我们当然希望这里具有更多人类意义。

    另外,我们可能更希望识别这些计算机所属的员工。 当前,我们正在重用列表模板(作为ComputerBean的脚手架的一部分生成),并且该模板主要用于列出所有计算机。 但是,我们总是可以定义一个单独的模板来显示此计算机子集,或者使列表模板更具动态性,以支持这两种情况。 现在,我们将把此功能收藏为很好但不是必需的。

    这将完成我们管理员工所需的所有更改。 现在,我们只需要清理计算机管理功能。

  3. 由于我们在这里,让我们清理计算机列表模板。 我们将其更改为显示员工的网络ID,而不是Employee Bean列中的当前文本。 打开grails-app / views / computerBean / list.gsp 。 找到可呈现Employee Bean列中文本的Groovy脚本...
    ${it.employeeBean}

    ...并对其进行更改以呈现员工的网络ID。

    ${it.employeeBean.networkId}

    刷新浏览器,然后看它的外观。 (“ 雇员”列在“ 品牌”和“ 模型”列之间似乎有点尴尬,所以我做了一些改动。请随意做。)

    列表模板已完成。 在显示模板上。

  4. 单击显示链接,然后评估我们需要更改的内容。

    看来我们只需要更改员工链接的文本即可。 到目前为止,我们几乎是专业人士。 因此,打开grails-app / views / computerBean / show.gsp ,然后找到呈现链接当前文本的脚本。

    ${computerBean?.employeeBean}

    就像我们对列表模板所做的一样,更改代码以显示员工的网络ID。

    ${computerBean?.employeeBean.networkId}

    让我们再次刷新浏览器并验证我们的更改。 (再次,“ 雇员”行在“ 品牌 ”行与“ 模型”行之间似乎有点尴尬,因此,欢迎您根据自己的意愿重新排列行。)

  5. 让我们继续编辑功能。

    我们开始在这里看到一个主题。 我们需要更改选择框以提供网络ID的列表。 在grails-app / views / computerBean / edit.gsp中找到<g:select>标记,然后进行调整。

    <g:select optionKey="id"
        from="${com.jasonrudolph.ejb3example.entity.EmployeeBean.list()}"
        name='employeeId'
        optionValue='networkId'
        value='${computerBean.employeeBean?.id}'>
    </g:select>

    通过将optionValue参数添加到标签,选择框中的文本采用了更有意义的形式。

    该视图现在是正确的,但是更新功能在视图之外也需要一些努力。 我们还需要增强控制器(即ComputerBeanController.groovy )。 如果用户更改了与计算机关联的员工,则需要确保我们正确保留这些关联更改。 换句话说,我们需要取消计算机与当前雇员的关联,并将其分配给新雇员。 增强的更新方法仅需要几行其他代码。

    def update = {
        def computerBean = ComputerBean.get( params.id )
        if(computerBean) {
               if (computerBean.employeeBean) {
                      computerBean.employeeBean.computers.remove(computerBean)
               }
    
               computerBean.properties = params
    
               def employeeBean = EmployeeBean.get(params.employeeId)
               employeeBean.computers.add(computerBean)
               computerBean.employeeBean = employeeBean
    
               if(computerBean.save()) {
                      redirect(action:show,id:computerBean.id)
               }
               else {
                      render(view:'edit',model:[computerBean:computerBean])
               }
        }
        else {
               flash.message = "ComputerBean not found with id ${params.id}"
               redirect(action:edit,id:params.id)
        }
    }

    保存您的更改,我们已经准备好尝试一下。 让我们更改这台计算机的品牌-现在是Lenovo-然后将其重新分配给John Doe。

  6. 当然,我们还需要能够将新计算机添加到我们的清单中。 因此,让我们单击New ComputerBean

    上次更改到“ 编辑”页面后,我们完全有资格清理此页面。 我们需要对选择框进行相同的调整。 打开grails-app / views / computerBean / create.gsp ,调整<g:select>标记,然后刷新浏览器。

    <g:select optionKey="id"
        from="${com.jasonrudolph.ejb3example.entity.EmployeeBean.list()}"
        name='employeeId'
        optionValue='networkId'
        value='${computerBean.employeeBean?.id}'>
    </g:select>

    正如我们看到的编辑功能一样,我们需要对控制器进行一些改进。 创建新计算机时,需要先将其分配给员工,然后再保存。 编辑ComputerBeanController.groovy以包括此更新的保存方法。

    def save = {
        def computerBean = new ComputerBean()
        computerBean.properties = params
    
        def employeeBean = EmployeeBean.get(params.employeeId)
        employeeBean.computers.add(computerBean)
        computerBean.employeeBean = employeeBean
    
        if(computerBean.save()) {
               redirect(action:show,id:computerBean.id)
        }
        else {
               render(view:'create',model:[computerBean:computerBean])
        }
    }

    回到浏览器,我们准备创建新计算机。 填写空白字段并创建。

    简现在拥有一台热门的新笔记本电脑。

  7. 最后我们需要的是删除功能。 这次我们不需要任何模板更改。 我们只需要向控制器添加一行即可。 当我们在ComputerBeanController.groovy中删除计算机时,我们还需要删除该计算机与员工的关联。 下面的第四行将解决这一问题。

    def delete = {
        def computerBean = ComputerBean.get( params.id )
    
        if(computerBean) {
               computerBean.employeeBean.getComputers().remove(computerBean)
               computerBean.delete()
               flash.message = "ComputerBean ${params.id} deleted."
               redirect(action:list)
        }
        else {
               flash.message = "ComputerBean not found with id ${params.id}"
               redirect(action:list)
        }
    }

    看起来简爱的新款MacBook已经被召回。 我们要删除它吗?

    我们终于得到它了! 只需几个简单的步骤,我们现在就在实体bean的顶部构建了一个可运行的Web应用程序。 当然,它仍然可以使用一些抛光剂。 至少,作为一个工作原型,这已经足够了。 更好的是,它是一个完全能够成长为最终产品的原型。

步骤6-定义验证规则

我们近期肯定需要做的一件事就是一些验证逻辑。 幸运的是,Grails提供了一种非常方便的声明式验证机制 。 对于每个实体bean,我们只需要添加一个Groovy脚本来定义适当的约束即可。 按照约定,Grails会查找与您的域类同名但以单词“ Constraints”结尾的脚本(例如EmployeeBeanConstraints.groovy )。

因此,让我们为每个实体bean定义约束。 至少我们需要约束来匹配我们的数据模型。 我们还将添加一些基本的业务验证。 例如,数据库要求网络ID的长度不得超过8个字符,但我们还将执行我们的业务规则,即网络ID的长度不得少于6个字符。

Grails在与我们的域类相同的目录中查找约束声明。 对于EmployeeBean类,我们需要创建一个名为EmployeeBeanConstraints.groovy的文件,并将其放置在ejb3_grails / src / java / com / jasonrudolph / ejb3example / entity /中 。 该文件应包括以下规则:

constraints = {
      networkId(length:6..8,blank:false,unique:true)
      firstName(maxLength:20,blank:false)
      lastName(maxLength:20,blank:false)
      startDate(nullable:false)
}

接下来,我们将定义ComputerBean类的约束。 该文件位于同一目录中,我们需要将其命名为ComputerBeanConstraints.groovy。

constraints = {
      serialNumber(maxLength:20,blank:false)
      brand(maxLength:20,blank:false)
      model(maxLength:20,blank:false)
}

为了使这些约束生效,我们需要重新启动应用程序。 在启动应用程序的控制台中,输入Control-C以停止应用程序。 然后,输入grails run-app ,我们准备测试验证。

现在,如果我们尝试使用无效的网络ID潜入新员工,则我们的应用程序知道不接受它。

自然,我们希望将错误消息更改为更多以业务为中心而不是技术含量较低的内容。 Grails提供了一个消息资源文件,供您定义自己的自定义错误消息

我们定义的约束仅代表Grails提供的验证的一小部分。 确保查看Grails文档以获取可用约束完整列表

第7步-获得动态

到目前为止,一切都向我们表明,我们可以比预期的速度更快地构建Web应用程序,并且我们已经看到了这个新兴框架的一些令人印象深刻的功能。 就是说,这是真正的魔术开始的地方。 回想一下当我们添加了查看分配给用户的所有计算机的功能时,我们对动态方法的简短提及。 让我们更深入地了解此功能。

一旦有了这样的基本CRUD功能,我们收到的第一个请求就是“我需要一种按_______来查看所有员工的方法。” 或者,“显示由_______制造的,序列号以_______开头的所有计算机。” 这些要求足够合理,但是构建这些简单报告通常需要花费多少精力? 很多时候,它需要某些会话bean中的新方法,我的Web框架的配置文件中甚至是某些SQL中的新映射,等等。我们不仅可以克服这些麻烦,而且可以做到准备Swift适应下一个变化。

首先,假设我们需要能够按姓氏查找所有雇员。 我们将在Employee List页面中添加一个新菜单选项,以将我们带到新的查询页面。 将此块添加到grails-app / views / employeeBean / list.gsp的菜单部分。

<span class="menuButton">
	<g:link action="search">Search Employees</g:link>
</span>

单击此菜单选项将在我们的控制器中调用名为search的新动作。 除了呈现搜索输入页面之外,我们不需要执行任何其他操作。 因此,在EmployeeBeanController.groovy中添加一个空方法来接受这些请求。

def search = {
}

这个空的动作告诉Grails搜索是对该控制器有效的动作。 当Grails收到此操作的请求时,它将仅在grails-app / views / employeeBean /中查找具有相同名称的模板(即search.gsp )并呈现其内容。

由于我们确实只希望为员工提供一个带有输入字段的简单表单,因此我们可以使用create.gsp模板作为一个很好的起点。 完成后,新的search.gsp模板应如下所示:

<html>
    <head>
         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
         <meta name="layout" content="main" />
         <title>Search Employees</title>
    </head>
    <body>
        <div class="nav">
            <span class="menuButton">
                <a href="${createLinkTo(dir:'')}">Home</a></span>
            <span class="menuButton">
            <g:link action="list">EmployeeBean List</g:link></span>
        </div>
        <div class="body">
           <h1>Search Employees</h1>
           <g:form action="showSearchResults" method="post" >
               <div class="dialog">
               <table>
                    <tr class='prop'>
                        <td valign='top' class='name'>
                            <label for='lastName'>Last Name Like:</label>
                        </td>
                        <td valign='top' class='value'>
                            <input type='text' name='lastName' value='' />
                        </td>
                   </tr>
               </table>
               </div>
               <div class="buttons">
                   <span class="formButton">
                       <input type="submit" value="Search"></input>
                   </span>
               </div>
           </g:form>
        </div>
    </body>
</html>

如果您认为我们不应该重复太多包装器内容(例如< head >标签等),那么您是对的。 Grails提供了各种布局机制来避免这种重复。 (我们认为这是对读者的一种练习。)同时,我们的搜索页面正在等待。 (虽然Grails通常提供这种更改的动态部署,但一些读者报告说需要重新启动应用程序才能成功访问新的搜索页面。为此,请在启动应用程序的控制台中输入Control-C。然后,输入grails run-app,然后进入搜索页面。)

您会注意到,在search.gsp中,我们指定了表单应将所有请求发布到名为showSearchResults的新动作(足够明显)。 因此,我们需要在EmployeeBeanController.groovy中定义该新操作。

def showSearchResults = {
    render(view:'list', model:[ employeeBeanList:
        EmployeeBean.findAllByLastNameLike("%" + params.lastName + "%") ])
}

因为我们只想显示匹配员工的列表,所以我们可以安全地使用现有的list.gsp模板。 这是Grails在几个简洁命令中提供重要功能的能力的另一个示例。 对showSearchResults操作的所有请求都将...

  • 使用params.lastName从请求中获取搜索条件
  • 获取与该姓氏匹配的员工的EmployeeBean对象列表
  • 将结果放入名为employeeBeanList的对象中
  • 在列表模板中使用employeeBeanList对象作为模型来呈现视图

当我们单击“搜索”时,肯定会得到匹配的结果。

当然,如果我们可以按姓氏搜索员工,为什么不也按名字搜索? 我们可以轻松地在搜索页面中添加一个新的输入字段。

<tr class='prop'>
    <td valign='top' class='name'>
        <label for='lastName'>First Name Like:</label>
    </td>
    <td valign='top' class='value'>
        <input type='text' name='firstName' value='' />
    </td>
</tr>

这很琐碎,但是我们必须在控制器中进行哪些更改? 很少 现在,我们将不再使用findAllByLastNameLike方法,而是调用findAllByLastNameLikeAndFirstNameLike

def showSearchResults = {
    render(view:'list', model:[ employeeBeanList:
        EmployeeBean.findAllByLastNameLikeAndFirstNameLike("%" + params.lastName + "%",
            "%" + params.firstName + "%") ])
}

Grails自动为您可以想到的每种查询组合提供动态查找器! 试试看。 通过上面的搜索,我们得出的查询恰好达到我们更新查询后的期望。

步骤8-建立您自己的标准

随着时间的流逝,您可能会发现您的需求变得越来越复杂。 我们如何处理需要对各种标准进行分组的查询? 例如,假设我想在以下位置找到所有员工...

  • 网络ID 部分匹配某些区分大小写的值, 或者
  • 名字姓氏完全符合各自的标准。

这种查询超出了我们刚刚看到的动态查找器的功能。 别担心。 这并不意味着您的应用程序必须承担任何其他复杂性。 Grails提供了非常灵活的Hibernate Criteria Builder ,可以满足这一需求,而不会牺牲到目前为止我们所看到的任何功能。

首先,让我们修改搜索页面以捕获新需求的输入。 如果我们编辑search.gsp并将当前输入字段替换为以下字段,它将为我们提供所需的内容。

<tr class='prop'>
    <td valign='top' class='name'><label for='lastName'>Network ID Like:</label></td>
    <td valign='top' class='value'>
        <input type='text' name='networkId' value='' />
    </td>
</tr>
<tr><td>-- or --</td></tr>
<tr class='prop'>
<td valign='top' class='name'><label for='lastName'>First Name Equals:</label></td>
<td valign='top' class='value'>
    <input type='text' name='firstName' value='' /></td>
</tr>
<tr class='prop'>
    <td valign='top' class='name'><label for='lastName'>Last Name Equals:</label></td>
    <td valign='top' class='value'>
        <input type='text' name='lastName' value='' />
    </td>
</tr>

现在让我们看一下实际的查询逻辑。 以下代码仅用几行就能满足我们的要求(绝对奇怪)。 (此代码应替换EmployeeBeanController.groovy中当前的showSearchResults方法。)

def showSearchResults = {
    def criteria = EmployeeBean.createCriteria()

    def results = criteria {
        or {
            ilike("networkId", "%" + params.networkId + "%")
            and {
                eq("firstName", params.firstName)
                eq("lastName", params.lastName)
            }
        }
    }

    render(view:'list', model:[ employeeBeanList: results.adaptee ])
}

即使您以前从未使用过Hibernate或Grails,也可以快速了解这里发生的事情。 在Groovy构建器中 ,每个组件都称为“节点”。 节点告诉我们,我们将匹配满足其子节点指定的一个或多个条件的任何行。 在这种情况下,它有两个子节点。

第一个子节点返回的结果满足对networkId属性的区分大小写的类似操作。

ilike("networkId", "%" + params.networkId + "%")

第二个子节点是and节点。 它返回与其所有子节点匹配的结果。 它的子节点查找与firstNamelastName属性完全匹配( eq )的行。

and {
    eq("firstName", params.firstName)
    eq("lastName", params.lastName)
}

这些节点一起满足了我们的查询要求。

如果分组运算符( 等)起初看起来很奇怪,那是因为Hibernate Criteria Builder在某种程度上类似于波兰符号语法,您可以在运算符之前指定运算符。 当然,这与Java使用关系运算符有所不同,但是在您进行前几次查询后,它应该变得相对直观。

现在,让我们来看一下它的作用。

在这里,我们正在寻找网络ID包含“ jr”或员工名称为“ John Doe”的所有员工。

当我们点击Search时 ,我们得到的正是这一点。

摘要

这个开发过程与您今天使用的方法相比如何? 在我们不断增加的每个功能中,您要触摸多少个组件才能在现有框架中进行相同的更改? XML文件? DAO? 表单/视图类? 其他? 通过维护这些额外的组件,您会得到什么? 极限灵活性? 如果是这样,您真的需要吗? 有些商店确实确实需要这种灵活性,但对于另外80%的商店,明智的默认设置(相对于配置而言是惯例)难道不值得提高生产率吗?

在此练习中,我们只编写了很少的代码,但是我们有一个功能强大且灵活的应用程序。 我们没有在现有实体bean中更改任何一行。 而且由于我们只写了很少的代码就可以做到这一点,所以我们需要维护的代码更少。 当需求再次改变时,明天我们将减少编写代码。 只要想想您的应用程序如此敏捷,您就能以多快的速度响应不断变化的业务需求!

资源资源

翻译自: https://www.infoq.com/articles/grails-ejb-tutorial/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

grails

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值