我正在尝试找到一种向grails域类添加动态字段的方法。我确实找到了基于Burt文章的动态域类插件,但这对我们的需求来说太过分了。
假设我们有一个人类的域类:
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以及Templating函数。
所以在客户 a 上我们可以使用脚手架,而人们会输出firstName,lastName,birthDate,在客户b上,脚手架会输出firstName,lastName,middleName。
将使用saveinterceptor
实现属性的存储,将这些属性序列化为JSON并将它们存储在特殊字段中。
但是我们还没有找到一种在运行时将这些JSON属性动态添加到域类的方法。有没有一个好方法来处理这个?如果是这样,如何最好地实现这个?
答案 0 :(得分:2)
您可以尝试通过在metaClass中展开getProperty(),setProperty(),setProperties()然后使用beforeUpdate(),beforeInsert()和afterLoad,在运行时将属性添加到类型DynamicExtendableDomainObject
的DomainClass中()挂钩持久性。
例如在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
}
}