Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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 graemerocher authored
View
72 ...e/src/main/groovy/org/codehaus/groovy/grails/orm/hibernate/metaclass/AbstractFindByPersistentMethod.java
@@ -2,20 +2,19 @@
import grails.gorm.DetachedCriteria;
import groovy.lang.Closure;
-import org.codehaus.groovy.grails.commons.GrailsApplication;
+import org.codehaus.groovy.grails.commons.*;
import org.codehaus.groovy.grails.orm.hibernate.HibernateDatastore;
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsHibernateUtil;
-import org.hibernate.Criteria;
-import org.hibernate.HibernateException;
-import org.hibernate.Session;
-import org.hibernate.SessionFactory;
+import org.hibernate.*;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.Restrictions;
+import org.hibernate.impl.CriteriaImpl;
import org.springframework.orm.hibernate3.HibernateCallback;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
public abstract class AbstractFindByPersistentMethod extends AbstractClausedStaticPersistentMethod {
@@ -23,6 +22,7 @@
public static final String OPERATOR_AND = "And";
public static final String[] OPERATORS = new String[]{ OPERATOR_AND, OPERATOR_OR };
private HibernateDatastore datastore;
+ private static final Map<String, Boolean> useLimitCache = new ConcurrentHashMap<String, Boolean>();
public AbstractFindByPersistentMethod(HibernateDatastore datastore, GrailsApplication application,
SessionFactory sessionFactory, ClassLoader classLoader,
@@ -37,18 +37,58 @@ protected Object doInvokeInternalWithExpressions(final Class clazz, String metho
final String operator = OPERATOR_OR.equals(operatorInUse) ? OPERATOR_OR : OPERATOR_AND;
return getHibernateTemplate().execute(new HibernateCallback<Object>() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
-
Criteria crit = buildCriteria(session, detachedCriteria, additionalCriteria, clazz, arguments, operator, expressions);
- return getResult(crit);
+ boolean useLimit = establishWhetherToUseLimit(clazz);
+ return getResult(crit, useLimit);
}
});
}
+ private boolean establishWhetherToUseLimit(Class clazz) {
+ boolean useLimit = true;
+ GrailsDomainClass domainClass = (GrailsDomainClass) application.getArtefact(DomainClassArtefactHandler.TYPE, clazz.getName());
+ if(domainClass != null) {
+ Boolean aBoolean = useLimitCache.get(domainClass.getName());
+ if(aBoolean != null) {
+ useLimit = aBoolean;
+ }
+ else {
+
+ for (GrailsDomainClassProperty property : domainClass.getPersistentProperties()) {
+ if((property.isOneToMany()||property.isManyToMany()) && property.getFetchMode() == GrailsDomainClassProperty.FETCH_EAGER) {
+ useLimit = false;
+ useLimitCache.put(domainClass.getName(), useLimit);
+ break;
+ }
+ }
+ }
+ }
+ return useLimit;
+ }
+
protected Object getResult(Criteria crit) {
- final List<?> list = crit.list();
- if (!list.isEmpty()) {
- return GrailsHibernateUtil.unwrapIfProxy(list.get(0));
+ CriteriaImpl impl = (CriteriaImpl) crit;
+ String entityOrClassName = impl.getEntityOrClassName();
+ GrailsClass domainClass = application.getArtefact(DomainClassArtefactHandler.TYPE, entityOrClassName);
+ boolean useLimit = establishWhetherToUseLimit(domainClass.getClazz());
+
+ return getResult(crit, useLimit);
+ }
+
+ private Object getResult(Criteria crit, boolean useLimit) {
+ if(useLimit) {
+ final List<?> list = crit.list();
+ if (!list.isEmpty()) {
+ return GrailsHibernateUtil.unwrapIfProxy(list.get(0));
+ }
+ }
+ else {
+ try {
+ return crit.uniqueResult();
+ } catch (NonUniqueResultException e) {
+ return null;
+ }
}
return null;
}
@@ -57,20 +97,26 @@ protected Criteria buildCriteria(Session session, DetachedCriteria<?> detachedCr
Closure<?> additionalCriteria, Class<?> clazz, Object[] arguments,
String operator, List<?> expressions) {
Criteria crit = getCriteria(datastore, application, session, detachedCriteria, additionalCriteria, clazz);
+
+ boolean useLimit = establishWhetherToUseLimit(clazz);
+
if (arguments.length > 0) {
if (arguments[0] instanceof Map<?, ?>) {
Map<?, ?> argMap = (Map<?, ?>)arguments[0];
GrailsHibernateUtil.populateArgumentsForCriteria(application, clazz, crit, argMap);
if (!argMap.containsKey(GrailsHibernateUtil.ARGUMENT_FETCH)) {
- crit.setMaxResults(1);
+ if(useLimit)
+ crit.setMaxResults(1);
}
}
else {
- crit.setMaxResults(1);
+ if(useLimit)
+ crit.setMaxResults(1);
}
}
else {
- crit.setMaxResults(1);
+ if(useLimit)
+ crit.setMaxResults(1);
}
if (operator.equals(OPERATOR_OR)) {
View
51 ...e-persistence/src/test/groovy/org/codehaus/groovy/grails/orm/hibernate/EagerFetchQueryResultsSpec.groovy
@@ -0,0 +1,51 @@
+package org.codehaus.groovy.grails.orm.hibernate
+
+import grails.persistence.Entity
+import spock.lang.Issue
+
+/**
+ */
+class EagerFetchQueryResultsSpec extends GormSpec {
+
+ @Issue('GRAILS-8915')
+ void "Test fetch eager association"() {
+ given:"a one-to-many with an eager association"
+ createSampleData()
+
+ when:"The association is queried"
+ def authors = EagerFetchQueryResultsBook.findByTitle('Book1').authors
+
+ then:"The correct results are returend"
+ ["Author1", "Author2"] as Set == authors.collect {it.name} as Set
+ }
+
+ public void createSampleData() {
+ EagerFetchQueryResultsBook book1 = new EagerFetchQueryResultsBook(title: 'Book1')
+ EagerFetchQueryResultsAuthor author1 = new EagerFetchQueryResultsAuthor(name: 'Author1')
+ author1.save(flush: true, failOnError: true)
+ book1.addToAuthors(author1)
+ EagerFetchQueryResultsAuthor author2 = new EagerFetchQueryResultsAuthor(name: 'Author2')
+ author2.save(flush: true, failOnError: true)
+ book1.addToAuthors(author2)
+ book1.save(flush: true, failOnError: true)
+ session.clear()
+ }
+
+ @Override
+ List getDomainClasses() {
+ [EagerFetchQueryResultsAuthor, EagerFetchQueryResultsBook]
+ }
+}
+
+@Entity
+class EagerFetchQueryResultsAuthor {
+ String name
+ static hasMany = [books:EagerFetchQueryResultsBook]
+}
+@Entity
+class EagerFetchQueryResultsBook {
+ String title
+ static hasMany = [authors:EagerFetchQueryResultsAuthor]
+ static belongsTo = [EagerFetchQueryResultsAuthor]
+ static fetchMode = [authors: 'eager']
+}
Please sign in to comment.
Something went wrong with that request. Please try again.