Обновить поле "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)"
}