Обновить поле "lastUpdated" в классе родительских доменов в Grails
У меня есть класс родительского домена, у которого есть hasMany
другого класса домена. И родительский, и дочерний классы домена имеют поля lastUpdated
и dateCreated
. Моя проблема в том, что когда я обновляю класс дочернего домена, мне нужен класс родительского домена, чтобы отразить это изменение и обновить его поле lastUpdated
.
Есть ли какое-либо сопоставление или другая конфигурация между родительским и дочерним элементами, которые предоставляет Grails для реализации этой функции?
Обновление
Я добавил следующие строки в класс дочернего домена:
def beforeUpdate = {
parent.lastUpdated = new Date()
}
Я также должен был убедиться в том, что в контроллере, когда я обновлял дочерний элемент, мне также пришлось сохранить родителя, чтобы сохранить новое поле lastUpdated
. Кажется, что это работает нормально, но мне все равно хотелось бы знать, есть ли сопоставление или что-то подобное, что бы это сделать.
Ответы
Ответ 1
У меня такое ощущение, что предлагаемая вами реализация будет ошибкой. Вы обновляете parent
, устанавливая дату lastUpdated
вручную, что может привести к тому, что grails обновит lastUpdated
снова после того, как проверит его. Если это произойдет, вы на самом деле закончите последнее время, которое произошло после установленной вами даты. В ходе тестирования это может быть всего несколько миллисекунд, но вы не можете этого гарантировать.
Не только это, но ваша реализация сложнее поддерживать, так как вы увеличили связь с родителями и детьми.
Могу ли я предложить другую реализацию? Поле LastUpdated должно представлять время, в течение которого был обновлен конкретный объект домена. Дата, которую вы ищете, не совсем то же самое, поэтому я бы не стал использовать существующее соглашение "неправильно". Похоже, что дата, которую вы хотите для родительского объекта, - это "последний раз, когда ребенок был изменен".
Используйте вместо этого формулу.
Чтобы сделать это, вы можете использовать формулу . С помощью формулы вы получаете именно то, что хотите, без прямого изменения родительского объекта, и вы все равно можете использовать динамические искатели и другие сахара Grails.
class Parent {
...
Date lastChildUpdated
static hasMany = [ children: Child ]
static mapping = {
...
lastChildUpdated formula: '(SELECT max(c.last_updated) FROM child c WHERE c.parent_id = id)'
}
}
GORM будет загружать значение формулы в каждый раз, когда вы читаете объект из базы данных. Теперь, когда вы сохраняете ребенка, родитель будет иметь точное значение для этого свойства, не касаясь родителя.
Ответ 2
Я использовал хак. Я добавил поле Long updateTrigger
в класс родительский и touch
:
class Parent {
Long updateTrigger
static mapping = {
autoTimestamp true
}
static constraints = {
updateTrigger(nullable:true)
}
public touch() {
if (updateTrigger == null) updateTrigger = 0
updateTrigger++
this
}
В действиях обновления/сохранения контроллера ребенка я просто вызываю:
child_instance.save() // save the child
child_instance.parent.touch().save() // updates parent time stamp
Это увеличит значение updateTrigger
, а save()
автоматически обновит поле lastUpdated
, благодаря autoTimestamp
, установленному на true
в сопоставлении. updatedTrigger
устанавливается как nullable, так что он не делает недействительной любую существующую таблицу базы данных и поэтому может быть добавлен в любое время в любой класс домена.
Ответ 3
В одном из моих проектов, где был домен.
В программе много рекламных материалов, и у нас есть подклассы AdvertisingMaterial, FlashAd, ImageAd и т.д. Пользователь хочет, чтобы была возможность фильтровать программы с flashAds, imageAds и т.д. Теперь мне нужно сделать фильтрацию на основе свойства класса, которое у нас есть в таблице базы данных (если таблица tablePerHierarchy истинна). Поэтому я сделал некоторые изменения в своем классе домена, чтобы получить это свойство.
class AdvertisingMaterial {
String className
static constraints = {
className(nullable: true)
}
static mapping = {
className formula: 'CLASS'
}
}
Теперь я могу использовать это поле className в моих динамических искателях и запросах критериев. Поэтому я могу сделать что-то вроде
List<AdvertisingMaterial>adMaterials=AdvertisingMaterial.findAllByClassName("com.project.FlashAd")
static mapping = {
fullName formula: "CONCAT(FIRST_NAME,' ',LAST_NAME)"
totalAmount formula: "SUM(AMOUNT)"
}