Сохранение карт и списков свойств как JSON в Grails
EDIT: метод onload() изменен на afterLoad(): иначе объекты могут не передаваться должным образом на карту.
В настоящее время я использую некоторые классы доменов с множеством динамических и сложных свойств, которые мне нужно постоянно обновлять и обновлять.
Я сохраняю их в структуре карты для каждого класса, так как это упрощает обращение к моим контроллерам и т.д.
Однако, поскольку Grails, похоже, не может сохранять сложные типы свойств, такие как List и Map в БД, я использую следующий подход для достижения этого через объекты JSON String:
class ClassWithComplexProperties {
Map complexMapStructure //not persisted
String complexMapStructureAsJSON //updated and synched with map via onload,beforeInsert,beforeUpdate
static transients = ['complexMapStructure']
def afterLoad() { //was previously (wrong!): def onLoad() {
complexMapStructure=JSON.parse(complexMapStructureAsJSON)
}
def beforeInsert() {
complexMapStructureAsJSON= complexMapStructure as JSON
}
def beforeUpdate() {
complexMapStructureAsJSON= complexMapStructure as JSON
}
static constraints = {
complexMapStructureAsJSON( maxSize:20000)
}
}
Это работает так долго, что я только загружаю данные из БД, но у меня возникают проблемы, когда я хочу сохранить свои изменения в БД. Например. когда я делаю следующее
/* 1. Load the json String, e.g. complexMapStructureAsJSON="""{
data1:[[1,2],[3,4]],//A complex structure of nested integer lists
data1:[[5,6]] //Another one
}""" :
*/
ClassWithComplexProperties c=ClassWithComplexProperties.get(1)
// 2. Change a value deep in the map:
c.complexMapStructure.data1[0][0]=7
// 3. Try to save:
c.save(flush:true)
Это обычно не работает, поскольку, я думаю, (?), GORM игнорирует запрос save() из-за того, что сама карта является временной, и никаких изменений в сохраняемых свойствах не обнаружено.
Я могу заставить его работать по назначению, если я взломаю шаг 3 выше и изменил его на:
// 3.Alternative save:
complexMapStructureAsJSON="" //creating a change in persisted property (which will be overwritten anyway by the beforeUpdate closure)
c.save(flush:true)
Для меня это не очень элегантное решение моей проблемы.
Вопросы:
- Существует ли более простой подход к сохранению моих сложных данных динамической карты?
- Если мне нужно сделать это так, как я сейчас делаю, есть ли способ избежать взлома на шаге 3?
Ответы
Ответ 1
Для опции 2 вы можете использовать событие beforeValidate
вместо событий beforeInsert
и beforeUpdate
, чтобы гарантировать, что изменение распространяется правильно.
class ClassWithComplexProperties {
Map complexMapStructure //not persisted
String complexMapStructureAsJSON //updated and synched with map via onload,beforeInsert,beforeUpdate
static transients = ['complexMapStructure']
def onLoad() {
complexMapStructure=JSON.parse(complexMapStructureAsJSON)
}
// >>>>>>>>>>>>>>
def beforeValidate() {
complexMapStructureAsJSON= complexMapStructure as JSON
}
// >>>>>>>>>>>>>>
static constraints = {
complexMapStructureAsJSON( maxSize:20000)
}
}
Ответ 2
Я, конечно, мало знаю о приложении, которое вы строите, но не повредит альтернативные модели хранения данных, в частности базы данных NOSQL. Grails также поддерживает их.
Ответ 3
Существует ли более простой подход к сохранению моих сложных данных динамической карты?
Grails может сохранять список и карту из коробки, вам не нужно писать сложный код конверсии и злоупотреблять Json.
Пример для карты:
class ClassWithComplexProperties {
Map<String, String> properties
}
def props = new ClassWithComplexProperties()
props.properties = ["foo" : "bar"]
props.save()
Пример для списка:
class ClassWithComplexProperties {
List<String> properties
static hasMany = [properties: String]
}
def props = new ClassWithComplexProperties()
props.properties = ["foo", "bar"]
props.save()
Я думаю, что это намного проще и чище, как с этим бороться.
Ответ 4
В ответ на
Существует ли более простой подход к сохранению моих сложных данных динамической карты?
Grails может сохранять Наборы, списки и Карты в базу данных. Это может быть более простой подход, чем обращение с конверсиями JSON. Чтобы карта сохранялась в базе данных, вам нужно включить ее в свойство hasMany.
Map complexMapStructure
static hasMany = [complexMapStructure: dynamicComplexPropertyObject]
В документации указано, что использование сумки может быть более эффективным.