Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

fix for GRAILS-8915 "Grails 2.0.1 only fetching first element from ea…

…ger collection"
  • Loading branch information...
commit 90334039faa74f4e74072133c4231c9c8593682f 1 parent 81884ec
graemerocher authored June 15, 2012
72  ...e/src/main/groovy/org/codehaus/groovy/grails/orm/hibernate/metaclass/AbstractFindByPersistentMethod.java
@@ -2,20 +2,19 @@
2 2
 
3 3
 import grails.gorm.DetachedCriteria;
4 4
 import groovy.lang.Closure;
5  
-import org.codehaus.groovy.grails.commons.GrailsApplication;
  5
+import org.codehaus.groovy.grails.commons.*;
6 6
 import org.codehaus.groovy.grails.orm.hibernate.HibernateDatastore;
7 7
 import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsHibernateUtil;
8  
-import org.hibernate.Criteria;
9  
-import org.hibernate.HibernateException;
10  
-import org.hibernate.Session;
11  
-import org.hibernate.SessionFactory;
  8
+import org.hibernate.*;
12 9
 import org.hibernate.criterion.Disjunction;
13 10
 import org.hibernate.criterion.Restrictions;
  11
+import org.hibernate.impl.CriteriaImpl;
14 12
 import org.springframework.orm.hibernate3.HibernateCallback;
15 13
 
16 14
 import java.sql.SQLException;
17 15
 import java.util.List;
18 16
 import java.util.Map;
  17
+import java.util.concurrent.ConcurrentHashMap;
19 18
 import java.util.regex.Pattern;
20 19
 
21 20
 public abstract class AbstractFindByPersistentMethod extends AbstractClausedStaticPersistentMethod {
@@ -23,6 +22,7 @@
23 22
     public static final String OPERATOR_AND = "And";
24 23
     public static final String[] OPERATORS = new String[]{ OPERATOR_AND, OPERATOR_OR };
25 24
     private HibernateDatastore datastore;
  25
+    private static final Map<String, Boolean> useLimitCache = new ConcurrentHashMap<String, Boolean>();
26 26
 
27 27
     public AbstractFindByPersistentMethod(HibernateDatastore datastore, GrailsApplication application,
28 28
                                           SessionFactory sessionFactory, ClassLoader classLoader,
@@ -37,18 +37,58 @@ protected Object doInvokeInternalWithExpressions(final Class clazz, String metho
37 37
         final String operator = OPERATOR_OR.equals(operatorInUse) ? OPERATOR_OR : OPERATOR_AND;
38 38
         return getHibernateTemplate().execute(new HibernateCallback<Object>() {
39 39
             public Object doInHibernate(Session session) throws HibernateException, SQLException {
40  
-
41 40
                 Criteria crit = buildCriteria(session, detachedCriteria, additionalCriteria, clazz, arguments, operator, expressions);
42 41
 
43  
-                return getResult(crit);
  42
+                boolean useLimit = establishWhetherToUseLimit(clazz);
  43
+                return getResult(crit, useLimit);
44 44
             }
45 45
         });
46 46
     }
47 47
 
  48
+    private boolean establishWhetherToUseLimit(Class clazz) {
  49
+        boolean useLimit = true;
  50
+        GrailsDomainClass domainClass = (GrailsDomainClass) application.getArtefact(DomainClassArtefactHandler.TYPE, clazz.getName());
  51
+        if(domainClass != null) {
  52
+            Boolean aBoolean = useLimitCache.get(domainClass.getName());
  53
+            if(aBoolean != null) {
  54
+                useLimit = aBoolean;
  55
+            }
  56
+            else {
  57
+
  58
+                for (GrailsDomainClassProperty property : domainClass.getPersistentProperties()) {
  59
+                    if((property.isOneToMany()||property.isManyToMany()) && property.getFetchMode() == GrailsDomainClassProperty.FETCH_EAGER) {
  60
+                        useLimit = false;
  61
+                        useLimitCache.put(domainClass.getName(), useLimit);
  62
+                        break;
  63
+                    }
  64
+                }
  65
+            }
  66
+        }
  67
+        return useLimit;
  68
+    }
  69
+
48 70
     protected Object getResult(Criteria crit) {
49  
-        final List<?> list = crit.list();
50  
-        if (!list.isEmpty()) {
51  
-            return GrailsHibernateUtil.unwrapIfProxy(list.get(0));
  71
+        CriteriaImpl impl = (CriteriaImpl) crit;
  72
+        String entityOrClassName = impl.getEntityOrClassName();
  73
+        GrailsClass domainClass = application.getArtefact(DomainClassArtefactHandler.TYPE, entityOrClassName);
  74
+        boolean useLimit = establishWhetherToUseLimit(domainClass.getClazz());
  75
+
  76
+        return getResult(crit, useLimit);
  77
+    }
  78
+
  79
+    private Object getResult(Criteria crit, boolean useLimit) {
  80
+        if(useLimit) {
  81
+            final List<?> list = crit.list();
  82
+            if (!list.isEmpty()) {
  83
+                return GrailsHibernateUtil.unwrapIfProxy(list.get(0));
  84
+            }
  85
+        }
  86
+        else {
  87
+            try {
  88
+                return crit.uniqueResult();
  89
+            } catch (NonUniqueResultException e) {
  90
+                return null;
  91
+            }
52 92
         }
53 93
         return null;
54 94
     }
@@ -57,20 +97,26 @@ protected Criteria buildCriteria(Session session, DetachedCriteria<?> detachedCr
57 97
             Closure<?> additionalCriteria, Class<?> clazz, Object[] arguments,
58 98
             String operator, List<?> expressions) {
59 99
         Criteria crit = getCriteria(datastore, application, session, detachedCriteria, additionalCriteria, clazz);
  100
+
  101
+        boolean useLimit = establishWhetherToUseLimit(clazz);
  102
+
60 103
         if (arguments.length > 0) {
61 104
             if (arguments[0] instanceof Map<?, ?>) {
62 105
                 Map<?, ?> argMap = (Map<?, ?>)arguments[0];
63 106
                 GrailsHibernateUtil.populateArgumentsForCriteria(application, clazz, crit, argMap);
64 107
                 if (!argMap.containsKey(GrailsHibernateUtil.ARGUMENT_FETCH)) {
65  
-                    crit.setMaxResults(1);
  108
+                    if(useLimit)
  109
+                        crit.setMaxResults(1);
66 110
                 }
67 111
             }
68 112
             else {
69  
-                crit.setMaxResults(1);
  113
+                if(useLimit)
  114
+                    crit.setMaxResults(1);
70 115
             }
71 116
         }
72 117
         else {
73  
-            crit.setMaxResults(1);
  118
+            if(useLimit)
  119
+                crit.setMaxResults(1);
74 120
         }
75 121
 
76 122
         if (operator.equals(OPERATOR_OR)) {
51  ...e-persistence/src/test/groovy/org/codehaus/groovy/grails/orm/hibernate/EagerFetchQueryResultsSpec.groovy
... ...
@@ -0,0 +1,51 @@
  1
+package org.codehaus.groovy.grails.orm.hibernate
  2
+
  3
+import grails.persistence.Entity
  4
+import spock.lang.Issue
  5
+
  6
+/**
  7
+ */
  8
+class EagerFetchQueryResultsSpec extends GormSpec {
  9
+
  10
+    @Issue('GRAILS-8915')
  11
+    void "Test fetch eager association"() {
  12
+        given:"a one-to-many with an eager association"
  13
+            createSampleData()
  14
+
  15
+        when:"The association is queried"
  16
+            def authors = EagerFetchQueryResultsBook.findByTitle('Book1').authors
  17
+
  18
+        then:"The correct results are returend"
  19
+            ["Author1", "Author2"] as Set == authors.collect {it.name} as Set
  20
+    }
  21
+
  22
+    public void createSampleData() {
  23
+        EagerFetchQueryResultsBook book1 = new EagerFetchQueryResultsBook(title: 'Book1')
  24
+        EagerFetchQueryResultsAuthor author1 = new EagerFetchQueryResultsAuthor(name: 'Author1')
  25
+        author1.save(flush: true, failOnError: true)
  26
+        book1.addToAuthors(author1)
  27
+        EagerFetchQueryResultsAuthor author2 = new EagerFetchQueryResultsAuthor(name: 'Author2')
  28
+        author2.save(flush: true, failOnError: true)
  29
+        book1.addToAuthors(author2)
  30
+        book1.save(flush: true, failOnError: true)
  31
+        session.clear()
  32
+    }
  33
+
  34
+    @Override
  35
+    List getDomainClasses() {
  36
+        [EagerFetchQueryResultsAuthor, EagerFetchQueryResultsBook]
  37
+    }
  38
+}
  39
+
  40
+@Entity
  41
+class EagerFetchQueryResultsAuthor {
  42
+    String name
  43
+    static hasMany = [books:EagerFetchQueryResultsBook]
  44
+}
  45
+@Entity
  46
+class EagerFetchQueryResultsBook {
  47
+    String title
  48
+    static hasMany = [authors:EagerFetchQueryResultsAuthor]
  49
+    static belongsTo = [EagerFetchQueryResultsAuthor]
  50
+    static fetchMode = [authors: 'eager']
  51
+}

0 notes on commit 9033403

Please sign in to comment.
Something went wrong with that request. Please try again.