2012-09-20 110 views
3

對於我目前正在研究的項目,我需要動態地將屬性添加到域類中,並稍後將其保留在數據庫中。一般來說,我需要一個連接到「普通」域類的鍵/值存儲。可悲的是我不能使用NoSQL數據庫(例如Redis)。如何在Grails中動態添加屬性/域到域類?

我的方法是通過確定它們中afterInsertafterUpdate,並將它們寫入到另一個表來處理一個save()的附加屬性 - 我寧願不要到域類,但一個額外的「現場」表中使用的地圖屬性(以更好地支持搜索)。

我嘗試添加使用的metaClass方法性質:

person.metaClass.middlename = "Biterius" 
assert person.middlename == "Biterius" // OK 

這工作,我可以在afterInsert /更新後的方法識別附加屬性,但似乎我不能改變的值之後 - 即,下面不工作:

person.middlename = "Tiberius" 
assert person.middlename == "Tiberius" // FAIL 

然後我嘗試過的Expando的方法由Expando的CL擴展Person類(直接(「Person extends Expando」)和一個抽象中間類(「Person extends AbstractPerson」和「AbstractPerson extends Expando」))。

def person = new Person() 
assert person in Person   // OK 
assert person in AbstractPerson // OK 
assert person in Expando   // OK 

這兩種變體都不起作用 - 我可以將值賦給任意的「屬性」,但值不會被存儲!

person.mynewproperty = "Tiberius" // no MissingPropertyException is thrown 
println person.mynewproperty  // returns null 

所以,我怎麼能在運行時編程方式將屬性添加到域類,改變它們,並在「字段」表afterInsert或更新後期間檢索它們以「手動」存儲它們?

還是我在做一些完全錯誤的事情?有其他/更簡單的方法來做到這一點?

+1

您是試圖保存元數據還是實際嘗試將列添加到表中動態?有點困惑 – chrislovecnm

+1

所以字段表會爲每個新屬性生成一張表?那是你在找什麼? –

+0

@chrislovecnm我想動態地添加一列到表。例如,將一個「中間名」添加到Person表中。 –

回答

0

把你的數據庫變成一個「NoSQL」是什麼?

在我的一個項目中,我只是使用String屬性將地圖存儲爲JSON對象。 對於Groovy來說,在地圖和JSON對象之間進行轉換並不是一個大問題。由於您可以像使用屬性的對象一樣訪問地圖,所以我發現此解決方案非常方便。

唯一的缺點:你必須提前計劃你的字符串屬性的大小...

更新:對不起,剛纔讀的要支持搜索...

怎麼樣

class Person { 
    ... 
    static hasMany = [extProperties:KeyValue] 
    ... 
    def invokeMethod(String name, args) { 
    if (name.startsWith('get')) { 
     //an unknown properties's getter is called 
    } 
    //add same for setter 
    } 
} 


class KeyValue { 
    String key 
    String value 
} 

我想這樣的模式會給你所有你需要的自由。即使沒有hasMany,您也可以使用invokeMethod來處理您的外部表...

getter和setter可以將您的值保存爲瞬態字符串屬性(static transients = ['myTransientProperty'])。此屬性應該在afterInsert /`afterUpdate'事件中可用。

+1

不錯的變體!我通過設置'invokeMethod'函數內的屬性並從'afterInsert' /'afterUpdate'函數中持久化它來使用它。只有不方便的是,我不能通過'person.middlename'使用groovy方式訪問它們,但必須使用getter和setter('person.getMiddlename()'等)。 –

+1

@JörgRech一旦獲得了getter和setter,就可以列出它們作爲瞬態和使用person.middlename –

+1

Jorg:要訪問它們,使用propertyMissing代替invokeMethod/methodMissing。 'def propertyMissing(String name){ KeyValue.findByName(name)?:throw new MissingPropertyException(name,delegate .class) }' – Raphael

0

爲什麼不在域對象上創建一個字符串映射並手動存儲您的額外數據?除非你存儲複雜的數據,否則你應該可以將任何你需要的東西放入字符串中。

+0

是的,這是另一個替代存儲addit離子性質。但是,這使得在單個屬性中搜索真的很困難。我不希望在域類中使用map屬性,而是使用額外的「Field」表來更好地支持搜索。 –

+0

然後,您可能需要創建另一個域類,充當動態字段的鍵/值查找表,並將其存儲爲您的域類中的多對多關聯。您可以使用條件查詢來查找單個屬性。 –