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 クラスが継承するクラスを実装しました。これにより、これから継承する各ドメイン クラスにカスタム フィールドが追加され、動的プロパティがそのまま保存さJSONれます (Perl の KiokuDB がそれらを保存するようなものです)。

Person がインスタンス化されたら、これらの動的プロパティを Person クラスに追加して、標準の Grails ゲッターとセッター、およびこれらのテンプレート関数を使用できるようにします。

したがって、顧客aではスキャフォールディングを使用でき、人は firstName、lastName、birthDate を出力し、顧客 b ではスキャフォールディングは firstName、lastName、middleName を出力します。

プロパティの保存は、 を使用して実装され、saveinterceptorこれらのプロパティを JSON にシリアル化し、特別なフィールドに保存します。

しかし、実行時にこれらの JSON プロパティを動的にドメイン クラスに追加する方法はまだ見つかっていません。これを処理する良い方法はありますか?もしそうなら、これをどのように実装するのが最善ですか?

4

1 に答える 1

2

DynamicExtendableDomainObjectgetProperty()、setProperty()、setProperties() を metaClass で展開し、beforeUpdate()、beforeInsert()、および afterLoad() を使用して Persistence にフックすることにより、実行時にタイプの DomainClass にプロパティを追加することができます。

たとえば、ブートストラップ (またはサービス) では次のようになります。

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 に答える