使用Hibernate过滤器插件在Grails中软删除实体

使用Hibernate过滤器插件在Grails中软删除实体,grails,gorm,soft-delete,hibernate-filters,Grails,Gorm,Soft Delete,Hibernate Filters,我一直在寻找一种方法来避免从数据库中删除我的用户,而是将它们标记为已删除,并且不将它们带回查询中 我发现了这个插件,这是一个伟大的工具的任务 但当我试图实现我的解决方案时,我遇到了同样的问题,这些问题的解决方案在互联网上没有(或者我找不到) 因此,接下来,我将描述解决软删除问题的方法。在本例中,我将让我的类用户将其delete()方法处理为软删除,将属性lowDate设置为对用户实例调用delete()时的实际日期。其理念是使用lowDate的用户GORM查询将忽略null 1) 安装所有Hib

我一直在寻找一种方法来避免从数据库中删除我的用户,而是将它们标记为已删除,并且不将它们带回查询中

我发现了这个插件,这是一个伟大的工具的任务

但当我试图实现我的解决方案时,我遇到了同样的问题,这些问题的解决方案在互联网上没有(或者我找不到)


因此,接下来,我将描述解决软删除问题的方法。

在本例中,我将让我的类用户将其delete()方法处理为软删除,将属性lowDate设置为对用户实例调用delete()时的实际日期。其理念是使用lowDate的用户GORM查询将忽略null

1) 安装所有Hibernate过滤器插件。在插件的页面上查找依赖项:。请看一下文档

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(),这是

但是当在beforeDelete内部调用save()时,save方法调用beforeDelete,依此类推,生成StackOverflow。我不知道为什么会这样

5) 在引导上启用筛选器:

class BootStrap {
    ...
    def init = { servletContext ->
        User.enableHibernateFilter('deletedFilter')
        environments {
            ...
        }
    }
...
}
仅此而已,它显示了现在的工作。为了测试功能性,这里有一些斯波克测试示例:

注意:“构建”方法来自构建测试数据插件

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' 
    }
}

希望这有帮助

对GORM中的实体进行软删除的另一种方法是使用以下插件:

它使用“已删除”标志将实体标记为已删除,并且不会显示在查询中。
这个插件使用了

以这种方式删除的模型可以从关联中获取吗?使用Grails编程已经很久了:),我应该做规范来检查一下。稍后,我将尝试找到该项目(希望在GH上),并检查它。非常好的问题顺便说一句。
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' 
    }
}