Grails命令对象数据绑定

Dón*_*nal 24 data-binding grails groovy command-objects

Grails 非常支持将请求参数绑定到域对象及其关联.这在很大程度上依赖于检测.id以数据库结束并自动加载数据库的请求参数.

但是,目前尚不清楚如何填充命令对象的关联.请看以下示例:

class ProductCommand {

    String name
    Collection<AttributeTypeCommand> attributeTypes 
    ProductTypeCommand productType
}
Run Code Online (Sandbox Code Playgroud)

该对象具有单端关联ProductTypeCommand和多端关联AttributeTypeCommand.所有属性类型和产品类型的列表都可从此接口的实现中获得

interface ProductAdminService {
    Collection<AttributeTypeCommand> listAttributeTypes();
    Collection<ProductTypeCommand> getProductTypes();
}
Run Code Online (Sandbox Code Playgroud)

我使用此界面填充GSP中的产品和属性类型选择列表.我还依赖 - 将此接口注入命令对象,并使用它来"模拟" 命令对象attributeTypesproductType属性

class ProductCommand {

    ProductAdminService productAdminService

    String name   

    List<Integer> attributeTypeIds = []
    Integer productTypeId

    void setProductType(ProductTypeCommand productType) {
        this.productTypeId = productType.id
    }

    ProductTypeCommand getProductType() {
        productAdminService.productTypes.find {it.id == productTypeId}        
    }

    Collection<AttributeTypeCommand> getAttributeTypes() {

        attributeTypeIds.collect {id ->
            productAdminService.getAttributeType(id)
        }
    }

    void setAttributeTypes(Collection<AttributeTypeCommand> attributeTypes) {
        this.attributeTypeIds = attributeTypes.collect {it.id}
    }
}
Run Code Online (Sandbox Code Playgroud)

实际发生的是,attributeTypeIdsproductTypeId属性绑定到相关的请求参数,getter/setter"模拟" productTypeattributeTypes属性.是否有更简单的方法来填充命令对象的关联?

And*_*ess 14

我在一些项目中看到的是使用Apache Commons Collections的Lazy*集合类.它使用这样的代码来懒惰地初始化命令关联:

class ProductCommand {

  String name
  String type

  List<AttributeTypeCommand> attributes = org.apache.commons.collections.list.LazyList.decorate(new ArrayList(), new org.apache.commons.collections.functors.InstantiateFactory(AttributeTypeCommand.class))
}

class AttributeTypeCommand {
  // ...
}
Run Code Online (Sandbox Code Playgroud)

通过上面给出的示例,GSP可以引用关联索引

<g:textField name="attributes[0].someProperty" ...
Run Code Online (Sandbox Code Playgroud)

这个工程即使是不存在的,因为指数在LazyList每得到(指数)调用评估清单是否已在该位置处的元素,如果没有,该列表会自动扩大规模,并从指定的工厂返回一个新的对象.

请注意,您还可以使用LazyMap来创建与惰性映射类似的代码:

http://commons.apache.org/collections/apidocs/org/apache/commons/collections/map/LazyMap.html

http://commons.apache.org/collections/apidocs/org/apache/commons/collections/list/LazyList.html

更新:

Groovy 2.0(尚未成为Grails发行版的一部分)将提供对lazy和eager列表的嵌入式支持.我写了一篇关于这个主题的博客文章:

http://blog.andresteingress.com/2012/06/29/groovy-2-0-love-for-grails-command-objects/

更新:

随着Grails 2.2.0的发布,Groovy 2.0成为了发行版的一部分.

http://blog.andresteingress.com/2012/06/29/groovy-2-0-love-for-grails-command-objects/

  • Grails必须首先执行"attributes [0]",这将导致List.get(index)调用,在LazyList/InstantiateFactory的情况下,将调用指定类的新实例.如果使用对象初始化列表,则此机制不适用. (2认同)

Chr*_*ris 7

你真的需要有attributeTypes和productType属性的子命令吗?你没有使用PropertyEditorSupport绑定的任何原因?例如:

public class ProductTypeEditor extends PropertyEditorSupport
{
    ProductAdminService productAdminService // inject somewhow
    void setAsText(String s)
    {
        if (s) value = productAdminService.productTypes.find { it.id == s.toLong() }
    }

    public String getAsText()
    {
        value?.id        
    }
}
Run Code Online (Sandbox Code Playgroud)

(以及对于attributeType对象类似的东西),并在编辑器注册器中注册它们:

import java.beans.PropertyEditorSupport
public class CustomEditorRegistrar implements PropertyEditorRegistrar {
    public void registerCustomEditors(PropertyEditorRegistry reg) {
        reg.registerCustomEditor(ProductType, new ProductTypeEditor())
        reg.registerCustomEditor(AttributeType, new AttributeTypeEditor())
    }
}
Run Code Online (Sandbox Code Playgroud)

并在您的resources.groovy中注册:

beans =
{
    customEditorRegistrar(CustomEditorRegistrar)
}
Run Code Online (Sandbox Code Playgroud)

然后在你的Cmd你只需:

class ProductCommand {
    String name
    List<AttributeType> attributeTypes = []
    ProductType productType
}
Run Code Online (Sandbox Code Playgroud)

如果你确实需要实际的子命令关联,那么我已经做了类似于@Andre Steingress建议的东西,结合PropertyEditorSupport绑定:

// parent cmd
import org.apache.commons.collections.ListUtils
import org.apache.commons.collections.FactoryUtils
public class DefineItemConstraintsCmd implements Serializable
{
    List allItemConstraints = ListUtils.lazyList([], FactoryUtils.instantiateFactory(ItemConstraintsCmd))
    //...
}    
// sub cmd
@Validateable
class ItemConstraintsCmd implements Serializable
{
    Item item // this has an ItemEditor for binding
    //...
}
Run Code Online (Sandbox Code Playgroud)

希望我没有误解你想要实现的目标:)


sim*_*sim 5

我遇到了嵌套命令对象的同样问题,所以我做了以下解决方法:

  1. 我明确地将其他域对象作为参数添加到我的控制器操作中
  2. 另外,为嵌套命令对象显式调用bindData()(通常,包装其他命令对象的命令对象将成功绑定其数据而无需显式绑定它,这取决于您的视图命名约定)
  3. 然后我在这些命令对象上调用.Validate()
  4. 使用这些对象来检查.hasErrors()的错误
  5. 要保存域对象,还要使用相应的命令对象明确指定每个嵌套属性

为了说明,这里是一个示例伪代码:

class CommandObjectBig{

    String name
    CommandObjectSmall details

    static constraints = {
      name (blank: false)
    }

}


class CommandObjectSmall{

    String address

    static constraints = {
      address (blank: false)
    }

}
Run Code Online (Sandbox Code Playgroud)

在控制器中:

.
.
.

def save = { CommandObjectBig cob, CommandObjectSmall cos ->

//assuming cob is bounded successfully by grails, and we only need to handle cos

bindData(cos, params.details)
cos.validate()

//then do you code logic depending on if cos or cob has errors

if(cob.hasErrors() || cos.hasErrors())
render(view: "create", model: [bigInstance: cob, smallInstance: cos])
}
else
{
 //create the Domain object using your wrapper command object, and assign its details
 //property it's value using cos command object instance, and call the save on you
 //command object and every thing should go smoothly from there
   .
   .
   .

}
.
.
.
Run Code Online (Sandbox Code Playgroud)
  • 希望将来版本的grails可以解决这个问题,并且可能允许开发人员添加一个可选的params或params作用域来分配给每个命令对象,它可能很有用:)