1

我正在尝试找到一种将动态字段添加到 grails 域类的方法。我确实根据 Burt 的文章找到了动态域类插件,但这对我们的需求来说太过分了。

假设我们有一个域类 person:

class Person extends DynamicExtendableDomainObject {
    String firstName
    String lastName

    static constraints = {
        firstName(nullable: false, blank: false, maxSize: 50)
        lastName(nullable: false, blank: false)
    }
}

现在客户a想在其中也有一个生日字段。通过使用某种管理工具,他在数据库中添加了这个额外的字段。

客户b还希望有一个字段中间名,因此他正在将字段中间名添加到此人。

现在我们实现了一个DynamicExtendableDomainObject类,Person 类继承自该类。这会为每个继承自此的 Domain 类添加一个自定义字段,以将动态属性存储JSON在其中(有点像 Perl 中的 KiokuDB 存储它们)。

现在当 Person 被实例化时,我们希望将这些动态属性添加到 Person 类中,以便能够使用标准 Grails getter 和 setter 以及用于这些的模板函数。

因此,在客户a上,我们可以使用脚手架,而人员将输出 firstName、lastName、birthDate,在客户 b 上,脚手架将输出 firstName、lastName、middleName。

属性的存储将通过使用saveinterceptor, 将这些属性序列化为 JSON 并将它们存储在特殊字段中来实现。

但是我们还没有找到在运行时将这些 JSON 属性动态添加到域类的方法。有没有好的方法来处理这个?如果是这样,如何最好地实现这一点?

4

1 回答 1

2

您可以尝试通过在metaClass 中展开DynamicExtendableDomainObjectgetProperty()、setProperty()、setProperties() 来尝试将运行时的属性添加到类型的 DomainClass,然后使用 beforeUpdate()、beforeInsert() 和 afterLoad() 挂钩到 Persistence。

例如在 Bootstrap(或服务)中:

def yourDynamicFieldDefinitionService

for(GrailsClass c in grailsApplication.getDomainClasses()){
    if(DynamicExtendableDomainObject.isAssignableFrom(c.clazz)){
        Set extendedFields = yourDynamicFieldDefinitionService.getFieldsFor(c.clazz)

        //getProperty()
        c.clazz.metaClass.getProperty = { String propertyName ->
            def result
            if(extendedFields.contains(propertyName)){
                result = delegate.getExtendedField(propertyName)
            } else {
                def metaProperty = c.clazz.metaClass.getMetaProperty(propertyName)
                if(metaProperty) result = metaProperty.getProperty(delegate)
            }
            result
        }

        //setProperty()
        c.clazz.metaClass.setProperty = { propertyName , propertyValue ->
                    if(extendedFields.contains(propertyName)){
                        delegate.setExtendedField(propertyName, propertyValue)
                        delegate.blobVersionNumber += 1
                    } else {
                        def metaProperty = c.clazz.metaClass.getMetaProperty(propertyName)
                        if(metaProperty) metaProperty.setProperty(delegate, propertyValue)
                    }
                }

        //setProperties()
                def origSetProperties = c.clazz.metaClass.getMetaMethod('setProperties',List)
                c.clazz.metaClass.setProperties = { def properties ->
                    for(String fieldName in extendedFields){
                        if(properties."${fieldName}"){
                            delegate."${fieldName}" = properties."${fieldName}"
                        }
                    }
                    origSetProperties.invoke(delegate,properties)
                }
    }
}

abstract DynamicExtendableDomainObject {
    String yourBlobField
    Long blobVersionNumber //field to signal hibernate that the instance is 'dirty'

    Object getExtendedField(String fieldName){
        ...
    }

    void setExtendedField(String fieldName, Object value){
        ...
    }

    def afterLoad(){
        //fill your transient storage to support getExtendedField + setExtendedField 
    }

    def beforeUpdate(){
        //serialize your transient storage to yourBlobField
    }

    def beforeInsert(){
        //serialize your transient storage to yourBlobField
    }
}
于 2013-02-26T15:56:43.323 回答