Мягкое удаление объекта в Grails с помощью плагина Hibernate Filters

Я искал способ не удалять своих пользователей из БД, а вместо этого помечать их как удаленных и не возвращать их в запросы.

Я нашел этот плагин http://grails.org/plugin/hibernate-filter, который был отличный инструмент для задачи.

Но когда я попытался реализовать свое решение, я столкнулся с теми же проблемами, решений которых нет (или я не смог найти) в Интернете.

Итак, далее я описываю, как я решаю проблему обратимого удаления.


person Tomas Romero    schedule 17.09.2012    source источник


Ответы (2)


В этом примере я заставлю свой класс User обрабатывать его метод delete() как мягкое удаление, установив атрибут lowDate с фактической датой, когда delete() вызывается для экземпляра User. Идея состоит в том, что пользователи с lowDate != null будут игнорироваться запросами GORM.

1) Установите плагин Hibernate Filter. Найдите зависимость на странице плагина: http://grails.org/plugin/hibernate-filter. Взгляните на документацию.

2) Добавьте в источник данных следующее:

import org.grails.plugin.hibernate.filter.HibernateFilterDomainConfiguration

environments {
    development {
    dataSource {
        ...    
        configClass = HibernateFilterDomainConfiguration
    }
    }
    test {
    dataSource {
        ...
        configClass = HibernateFilterDomainConfiguration
    }
    }
    production {
    dataSource {
        ...
        configClass = HibernateFilterDomainConfiguration
    }
    }   
}

3) Определите свой фильтр в классе:

class User {
    ...
    String email
    Date lowDate
    static hibernateFilters = {
        deletedFilter(condition:'low_date is null', default:true)
    }
    static constraints = {
        ...
        lowDate nullable: true
    }
    ...
}

Примечание: посмотрите, как я определил условие. Он получает значение sql, поэтому будьте осторожны, называя атрибут так, как он есть в базе данных, а не именем класса.

Это заставит методы GORM избегать привлечения пользователей, у которых lowDate отличается от null.

4) Определите beforeDelele таким образом, чтобы избежать физического удаления:

class User {
    ...
    def beforeDelete() {
        SecUser.executeUpdate("update SecUser su set lowDate = :lowDate where email = :email",
                                [lowDate: new Date(), email: email])
        return false
    }
}

Примечание. Я попробовал более простой способ реализации beforeDelete(), который был

def beforeDelete() {
    this.lowDate = new Date()
    this.save()
    return false
}

Но когда save() вызывается внутри beforeDelete, вызывается метод сохранения beforeDelete и так далее, создавая StackOverflow. Я не знаю, почему это происходит.

5) Включите фильтр в BootStrap:

class BootStrap {
    ...
    def init = { servletContext ->
        User.enableHibernateFilter('deletedFilter')
        environments {
            ...
        }
    }
...
}

Вот и все, шоу теперь работает. Чтобы проверить функциональность, вот несколько тестов spock:

Примечание: метод build взят из плагина build-test-data.

class UserIntegrationSpec extends IntegrationSpec {

    def 'it should not find users marked as deleted'(){
        given: 'some users with lowDate and some withOut lowDate (=null)'
            User.build(firstName:'delUser1', lowDate: new Date())
            User.build(firstName:'user1')
            User.build(firstName:'delUser2', lowDate: new Date())
            User.build(firstName:'user2')
            def users = User.list()
        expect: 'it should only find the ones with lowDate == null'
            users.size() == 2
            users.every { it.firstName == 'user1' || it.firstName == 'user2' }      
    }

    def 'it should only delete users logically' (){
        given: 'a persisted user'
            def user = User.build(firstName: 'logiDelUser')
        when: 'user.delete() is called'
            user.delete(failOnError:true, flush:true)
            def deletedUser
            def users 
            User.withoutHibernateFilters(){
                users = User.list()
                deletedUser = User.find { firstName == 'logiDelUser' }
            }
        then: 'it should not delete the user from the DB, but set a low date instead'
            users.size() != 0
            deletedUser.lowDate != null
            deletedUser.firstName == 'logiDelUser' 
    }
}

Надеюсь, что это поможет!

person Tomas Romero    schedule 17.09.2012
comment
Могут ли удаленные таким образом модели быть извлечены из ассоциации? - person Alexander Suraphel; 09.07.2014
comment
Давно с тех пор, как я программировал с помощью Grails :), я должен сделать спецификацию, чтобы проверить это. Позже попробую найти проект (надеюсь на GH) и проверить. Очень хороший вопрос, кстати. - person Tomas Romero; 09.07.2014

Другой способ мягкого удаления сущностей в GORM — использование следующего плагина:

http://grails.org/plugin/logical-delete

Он использует флаг «удалено», чтобы пометить объект как удаленный и не отображается в запросах. Этот плагин использует плагин фильтра Hibernate.

person Julian    schedule 02.02.2014