无法避免在Grails中具有多对多关系的n + 1选择

时间:2013-03-17 01:18:13

标签: grails gorm grails-2.0

我正在创建一个带有Grails的玩具Q& A网站,以便学习该平台。 我有两个域,帖子和标签,它们之间有很多关系。我想打印一个带有标签的帖子列表。

我不能使用延迟抓取,因为我会遇到N + 1选择问题

我不能使用急切的抓取,因为它使用左连接,我将无法正确分页结果。

因此我决定使用以下代码手动获取标记:


static def getList(params) {

        ArrayList questions = Question.list(params)

        def questionMap = [:]
        questions.each {
            questionMap.put(it.id, it)
        }

        if(questions.size()>0) {
            Tag.executeQuery('SELECT q.id, t FROM Tag t JOIN t.questions q \
                                WHERE q.id in ( :list ) ', [ list:questions.collect{ it.id } ] ).each { questionMap.get(it[0]).tags.add(it[1]) }
        }

        return questions
}

然而,当我在视图中打印标签时:

<g:each in="${questions}" var="question">
   ${question.title} 
   <g:each in="${question.tags}" var="tag">
      ${tag?.text}
   </g:each>
</g:each>

无论如何,每个问题都会执行一个查询! 这里推荐的方法是什么?

2 个答案:

答案 0 :(得分:1)

您的代码存在的问题是您没有对Tag查询的结果做任何事情。此外,它是一种更好的方法,可以为多对多关系建立连接类。例如,如果您看到Spring Security Core插件,则会显示UserRole和名为UserRole的联接类。 Here是示例类。

所以我的建议是:

class Tag {
...
}

class Question{
...
}

class QuestionTag implements Serializable {
  Tag tag
  Question question
  static mapping = {
    id composite: ['tag','question']
    ...
  }
  //need to override equals and hashCode
}

要存储标记的结果,您可以为您的类添加瞬态字段:

课堂问题{   def标签   静态瞬态= [&#39;标签&#39;]   //删除hasMany。 }

您现在可以执行HQL,在问题列表中查找问题实例并设置tags属性。由于您使用的是一个不返回单个类的HQL,因此结果不会映射为Tag对象,因此访问权限略有不同。

  

HQL查询可以返回域类实例或指定的数组   查询选择单个字段或计算值时的数据

答案 1 :(得分:0)

你在说

  

“我不能使用渴望的提取,因为它使用左连接和我   将无法正确分页结果。“

您可以使用session.createFilter为协会进行分页。

此示例(版权所有Burt Beckwith)来自Burt Beckwith's book Programming Grails来自“第5章,Hibernate,session.createFilter”

// example from Burt Beckwith's book "Programming Grails", (c) Burt Beckwith
class Branch {
    String name
    List visits
    static hasMany = [visits: Visit]

    List<Visit> getVisitsByPage(int pageSize, int pageNumber) {
        Branch.withSession { session ->
            session.createFilter(visits, '')
                    .setMaxResults(pageSize)
                    .setFirstResult(pageSize * pageNumber)
                    .list()
        }
    }
}

我建议购买this book