Skip to content

Commit d1c9b97

Browse files
author
nebolsin
committed
fix for GRAILS-1096
git-svn-id: https://svn.codehaus.org/grails/trunk@4101 1cfb16fd-6d17-0410-8ff1-b7e8e1e2867d
1 parent 6eefb3a commit d1c9b97

File tree

2 files changed

+156
-72
lines changed

2 files changed

+156
-72
lines changed

src/persistence/org/codehaus/groovy/grails/orm/hibernate/metaclass/ExecuteQueryPersistentMethod.java

Lines changed: 110 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -15,64 +15,130 @@
1515
package org.codehaus.groovy.grails.orm.hibernate.metaclass;
1616

1717
import groovy.lang.MissingMethodException;
18+
import groovy.lang.GString;
19+
import org.codehaus.groovy.grails.orm.hibernate.exceptions.GrailsQueryException;
20+
import org.hibernate.HibernateException;
21+
import org.hibernate.Query;
22+
import org.hibernate.Session;
23+
import org.hibernate.SessionFactory;
24+
import org.springframework.orm.hibernate3.HibernateCallback;
1825

26+
import java.sql.SQLException;
1927
import java.util.*;
2028
import java.util.regex.Pattern;
2129

22-
import org.codehaus.groovy.grails.commons.GrailsClassUtils;
23-
import org.codehaus.groovy.grails.orm.hibernate.exceptions.GrailsQueryException;
24-
import org.hibernate.SessionFactory;
25-
2630
/**
2731
* Allows the executing of abituary HQL queries
28-
*
29-
* eg. Account.executeQuery( "select distinct a.number from Account a where a.branch = ?", 'London' )
30-
* or Account.executeQuery( "select distinct a.number from Account a where a.branch = :branch", [branch:'London'] )
31-
*
32+
* <p/>
33+
* eg. Account.executeQuery( "select distinct a.number from Account a where a.branch = ?", 'London' ) or
34+
* Account.executeQuery( "select distinct a.number from Account a where a.branch = :branch", [branch:'London'] )
35+
*
3236
* @author Graeme Rocher
3337
* @author Sergey Nebolsin
34-
* @since 30-Apr-2006
3538
* @see <a href="http://www.hibernate.org/hib_docs/reference/en/html/queryhql.html">http://www.hibernate.org/hib_docs/reference/en/html/queryhql.html</a>
39+
* @since 30-Apr-2006
3640
*/
3741
public class ExecuteQueryPersistentMethod
38-
extends AbstractStaticPersistentMethod {
42+
extends AbstractStaticPersistentMethod {
43+
44+
private static final String METHOD_SIGNATURE = "executeQuery";
45+
private static final Pattern METHOD_PATTERN = Pattern.compile( "^executeQuery$" );
46+
47+
public ExecuteQueryPersistentMethod( SessionFactory sessionFactory,
48+
ClassLoader classLoader ) {
49+
super( sessionFactory, classLoader, METHOD_PATTERN );
50+
}
51+
52+
protected Object doInvokeInternal( Class clazz, String methodName, Object[] arguments ) {
53+
checkMethodSignature( clazz, methodName, arguments );
54+
55+
final String query = arguments[0].toString();
56+
final Map paginateParams = extractPaginateParams( arguments );
57+
final List positionalParams = extractPositionalParams( arguments );
58+
final Map namedParams = extractNamedParams( arguments );
59+
60+
return getHibernateTemplate().executeFind( new HibernateCallback() {
61+
public Object doInHibernate( Session session ) throws HibernateException, SQLException {
62+
Query q = session.createQuery( query );
63+
// process paginate params
64+
if( paginateParams.containsKey( ARGUMENT_MAX ) ) {
65+
q.setMaxResults( ((Number)paginateParams.get( ARGUMENT_MAX ) ).intValue() );
66+
}
67+
if( paginateParams.containsKey( ARGUMENT_OFFSET ) ) {
68+
q.setFirstResult( ((Number)paginateParams.remove( ARGUMENT_OFFSET )).intValue() );
69+
}
70+
// process positional HQL params
71+
int index = 0;
72+
for( Iterator iterator = positionalParams.iterator(); iterator.hasNext(); index++ ) {
73+
Object parameter = iterator.next();
74+
q.setParameter( index, parameter );
75+
}
76+
// process named HQL params
77+
for( Iterator iterator = namedParams.entrySet().iterator(); iterator.hasNext(); ) {
78+
Map.Entry entry = (Map.Entry) iterator.next();
79+
if( !( entry.getKey() instanceof String ) )
80+
throw new GrailsQueryException( "Named parameter's name must be of type String" );
81+
String parameterName = (String) entry.getKey();
82+
Object parameterValue = entry.getValue();
83+
if( Collection.class.isAssignableFrom( parameterValue.getClass() )) {
84+
q.setParameterList( parameterName, (Collection) parameterValue );
85+
} else if( parameterValue.getClass().isArray() ) {
86+
q.setParameterList( parameterName, (Object[]) parameterValue );
87+
} else if( parameterValue instanceof GString ) {
88+
q.setParameter( parameterName, parameterValue.toString() );
89+
} else {
90+
q.setParameter( parameterName, parameterValue );
91+
}
92+
}
93+
return q.list();
94+
}
95+
} );
96+
}
3997

40-
private static final String METHOD_SIGNATURE = "executeQuery";
41-
private static final Pattern METHOD_PATTERN = Pattern.compile("^executeQuery$");
98+
private void checkMethodSignature( Class clazz, String methodName, Object[] arguments ) {
99+
boolean valid = true;
100+
if( arguments.length < 1 ) valid = false;
101+
else if( arguments.length == 3 && !(arguments[2] instanceof Map) ) valid = false;
102+
else if( arguments.length > 3 ) valid = false;
42103

43-
public ExecuteQueryPersistentMethod(SessionFactory sessionFactory,
44-
ClassLoader classLoader) {
45-
super(sessionFactory, classLoader, METHOD_PATTERN);
46-
}
104+
if( !valid ) throw new MissingMethodException( METHOD_SIGNATURE, clazz, arguments );
105+
}
47106

48-
protected Object doInvokeInternal(Class clazz, String methodName, Object[] arguments) {
49-
// if no arguments passed throw exception
50-
if (arguments.length == 0)
51-
throw new MissingMethodException(METHOD_SIGNATURE, clazz, arguments);
107+
private Map extractPaginateParams( Object[] arguments ) {
108+
Map result = new HashMap();
109+
int paginateParamsIndex = 0;
110+
if( arguments.length == 2 && arguments[1] instanceof Map ) paginateParamsIndex = 1;
111+
else if( arguments.length == 3 ) paginateParamsIndex = 2;
112+
if( paginateParamsIndex > 0 ) {
113+
Map sourceMap = (Map) arguments[paginateParamsIndex];
114+
if( sourceMap.containsKey( ARGUMENT_MAX )) result.put( ARGUMENT_MAX, sourceMap.get(ARGUMENT_MAX));
115+
if( sourceMap.containsKey( ARGUMENT_OFFSET )) result.put( ARGUMENT_OFFSET, sourceMap.get(ARGUMENT_OFFSET));
116+
}
117+
return result;
118+
}
52119

53-
if (arguments.length == 1) {
54-
return getHibernateTemplate().find(arguments[0].toString());
55-
} else if (arguments.length == 2) {
56-
if (arguments[1] instanceof Collection) {
57-
return getHibernateTemplate().find(arguments[0].toString(), GrailsClassUtils.collectionToObjectArray((Collection) arguments[1]));
58-
} else if (arguments[1] instanceof Map) {
59-
Map paramsMap = (Map) arguments[1];
60-
String[] paramNames = new String[paramsMap.size()];
61-
Object[] paramValues = new Object[paramsMap.size()];
62-
int index = 0;
63-
for (Iterator it = paramsMap.entrySet().iterator(); it.hasNext();) {
64-
Map.Entry entry = (Map.Entry) it.next();
65-
if (!(entry.getKey() instanceof String))
66-
throw new GrailsQueryException("Named parameter's name must be of type String");
67-
paramNames[index] = (String) entry.getKey();
68-
paramValues[index++] = entry.getValue();
69-
}
70-
return getHibernateTemplate().findByNamedParam(arguments[0].toString(), paramNames, paramValues);
71-
} else {
72-
return getHibernateTemplate().find(arguments[0].toString(), arguments[1]);
73-
}
74-
}
75-
throw new MissingMethodException(METHOD_SIGNATURE, clazz, arguments);
76-
}
120+
private List extractPositionalParams( Object[] arguments ) {
121+
List result = new ArrayList();
122+
if( arguments.length < 2 || arguments[1] instanceof Map ) return result;
123+
else {
124+
if( arguments[1] instanceof Collection ) {
125+
result.addAll( (Collection) arguments[1] );
126+
} else if( arguments[1].getClass().isArray() ) {
127+
result.addAll( Arrays.asList( (Object[]) arguments[1] ) );
128+
} else {
129+
result.add( arguments[1] );
130+
}
131+
}
132+
return result;
133+
}
77134

135+
private Map extractNamedParams( Object[] arguments ) {
136+
Map result = new HashMap();
137+
if( arguments.length < 2 || !(arguments[1] instanceof Map) ) return result;
138+
result.putAll( (Map) arguments[1] );
139+
// max and offset are processed by paginate params
140+
result.remove( ARGUMENT_MAX );
141+
result.remove( ARGUMENT_OFFSET );
142+
return result;
143+
}
78144
}

test/persistence/org/codehaus/groovy/grails/orm/hibernate/PersistentMethodTests.java

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -844,19 +844,19 @@ public void testExecuteQueryMethod() {
844844
obj.setProperty( "id", new Long(1) );
845845
obj.setProperty( "firstName", "fred" );
846846
obj.setProperty( "lastName", "flintstone" );
847-
848847
obj.invokeMethod("save", null);
849848

850-
GroovyObject obj2 = (GroovyObject)domainClass.newInstance();
851-
obj2.setProperty( "id", new Long(2) );
852-
obj2.setProperty( "firstName", "wilma" );
853-
obj2.setProperty( "lastName", "flintstone" );
849+
obj = (GroovyObject)domainClass.newInstance();
850+
obj.setProperty( "id", new Long(2) );
851+
obj.setProperty( "firstName", "wilma" );
852+
obj.setProperty( "lastName", "flintstone" );
853+
obj.invokeMethod("save", null);
854854

855-
obj2.invokeMethod("save", null);
855+
MetaClass domain = obj.getMetaClass();
856856

857857
// test query without a method
858858
try {
859-
obj.getMetaClass().invokeStaticMethod(obj, "executeQuery", new Object[] {} );
859+
domain.invokeStaticMethod(obj, "executeQuery", new Object[] {} );
860860
fail("Should have thrown an exception");
861861
}
862862
catch(Exception e) {
@@ -865,43 +865,54 @@ public void testExecuteQueryMethod() {
865865

866866
// test query with too many params
867867
try {
868-
obj.getMetaClass().invokeStaticMethod(obj, "executeQuery", new Object[] { "1", "2", "3"} );
868+
domain.invokeStaticMethod(obj, "executeQuery", new Object[] { "query", "param", new HashMap(), "4" } );
869+
fail("Should have thrown an exception");
870+
}
871+
catch(Exception e) {
872+
//expected
873+
}
874+
875+
// test query with wrong third param type (must be Map)
876+
try {
877+
domain.invokeStaticMethod(obj, "executeQuery", new Object[] { "query", "param", "wrong third param" } );
869878
fail("Should have thrown an exception");
870879
}
871880
catch(Exception e) {
872881
//expected
873882
}
874883

875884
// test find with a query
876-
Object returnValue = obj.getMetaClass().invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p" });
877-
assertNotNull(returnValue);
878-
assertEquals(ArrayList.class,returnValue.getClass());
885+
Object returnValue = domain.invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p" });
886+
assertNotNull( returnValue );
887+
assertEquals( ArrayList.class, returnValue.getClass() );
879888
List listResult = (List)returnValue;
880889
assertEquals(2, listResult.size());
881890

891+
// test find with a query and paginate params
892+
Map paginateParams = new HashMap();
893+
paginateParams.put( "max", new Integer(1) );
894+
listResult = (List)obj.getMetaClass().invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p order by p.firstName", paginateParams });
895+
assertEquals(1, listResult.size());
896+
assertEquals("fred", ((GroovyObject)listResult.get(0)).getProperty("firstName"));
897+
paginateParams.put( "offset", new Integer(1) );
898+
listResult = (List)obj.getMetaClass().invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p order by p.firstName", paginateParams });
899+
assertEquals(1, listResult.size());
900+
assertEquals("wilma", ((GroovyObject)listResult.get(0)).getProperty("firstName"));
901+
882902
// test find with query and args
883903
List args = new ArrayList();
884904
args.add( "wilma" );
885-
returnValue = obj.getMetaClass().invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p where p.firstName = ?", args });
886-
assertNotNull(returnValue);
887-
assertEquals(ArrayList.class,returnValue.getClass());
888-
listResult = (List)returnValue;
905+
listResult = (List) domain.invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p where p.firstName = ?", args });
889906
assertEquals(1, listResult.size());
890907

891908
// test find with query and arg
892-
returnValue = obj.getMetaClass().invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p where p.firstName = ?", "wilma" });
893-
assertNotNull(returnValue);
894-
assertEquals(ArrayList.class,returnValue.getClass());
895-
listResult = (List)returnValue;
909+
listResult = (List)domain.invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p where p.firstName = ?", "wilma" });
896910
assertEquals(1, listResult.size());
897911

898912
// test find with query and named params
899913
Map namedArgs = new HashMap();
900914
namedArgs.put( "name", "wilma" );
901-
returnValue = obj.getMetaClass().invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p where p.firstName = :name", namedArgs });
902-
assertNotNull(returnValue);
903-
assertEquals(ArrayList.class,returnValue.getClass());
904-
listResult = (List)returnValue;
915+
listResult = (List)domain.invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p where p.firstName = :name", namedArgs });
905916
assertEquals(1, listResult.size());
906917

907918
// test find with query and named list params
@@ -910,17 +921,24 @@ public void testExecuteQueryMethod() {
910921
namesList.add("wilma");
911922
namesList.add("fred");
912923
namedArgs.put( "namesList", namesList );
913-
returnValue = obj.getMetaClass().invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p where p.firstName in (:namesList)", namedArgs });
914-
assertNotNull(returnValue);
915-
assertEquals(ArrayList.class,returnValue.getClass());
916-
listResult = (List)returnValue;
924+
listResult = (List)domain.invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p where p.firstName in (:namesList) order by p.firstName", namedArgs });
917925
assertEquals(2, listResult.size());
926+
// test find with a query and named list params and paginate params
927+
paginateParams.clear();
928+
paginateParams.put( "max", new Integer(1) );
929+
listResult = (List)domain.invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p where p.firstName in (:namesList) order by p.firstName", namedArgs, paginateParams });
930+
assertEquals(1, listResult.size());
931+
assertEquals("fred", ((GroovyObject)listResult.get(0)).getProperty("firstName"));
932+
paginateParams.put( "offset", new Integer(1) );
933+
listResult = (List)domain.invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p where p.firstName in (:namesList) order by p.firstName", namedArgs, paginateParams });
934+
assertEquals(1, listResult.size());
935+
assertEquals("wilma", ((GroovyObject)listResult.get(0)).getProperty("firstName"));
918936

919937
// test query with wrong named parameter
920938
try {
921939
namedArgs.clear();
922940
namedArgs.put(new Long(1), "wilma");
923-
obj.getMetaClass().invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p where p.firstName = :name", namedArgs});
941+
domain.invokeStaticMethod(obj, "executeQuery", new Object[] { "select distinct p from PersistentMethodTests as p where p.firstName = :name", namedArgs});
924942
// new Long(1) is not valid name for named param, so exception should be thrown
925943
fail("Should have thrown grails query exception");
926944
}

0 commit comments

Comments
 (0)