【问题标题】:What is the best way to process a large list of Domain Objects?处理大量域对象的最佳方法是什么?
【发布时间】:2013-09-18 15:02:37
【问题描述】:

处理大量域对象的最佳方法是什么? 例如,我有 'Users' 和 'Book' 域,并且 Book 对象有 READ 权限。 当我添加一本新书时,我想为这本书的所有用户设置阅读权限。 起初是一个代码:

def users = Users.findAll{ ... }
users.each { addPermission(book, it, READ) }  

我正在使用 Spring Security Core 和 ACL 插件。 但现在,我认为将 10000 个用户对象加载到内存并不是最好的方法。 我将使用 SCROLL 方法和 Criteria 中的 maxResults(???)。 所以我的问题是最好的方法是什么?如何确定 MaxResults 的最佳数量?

【问题讨论】:

  • 我猜你有充分的理由这样做,但你不能通过在图书所属的位置添加“BookGroup”然后授予用户阅读“BookGroup”中所有图书的权利来解决它吗?使用 findAll 似乎有点矫枉过正,但我​​不详细了解您的情况。
  • 我正在使用 Spring Security ACL 插件。 Acl_entry 表负责保留所有权限。如果我只有一个权限,你的建议很好 - 阅读。但如果我们有 WRITE、CREATE、DELETE ....
  • 总有一些障碍需要克服,如果这很容易,每个人都会开发系统。如果您使用的是 Spring Security,您不能控制谁可以使用角色做什么?
  • Marco,我了解您尝试使用 OOP(添加新类)来解决它。但我必须以同样的方式调用 addPermission(book, it, READ) 。所以我的问题不是如何改变我的模型。我只需要处理一个大列表。
  • 好的,知道了。我更了解你现在的来历。

标签: grails grails-orm


【解决方案1】:

对于这样的事情,我会进行批量更新。 ExecuteUpdate 允许您做这样的事情并且它的性能要高得多。查看此示例并根据您的需要对其进行自定义。

def updatedRecords = User.executeUpdate("update User set permission = 'READ' where somecriteriaOrNot ")

【讨论】:

  • 这种方式不适用于使用 Spring Security ACL 插件,因为 Acl_entry 表保留所有权限。我必须调用 addPermission(book, it, READ)。无论如何,谢谢。
【解决方案2】:

执行此操作的更多 Grails 方法是使用批处理。试试下面给出的例子: 编辑:改进的答案。现在,使用基于分页的批处理。

             def noOfObjectsTobeProcessedAtAtime=1000//Step or pagination size...

    List offsetMaxMapList = (0..User.count()-1).step(noOfObjectsTobeProcessedAtAtime).collect{[max:noOfObjectsTobeProcessedAtAtime,offset:it]}
    offsetMaxMapList.each{offsetMaxMap->
        addPermissionToUserInBatch(params)  

    }

    def addPermissionToUserInBatch(params){
        def batch = []
                def session
                def users = Users.createCriteria().list(params){}

                users.eachWithIndex { user, index ->
                    batch << user

                    if (batch.size() >= batchSize) {
                        User.withTransaction {
                            batch.each {User userObject ->
                             addPermission(book, userObject, READ)
                                }
                        }
                        batch.clear()

                    } else if (batch.size() < batchSize && (users.size() - index - 1) == 0) {
                        User.withTransaction {
                            batch.each {User userObject ->
                                 addPermission(book, userObject, READ)
                                }
                        }
                        batch.clear()
                    }
                    session = sessionFactory.getCurrentSession()
                    session.clear()
                }

    }

希望对你有帮助!!!

【讨论】:

  • 我猜你想控制一级缓存的大小。 see Hibernate doc 但是 def users = Users.findAll{ ... } 获取缓存中的所有用户,不是吗?
  • @AndriiOlenchenko:是的,你是对的。我已经对我的答案进行了编辑。现在,它不会一次加载所有对象。我知道它会使用更多的不。要求加载用户对象
  • 好的。但我认为我可以检索一批用户以避免每个用户的sql请求。
【解决方案3】:

谢谢大家。我想总结一下。我希望 ti 将成为我的模板。

def dc = new DetachedCriteria(Users).build{
    //some conditions of criteria 
}
def count = dc.count()

// Optional:            
// dc = dc.build{
//     projections { property('username') }
// }

def batchSize = 50 // Hibernate Doc recommends 10..50
0.step(count, batchSize){ offset-> 
    dc.list(offset:offset, max:batchSize).each{
       // doSmthWithTransaction(it)
    }
    //clear the first-level cache
    //def hiberSession = sessionFactory.getCurrentSession() 
    //hiberSession.clear()
    // or
    Users.withSession { session -> session.clear() }
}

附:我在这里不使用 Transaction,因为我在 doSmthWithTransaction 方法上使用它

【讨论】:

    猜你喜欢
    • 2010-12-02
    • 2011-12-19
    • 2018-03-29
    • 2018-08-30
    • 1970-01-01
    • 2011-06-05
    • 1970-01-01
    • 2016-06-27
    • 2010-09-06
    相关资源
    最近更新 更多