View Javadoc

1   //$Id: SQLQueryImpl.java 10861 2006-11-22 00:11:25Z steve.ebersole@jboss.com $
2   package org.hibernate.impl;
3   
4   import java.util.ArrayList;
5   import java.util.Arrays;
6   import java.util.Collection;
7   import java.util.Iterator;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  import java.io.Serializable;
12  import java.math.BigInteger;
13  
14  import org.hibernate.FlushMode;
15  import org.hibernate.HibernateException;
16  import org.hibernate.LockMode;
17  import org.hibernate.Query;
18  import org.hibernate.QueryException;
19  import org.hibernate.SQLQuery;
20  import org.hibernate.ScrollMode;
21  import org.hibernate.ScrollableResults;
22  import org.hibernate.MappingException;
23  import org.hibernate.Session;
24  import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
25  import org.hibernate.engine.ResultSetMappingDefinition;
26  import org.hibernate.engine.NamedSQLQueryDefinition;
27  import org.hibernate.engine.QueryParameters;
28  import org.hibernate.engine.SessionImplementor;
29  import org.hibernate.engine.TypedValue;
30  import org.hibernate.engine.query.ParameterMetadata;
31  import org.hibernate.engine.query.sql.NativeSQLQueryJoinReturn;
32  import org.hibernate.engine.query.sql.NativeSQLQueryScalarReturn;
33  import org.hibernate.engine.query.sql.NativeSQLQueryRootReturn;
34  import org.hibernate.engine.query.sql.NativeSQLQueryReturn;
35  import org.hibernate.type.Type;
36  import org.hibernate.util.CollectionHelper;
37  import org.hibernate.util.StringHelper;
38  
39  /**
40   * Implements SQL query passthrough.
41   *
42   * <pre>
43   * <sql-query name="mySqlQuery">
44   * <return alias="person" class="eg.Person"/>
45   *   SELECT {person}.NAME AS {person.name}, {person}.AGE AS {person.age}, {person}.SEX AS {person.sex}
46   *   FROM PERSON {person} WHERE {person}.NAME LIKE 'Hiber%'
47   * </sql-query>
48   * </pre>
49   *
50   * @author gperrone
51   */
52  public class SQLQueryImpl extends AbstractQueryImpl implements SQLQuery {
53  
54  	private final List queryReturns;
55  	private Collection querySpaces;
56  	private final boolean callable;
57  	private boolean autodiscovertypes;
58  
59  	/**
60  	 * Constructs a SQLQueryImpl given a sql query defined in the mappings.
61  	 *
62  	 * @param queryDef The representation of the defined <sql-query/>.
63  	 * @param session The session to which this SQLQueryImpl belongs.
64  	 * @param parameterMetadata Metadata about parameters found in the query.
65  	 */
66  	SQLQueryImpl(NamedSQLQueryDefinition queryDef, SessionImplementor session, ParameterMetadata parameterMetadata) {
67  		super( queryDef.getQueryString(), queryDef.getFlushMode(), session, parameterMetadata );
68  		if ( queryDef.getResultSetRef() != null ) {
69  			ResultSetMappingDefinition definition = session.getFactory()
70  					.getResultSetMapping( queryDef.getResultSetRef() );
71  			if (definition == null) {
72  				throw new MappingException(
73  						"Unable to find resultset-ref definition: " +
74  						queryDef.getResultSetRef() 
75  					);
76  			}
77  			this.queryReturns = Arrays.asList( definition.getQueryReturns() );
78  		}
79  		else {
80  			this.queryReturns = Arrays.asList( queryDef.getQueryReturns() );
81  		}
82  
83  		this.querySpaces = queryDef.getQuerySpaces();
84  		this.callable = queryDef.isCallable();
85  	}
86  
87  	SQLQueryImpl(
88  			final String sql,
89  	        final List queryReturns,
90  	        final Collection querySpaces,
91  	        final FlushMode flushMode,
92  	        boolean callable,
93  	        final SessionImplementor session,
94  	        ParameterMetadata parameterMetadata) {
95  		// TODO : absolutely no usages of this constructor form; can it go away?
96  		super( sql, flushMode, session, parameterMetadata );
97  		this.queryReturns = queryReturns;
98  		this.querySpaces = querySpaces;
99  		this.callable = callable;
100 	}
101 
102 	SQLQueryImpl(
103 			final String sql,
104 	        final String returnAliases[],
105 	        final Class returnClasses[],
106 	        final LockMode[] lockModes,
107 	        final SessionImplementor session,
108 	        final Collection querySpaces,
109 	        final FlushMode flushMode,
110 	        ParameterMetadata parameterMetadata) {
111 		// TODO : this constructor form is *only* used from constructor directly below us; can it go away?
112 		super( sql, flushMode, session, parameterMetadata );
113 		queryReturns = new ArrayList(returnAliases.length);
114 		for ( int i=0; i<returnAliases.length; i++ ) {
115 			NativeSQLQueryRootReturn ret = new NativeSQLQueryRootReturn(
116 					returnAliases[i],
117 					returnClasses[i].getName(),
118 					lockModes==null ? LockMode.NONE : lockModes[i]
119 			);
120 			queryReturns.add(ret);
121 		}
122 		this.querySpaces = querySpaces;
123 		this.callable = false;
124 	}
125 
126 	SQLQueryImpl(
127 			final String sql,
128 	        final String returnAliases[],
129 	        final Class returnClasses[],
130 	        final SessionImplementor session,
131 	        ParameterMetadata parameterMetadata) {
132 		this( sql, returnAliases, returnClasses, null, session, null, null, parameterMetadata );
133 	}
134 	
135 	SQLQueryImpl(String sql, SessionImplementor session, ParameterMetadata parameterMetadata) {
136 		super( sql, null, session, parameterMetadata );
137 		queryReturns = new ArrayList();
138 		querySpaces = null;
139 		callable = false;
140 	}
141 	
142 	private static final NativeSQLQueryReturn[] NO_SQL_RETURNS = new NativeSQLQueryReturn[0];
143 	
144 	private NativeSQLQueryReturn[] getQueryReturns() {
145 		return ( NativeSQLQueryReturn[] ) queryReturns.toArray( NO_SQL_RETURNS );
146 	}
147 
148 	public List list() throws HibernateException {
149 		verifyParameters();
150 		before();
151 
152 		Map namedParams = getNamedParams();
153 		NativeSQLQuerySpecification spec = generateQuerySpecification( namedParams );
154 
155 		try {
156 			return getSession().list( spec, getQueryParameters( namedParams ) );
157 		}
158 		finally {
159 			after();
160 		}
161 	}
162 
163 	private NativeSQLQuerySpecification generateQuerySpecification(Map namedParams) {
164 		return new NativeSQLQuerySpecification(
165 		        expandParameterLists(namedParams),
166 		        getQueryReturns(),
167 		        querySpaces
168 		);
169 	}
170 
171 	public ScrollableResults scroll(ScrollMode scrollMode) throws HibernateException {
172 		verifyParameters();
173 		before();
174 
175 		Map namedParams = getNamedParams();
176 		NativeSQLQuerySpecification spec = generateQuerySpecification( namedParams );
177 
178 		QueryParameters qp = getQueryParameters( namedParams );
179 		qp.setScrollMode( scrollMode );
180 
181 		try {
182 			return getSession().scroll( spec, qp );
183 		}
184 		finally {
185 			after();
186 		}
187 	}
188 
189 	public ScrollableResults scroll() throws HibernateException {
190 		return scroll(ScrollMode.SCROLL_INSENSITIVE);
191 	}
192 
193 	public Iterator iterate() throws HibernateException {
194 		throw new UnsupportedOperationException("SQL queries do not currently support iteration");
195 	}
196 
197 	public QueryParameters getQueryParameters(Map namedParams) {
198 		QueryParameters qp = super.getQueryParameters(namedParams);
199 		qp.setCallable(callable);
200 		qp.setAutoDiscoverScalarTypes(autodiscovertypes);
201 		return qp;
202 	}
203 
204 	protected void verifyParameters() {
205 		verifyParameters( callable );
206 		boolean noReturns = queryReturns==null || queryReturns.isEmpty();
207 		if ( noReturns ) {
208 			this.autodiscovertypes = noReturns;
209 		}
210 		else {
211 			Iterator itr = queryReturns.iterator();
212 			while ( itr.hasNext() ) {
213 				NativeSQLQueryReturn rtn = ( NativeSQLQueryReturn ) itr.next();
214 				if ( rtn instanceof NativeSQLQueryScalarReturn ) {
215 					NativeSQLQueryScalarReturn scalar = ( NativeSQLQueryScalarReturn ) rtn;
216 					if ( scalar.getType() == null ) {
217 						autodiscovertypes = true;
218 						break;
219 					}
220 				}
221 			}
222 		}
223 	}
224 
225 	public String[] getReturnAliases() throws HibernateException {
226 		throw new UnsupportedOperationException("SQL queries do not currently support returning aliases");
227 	}
228 
229 	public Type[] getReturnTypes() throws HibernateException {
230 		throw new UnsupportedOperationException("not yet implemented for SQL queries");
231 	}
232 	
233 	public Query setLockMode(String alias, LockMode lockMode) {
234 		throw new UnsupportedOperationException("cannot set the lock mode for a native SQL query");
235 	}
236 	
237 	protected Map getLockModes() {
238 		//we never need to apply locks to the SQL
239 		return CollectionHelper.EMPTY_MAP;
240 	}
241 
242 	public SQLQuery addScalar(String columnAlias, Type type) {
243 		queryReturns.add( new NativeSQLQueryScalarReturn( columnAlias, type ) );
244 		return this;
245 	}
246 
247 	public SQLQuery addScalar(String columnAlias) {
248 		autodiscovertypes = true;
249 		queryReturns.add( new NativeSQLQueryScalarReturn( columnAlias, null ) );
250 		return this;
251 	}
252 
253 	public SQLQuery addJoin(String alias, String path) {
254 		return addJoin(alias, path, LockMode.READ);
255 	}
256 
257 	public SQLQuery addEntity(Class entityClass) {
258 		return addEntity( StringHelper.unqualify( entityClass.getName() ), entityClass );
259 	}
260 
261 	public SQLQuery addEntity(String entityName) {
262 		return addEntity( StringHelper.unqualify( entityName ), entityName );
263 	}
264 
265 	public SQLQuery addEntity(String alias, String entityName) {
266 		return addEntity(alias, entityName, LockMode.READ);
267 	}
268 
269 	public SQLQuery addEntity(String alias, Class entityClass) {
270 		return addEntity( alias, entityClass.getName() );
271 	}
272 	
273 	public SQLQuery addJoin(String alias, String path, LockMode lockMode) {
274 		int loc = path.indexOf('.');
275 		if ( loc < 0 ) {
276 			throw new QueryException( "not a property path: " + path );
277 		}
278 		String ownerAlias = path.substring(0, loc);
279 		String role = path.substring(loc+1);
280 		queryReturns.add( new NativeSQLQueryJoinReturn(alias, ownerAlias, role, CollectionHelper.EMPTY_MAP, lockMode) );
281 		return this;
282 	}
283 
284 	public SQLQuery addEntity(String alias, String entityName, LockMode lockMode) {
285 		queryReturns.add( new NativeSQLQueryRootReturn(alias, entityName, lockMode) );
286 		return this;
287 	}
288 
289 	public SQLQuery addEntity(String alias, Class entityClass, LockMode lockMode) {
290 		return addEntity( alias, entityClass.getName(), lockMode );
291 	}
292 
293 	public SQLQuery setResultSetMapping(String name) {
294 		ResultSetMappingDefinition mapping = session.getFactory().getResultSetMapping( name );
295 		if ( mapping == null ) {
296 			throw new MappingException( "Unknown SqlResultSetMapping [" + name + "]" );
297 		}
298 		NativeSQLQueryReturn[] returns = mapping.getQueryReturns();
299 		int length = returns.length;
300 		for ( int index = 0 ; index < length ; index++ ) {
301 			queryReturns.add( returns[index] );
302 		}
303 		return this;
304 	}
305 
306 	public SQLQuery addSynchronizedQuerySpace(String querySpace) {
307 		if ( querySpaces == null ) {
308 			querySpaces = new ArrayList();
309 		}
310 		querySpaces.add( querySpace );
311 		return this;
312 	}
313 
314 	public SQLQuery addSynchronizedEntityName(String entityName) {
315 		return addQuerySpaces( getSession().getFactory().getEntityPersister( entityName ).getQuerySpaces() );
316 	}
317 
318 	public SQLQuery addSynchronizedEntityClass(Class entityClass) {
319 		return addQuerySpaces( getSession().getFactory().getEntityPersister( entityClass.getName() ).getQuerySpaces() );
320 	}
321 
322 	private SQLQuery addQuerySpaces(Serializable[] spaces) {
323 		if ( spaces != null ) {
324 			if ( querySpaces == null ) {
325 				querySpaces = new ArrayList();
326 			}
327 			for ( int i = 0; i < spaces.length; i++ ) {
328 				querySpaces.add( spaces[i] );
329 			}
330 		}
331 		return this;
332 	}
333 
334 	public int executeUpdate() throws HibernateException {
335 		Map namedParams = getNamedParams();
336 		before();
337 		try {
338 			return getSession().executeNativeUpdate(
339 					generateQuerySpecification( namedParams ),
340 					getQueryParameters( namedParams )
341 			);
342 		}
343 		finally {
344 			after();
345 		}
346 	}
347 
348 	/**
349 	 * @see org.hibernate.Query#count()
350 	 */
351 	public int count() throws HibernateException {
352 		StringBuffer sqlQuery = new StringBuffer(this.getQueryString().toUpperCase());
353 		StringBuffer sqlQueryBuffer = new StringBuffer();
354 		sqlQueryBuffer.append("SELECT COUNT(*) ");
355 		int start = sqlQuery.indexOf("FROM");
356 		int stop = sqlQuery.indexOf("ORDER BY");
357 		if (stop >= 0) {
358 			sqlQueryBuffer.append(this.getQueryString().substring(start, stop));
359 		} else {
360 			sqlQueryBuffer.append(this.getQueryString().substring(start));
361 		}
362 		Query countQuery = ((Session)this.session).createSQLQuery(sqlQueryBuffer.toString());
363 		
364 		//SET NAMED PARAMS	
365 		Map parameters = this.getNamedParams();
366 		Set parameterNames = parameters.keySet();
367 		Iterator iter = parameterNames.iterator();
368 		while (iter.hasNext()) {
369 			String name = (String)iter.next();
370 			TypedValue typedValue = (TypedValue)parameters.get(name);
371 			countQuery.setParameter(name, typedValue.getValue(), typedValue.getType());
372 		}
373 		
374 		//SET POSITIONAL PARAMS
375 		List paramTypes = this.getTypes();
376 		List paramValues = this.getValues();
377 		if (paramTypes != null && paramTypes.size() > 0) {
378 			for (int i = 0; i < paramTypes.size(); i++) {
379 				countQuery.setParameter(i, paramValues.get(i), (Type)paramTypes.get(i));
380 			}
381 		}
382 		int count = ((BigInteger)countQuery.uniqueResult()).intValue();
383 		return count;
384 	}
385 
386 }