Avoid choosing N + 1 in Grails / Hibernate

UPDATE . May be due to this Grails error in version 2.0.1: http://comments.gmane.org/gmane.comp.lang.groovy.grails.user/125400

UPDATE 2 . This makes me closer, but pagination doesn't work. In this case, all A are returned, each with its own list B and each B with its C. As soon as I add the offset and max, everything gets messed up. For example, in this case 10 A will not be returned. Perhaps 1 A and 9 B will be returned.

def results = A.list([fetch:[bees: "eager"], offset: 0, max: 10]);

Problem: I want to load all A, their associated B and B, related to C in one request. Getting all A and related B in one query is very simple. I am not sure how to load all the BCs as part of the same request. By design, the default mapping from B โ†’ C should be impatient, since I always want C when I load B. I thought setting up this mapping would solve the problem.

class A {
    static hasMany = [
            bees:  B,
    ]
}

class B {
    C c;

    static belongsTo = [a: A]

    // also tried this and every possible combination
    //static fetchMode = [c: 'eager']

    static mapping = {
        c fetch: 'join'
        // c lazy: 'false'
    }
}

class C {
    String someField;
}

Here is my request:

def results = A.executeQuery("from A a left join fetch a.bees",
                     [max: pageSize, offset: offset]);

If I now iterate over the results:

for (A a in results) {
   for (B b in a.bees) {
      println "B: " + b; // this is OK, B is already loaded
      println "B C: " + b.c.someField; // C not loaded
   }
}

When I repeat the results, the bcsomeField line will cause a โ€œselectionโ€ for each B. This is bad. I want to avoid this if possible. I sent a solution as an answer that uses a second query using a search map, but there should be a better way.

, " 2", . , ( /). Grails , "fetch:" join " /max, . . , - , " 1" - Grails 2.0.1.

?

+3
2

, A B.

, C ID - :

def cIds = [] as Set;

for (A a : results) {
    for (B b : a.bees) {
        // Note: getCId() is a special method which does not cause a query
        cIds.add(b.getCId());
    }
}

ID C, :

def cMap = [:]
def cees = C.getAll(cIds.toList());
for (C c : cees) {
    cMap[c.id] = c;
}

, C:

for (A a : results) {
    for (B b : a.bees) {
        println "The meaning of life is " + cMap[b.getCId()].someField;
    }
}

= 2 ( , C)

- , .

0

All Articles