• 一步一步学grails:4 关系映射


    资源在此处下载。

    GORM-groovy 对象关系映射中,最常见的不是1:1关系,而是1M关系,也不是MM关系(因为MM关系比较麻烦,一般不能直接定义,通常都要拆分成两个1M关系来处理,并且要引入第3个类,否则会出现问题)。在本项目中,最利于理解1M的地方,就是“生产计划”planning域。

    这不是一个单独的域,他分别与其他4个域类都发生了111M关系。这些域都不能单独看待,而要整体看待。

    1、  下面来讨论这5个域类:

    1)        生产计划

    实际上就是根据订单而制订出的产品的具体生产过程。它包含了以下几个重要的内容:

    首先,既然是根据订单进行生产,那么生产计划中必须包含有订单对象(即产品编号-前面已定义了这个域)。

    其次,产品生产是要消耗一定原材料的,而且原材料可能不只一种,因此在生产计划中应当有物料表,以列出生产中可能用到的各种原材料。

    最后,一个产品可能需要使用到一个或一系列的生产工艺才能制造出来,因此还应当在生产计划中列出工艺表,以列出可能用到的生产工艺。

    生产计划中产品编号是一个相对简单属性,一个计划只针对一个产品,因此只会有一个产品编号,这只是1:1关系而已,前面已经定义了产品编号域,这里不再讨论。

    属性物料表和工艺表则不同,它们包含的都是集合,由多个对象构成列表,因此这是1:m1对多)关系,而集合里面的每个对象都是复杂对象,应当定义为域类。下面来一一讨论。当然,除了这些复杂对象外,生产计划还有一些简单的属性,比如简单的string int。该域类代码如下:

    class Planning {

       

        static constraints = {

        productNo()

        productName()

        spec(nullable:true)

        pieces()

        inspection(nullable:true)

        warehousing(nullable:true)

        shipment(nullable:true)

        factMaterial(nullable:true)

        consumeMaterial(nullable:true)

        accountsActual()

        accountsDue()

        rejection(nullable:true)

        linkman(nullable:true)

        remark(nullable:true)

        }

        static optionals=['spec','inspection','warehousing','shipment','factMaterial','consumeMaterial','accountsActual','accountsDue','rejection','linkman','remark']

        ProductionNo productNo   //产品编号

        String productName       //产品名

        String spec              //规格型号

       

        static hasMany=[materials:Material,craftList:Crafts]//两个1对多关系:与物料表表项,与工艺表表项

        int pieces            //件数

        String inspection     //检验

        String warehousing       //入库

        String shipment          //出库,领用

        String factMaterial      //实际用料

        String consumeMaterial   //材料消耗

        float accountsActual  //实收金额

        float accountsDue     //应收金额

        String rejection      //退货

        String linkman        //联系人

        String remark         //备注

       

    }

    注意其中的2个地方。一个是

    ProductionNo productNo   //产品编号

    这表明了一个1:1关系,因此我们还需要修改ProductionNo域类,加上这句:

    static belongTo=[Planning]//定义11关系,即1个生产编号对应1个生产计划

    另一个地方是

    static hasMany=[materials:Material,craftList:Crafts]//两个1对多关系:与物料表表项,与工艺表表项

    这定义了两个1:m关系。

    2)        物料表

    物料表实际上是1个以上的物料表表项组成的列表。物料表中的表项我们可以单独定义为一个域:

    class Material {//Material域:定义生产计划中某个产品的一种原材料-物料表表项

        static constraints = {

        }

        MaterialCost fee                       //定义一个材料的价格11关系   

        float amount=0                         //该材料所需数量

    }

    而一个MaterialCost定义了一个材料的价格:

    class MaterialCost {

        static constraints = {

        code(blank:false,nullable:false)//必填

        name(blank:false,nullable:false)//必填

        price(min:0.0f,nullable:false)//必填,值不能为负

        unit()

        remark()

        }

        static optionals=["unit","remark"]

        String code              //代码

        String name              //物料类型

        float price              //单价()

        String unit='/u516C/u65A4'      //单位,默认"公斤",native2acii转码

        String remark          //备注

        String toString(){

           String.format('%1$s%2$s',code,name)

           //"${coo.shortName}${prefix}-${sno}${suffix}"

        }   

    }

     

    3)        工艺表

    工艺表表项也可以定义为一个单独的域:

    class Crafts {//Crafts域:定义生产计划中某个产品的一种工艺 - 工艺表表项

     

        static constraints = {

        }

        ProcessingFee fee //定义一个加工费,11关系

        float hours       //定义工时

        int sequence   //定义序号

    }

    其中引用了ProcessiongFee加工费。加工费定义了一种加工工艺的费用,域定义如下:

    class ProcessingFee {//域:加工费

        static constraints = {

           code()

           workType(nullale:true)

           processFee(nullale:true)

           labourFee(nullale:true)

           remark(nullale:true)

        }

        String code          //代码

        String workType      //工种

        float processFee  //加工费

        float labourFee      //人工费

        String remark     //备注

        String toString(){

           "${code} - ${workType}"

        }

        static optionals=["workType","processFee","labourFee","remark"]

    }

    2、   生成脚手架

    定义完上述5个域类,再生成它们的脚手架代码。

    3、  修改“生产计划”控制器中的action代码

    由于我们希望在每编制一个新的“生产计划”都需要先编一个“产品编号”而不是直接就从编制“生产计划”开始,所以我们需要在PlanningController控制器修改action

    def create = {

                redirect(controller: 'productionNo', action: 'create')

    }

    这样当url指向planningControllercreate动作时,实际上是调用productionNoControllercreate动作。

    4、  修改“产品编号”控制器中的action

    在产品编号create页面中,create按钮将调用productionNoControllersave动作。我们也需要修改它,使得“产品编号”一编好,就显示“编制生产计划”页面:

    def save = {

                def productionNoInstance = new ProductionNo(params)

                if(!productionNoInstance.hasErrors()) {

                  if(productionNoInstance.save()){

                        flash.message = "ProductionNo ${productionNoInstance.id} created"

                        redirect(action:'create_2',controller:'planning',params:['productNo.id':productionNoInstance.id])

                  }

                }

                else {

                    render(view:'create',model:[productionNoInstance:productionNoInstance])

                }

            }

    注意划线部分的代码,redirect方法的前两个参数指定将跳转到PlanningControllercreate_2动作。别着急,这个create_2我们将在后面定义它。

    值得注意的是第三个参数。我们本来是想把新建的“产品编号”对象实例传递给create_2的,然而最终我们只需要把对象实例的id传给create_2就可以了。Create_2会把这个id值代表的对象传递到页面去。

    5、  编辑“生产计划”控制器中的action

    在其中新增一个action

    def create_2 = {

            def planningInstance = new Planning()

            planningInstance.properties = params

            render(view:'create',model:[planningInstance:planningInstance])

            //return ['planningInstance':planningInstance]

    }

    这个action 渲染页面create.gsp

    6、  修改create.gsp

    “生产计划”的属性有很多,我们在新建一个“生产计划”时,并不需要一次性把所有的属性都进行赋值——有的属性需要在新建时指定,而有的属性,很可能需要在生产完成之后再来填写它,因此需要修改create.gsp页面,使得在新建“生产计划”时不需要填写全部那么多的属性就可以新建一个生产计划。同时,需要注意的是产品编号这个字段,需要把它从下拉列表改为hidden控件。因为我们的业务要求用户在编制了产品编号后,才进行生产计划的编制——这时其实生产编号已经指定,不再需要用户从下拉列表中选取了:

    ……

    <tr class="prop">

                                    <td valign="top" class="name">

                                        <label for="productNo">产品编号:</label>

                                    </td>

                                    <td valign="top" class="value ${hasErrors(bean:planningInstance,field:'productNo','errors')}">

    ${planningInstance?.productNo}

    <input type="hidden" id="productNo" name="productNo.id" value="${planningInstance?.productNo?.id}" readonly/>

     …… 

    所以这里直接就把新建的产品编号显示出来,然后用一个隐藏域传递产品编号的id值。

    7、  修改show.gsp

    由于生产计划的字段有点多,我们需要对show.gsp进行重新排版,使显示更加美观:

    8、   

  • 相关阅读:
    【赵强老师】使用Docker Compose进行服务编排
    【赵强老师】Weblogic域和域的组成
    【赵强老师】管理Docker镜像
    3. 清理统一审计 AUD$UNIFIED 基表部份数据
    4. AUD$UNIFIED 基表及 分区键创建索引
    2.更改统一审计AUD$UNIFIED基表 默认表空间
    1.更改统一审计AUD$UNIFIED 分区为1天
    9. 将APEX18.2 升级到 APEX19.2 详细步骤
    1.2 安装中文语言包
    通过VBOX 导入系统工具 搭建APEX开发环境
  • 原文地址:https://www.cnblogs.com/encounter/p/2188554.html
Copyright © 2020-2023  润新知