1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.impl;
26
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.io.Serializable;
34
35 import org.hibernate.FlushMode;
36 import org.hibernate.HibernateException;
37 import org.hibernate.LockMode;
38 import org.hibernate.Query;
39 import org.hibernate.QueryException;
40 import org.hibernate.SQLQuery;
41 import org.hibernate.ScrollMode;
42 import org.hibernate.ScrollableResults;
43 import org.hibernate.MappingException;
44 import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
45 import org.hibernate.engine.ResultSetMappingDefinition;
46 import org.hibernate.engine.NamedSQLQueryDefinition;
47 import org.hibernate.engine.QueryParameters;
48 import org.hibernate.engine.SessionImplementor;
49 import org.hibernate.engine.query.ParameterMetadata;
50 import org.hibernate.engine.query.sql.NativeSQLQueryJoinReturn;
51 import org.hibernate.engine.query.sql.NativeSQLQueryScalarReturn;
52 import org.hibernate.engine.query.sql.NativeSQLQueryRootReturn;
53 import org.hibernate.engine.query.sql.NativeSQLQueryReturn;
54 import org.hibernate.type.Type;
55 import org.hibernate.util.CollectionHelper;
56 import org.hibernate.util.StringHelper;
57
58 /**
59 * Implements SQL query passthrough.
60 *
61 * <pre>
62 * <sql-query name="mySqlQuery">
63 * <return alias="person" class="eg.Person"/>
64 * SELECT {person}.NAME AS {person.name}, {person}.AGE AS {person.age}, {person}.SEX AS {person.sex}
65 * FROM PERSON {person} WHERE {person}.NAME LIKE 'Hiber%'
66 * </sql-query>
67 * </pre>
68 *
69 * @author Max Andersen
70 */
71 public class SQLQueryImpl extends AbstractQueryImpl implements SQLQuery {
72
73 private final List queryReturns;
74 private Collection querySpaces;
75 private final boolean callable;
76 private boolean autodiscovertypes;
77
78 /**
79 * Constructs a SQLQueryImpl given a sql query defined in the mappings.
80 *
81 * @param queryDef The representation of the defined <sql-query/>.
82 * @param session The session to which this SQLQueryImpl belongs.
83 * @param parameterMetadata Metadata about parameters found in the query.
84 */
85 SQLQueryImpl(NamedSQLQueryDefinition queryDef, SessionImplementor session, ParameterMetadata parameterMetadata) {
86 super( queryDef.getQueryString(), queryDef.getFlushMode(), session, parameterMetadata );
87 if ( queryDef.getResultSetRef() != null ) {
88 ResultSetMappingDefinition definition = session.getFactory()
89 .getResultSetMapping( queryDef.getResultSetRef() );
90 if (definition == null) {
91 throw new MappingException(
92 "Unable to find resultset-ref definition: " +
93 queryDef.getResultSetRef()
94 );
95 }
96 this.queryReturns = Arrays.asList( definition.getQueryReturns() );
97 }
98 else {
99 this.queryReturns = Arrays.asList( queryDef.getQueryReturns() );
100 }
101
102 this.querySpaces = queryDef.getQuerySpaces();
103 this.callable = queryDef.isCallable();
104 }
105
106 SQLQueryImpl(
107 final String sql,
108 final List queryReturns,
109 final Collection querySpaces,
110 final FlushMode flushMode,
111 boolean callable,
112 final SessionImplementor session,
113 ParameterMetadata parameterMetadata) {
114 // TODO : absolutely no usages of this constructor form; can it go away?
115 super( sql, flushMode, session, parameterMetadata );
116 this.queryReturns = queryReturns;
117 this.querySpaces = querySpaces;
118 this.callable = callable;
119 }
120
121 SQLQueryImpl(
122 final String sql,
123 final String returnAliases[],
124 final Class returnClasses[],
125 final LockMode[] lockModes,
126 final SessionImplementor session,
127 final Collection querySpaces,
128 final FlushMode flushMode,
129 ParameterMetadata parameterMetadata) {
130 // TODO : this constructor form is *only* used from constructor directly below us; can it go away?
131 super( sql, flushMode, session, parameterMetadata );
132 queryReturns = new ArrayList(returnAliases.length);
133 for ( int i=0; i<returnAliases.length; i++ ) {
134 NativeSQLQueryRootReturn ret = new NativeSQLQueryRootReturn(
135 returnAliases[i],
136 returnClasses[i].getName(),
137 lockModes==null ? LockMode.NONE : lockModes[i]
138 );
139 queryReturns.add(ret);
140 }
141 this.querySpaces = querySpaces;
142 this.callable = false;
143 }
144
145 SQLQueryImpl(
146 final String sql,
147 final String returnAliases[],
148 final Class returnClasses[],
149 final SessionImplementor session,
150 ParameterMetadata parameterMetadata) {
151 this( sql, returnAliases, returnClasses, null, session, null, null, parameterMetadata );
152 }
153
154 SQLQueryImpl(String sql, SessionImplementor session, ParameterMetadata parameterMetadata) {
155 super( sql, null, session, parameterMetadata );
156 queryReturns = new ArrayList();
157 querySpaces = null;
158 callable = false;
159 }
160
161 private static final NativeSQLQueryReturn[] NO_SQL_RETURNS = new NativeSQLQueryReturn[0];
162
163 private NativeSQLQueryReturn[] getQueryReturns() {
164 return ( NativeSQLQueryReturn[] ) queryReturns.toArray( NO_SQL_RETURNS );
165 }
166
167 public List list() throws HibernateException {
168 verifyParameters();
169 before();
170
171 Map namedParams = getNamedParams();
172 NativeSQLQuerySpecification spec = generateQuerySpecification( namedParams );
173
174 try {
175 return getSession().list( spec, getQueryParameters( namedParams ) );
176 }
177 finally {
178 after();
179 }
180 }
181
182 private NativeSQLQuerySpecification generateQuerySpecification(Map namedParams) {
183 return new NativeSQLQuerySpecification(
184 expandParameterLists(namedParams),
185 getQueryReturns(),
186 querySpaces
187 );
188 }
189
190 public ScrollableResults scroll(ScrollMode scrollMode) throws HibernateException {
191 verifyParameters();
192 before();
193
194 Map namedParams = getNamedParams();
195 NativeSQLQuerySpecification spec = generateQuerySpecification( namedParams );
196
197 QueryParameters qp = getQueryParameters( namedParams );
198 qp.setScrollMode( scrollMode );
199
200 try {
201 return getSession().scroll( spec, qp );
202 }
203 finally {
204 after();
205 }
206 }
207
208 public ScrollableResults scroll() throws HibernateException {
209 return scroll(ScrollMode.SCROLL_INSENSITIVE);
210 }
211
212 public Iterator iterate() throws HibernateException {
213 throw new UnsupportedOperationException("SQL queries do not currently support iteration");
214 }
215
216 public QueryParameters getQueryParameters(Map namedParams) {
217 QueryParameters qp = super.getQueryParameters(namedParams);
218 qp.setCallable(callable);
219 qp.setAutoDiscoverScalarTypes(autodiscovertypes);
220 return qp;
221 }
222
223 protected void verifyParameters() {
224 verifyParameters( callable );
225 boolean noReturns = queryReturns==null || queryReturns.isEmpty();
226 if ( noReturns ) {
227 this.autodiscovertypes = noReturns;
228 }
229 else {
230 Iterator itr = queryReturns.iterator();
231 while ( itr.hasNext() ) {
232 NativeSQLQueryReturn rtn = ( NativeSQLQueryReturn ) itr.next();
233 if ( rtn instanceof NativeSQLQueryScalarReturn ) {
234 NativeSQLQueryScalarReturn scalar = ( NativeSQLQueryScalarReturn ) rtn;
235 if ( scalar.getType() == null ) {
236 autodiscovertypes = true;
237 break;
238 }
239 }
240 }
241 }
242 }
243
244 public String[] getReturnAliases() throws HibernateException {
245 throw new UnsupportedOperationException("SQL queries do not currently support returning aliases");
246 }
247
248 public Type[] getReturnTypes() throws HibernateException {
249 throw new UnsupportedOperationException("not yet implemented for SQL queries");
250 }
251
252 public Query setLockMode(String alias, LockMode lockMode) {
253 throw new UnsupportedOperationException("cannot set the lock mode for a native SQL query");
254 }
255
256 protected Map getLockModes() {
257 //we never need to apply locks to the SQL
258 return CollectionHelper.EMPTY_MAP;
259 }
260
261 public SQLQuery addScalar(String columnAlias, Type type) {
262 queryReturns.add( new NativeSQLQueryScalarReturn( columnAlias, type ) );
263 return this;
264 }
265
266 public SQLQuery addScalar(String columnAlias) {
267 autodiscovertypes = true;
268 queryReturns.add( new NativeSQLQueryScalarReturn( columnAlias, null ) );
269 return this;
270 }
271
272 public SQLQuery addJoin(String alias, String path) {
273 return addJoin(alias, path, LockMode.READ);
274 }
275
276 public SQLQuery addEntity(Class entityClass) {
277 return addEntity( StringHelper.unqualify( entityClass.getName() ), entityClass );
278 }
279
280 public SQLQuery addEntity(String entityName) {
281 return addEntity( StringHelper.unqualify( entityName ), entityName );
282 }
283
284 public SQLQuery addEntity(String alias, String entityName) {
285 return addEntity(alias, entityName, LockMode.READ);
286 }
287
288 public SQLQuery addEntity(String alias, Class entityClass) {
289 return addEntity( alias, entityClass.getName() );
290 }
291
292 public SQLQuery addJoin(String alias, String path, LockMode lockMode) {
293 int loc = path.indexOf('.');
294 if ( loc < 0 ) {
295 throw new QueryException( "not a property path: " + path );
296 }
297 String ownerAlias = path.substring(0, loc);
298 String role = path.substring(loc+1);
299 queryReturns.add( new NativeSQLQueryJoinReturn(alias, ownerAlias, role, CollectionHelper.EMPTY_MAP, lockMode) );
300 return this;
301 }
302
303 public SQLQuery addEntity(String alias, String entityName, LockMode lockMode) {
304 queryReturns.add( new NativeSQLQueryRootReturn(alias, entityName, lockMode) );
305 return this;
306 }
307
308 public SQLQuery addEntity(String alias, Class entityClass, LockMode lockMode) {
309 return addEntity( alias, entityClass.getName(), lockMode );
310 }
311
312 public SQLQuery setResultSetMapping(String name) {
313 ResultSetMappingDefinition mapping = session.getFactory().getResultSetMapping( name );
314 if ( mapping == null ) {
315 throw new MappingException( "Unknown SqlResultSetMapping [" + name + "]" );
316 }
317 NativeSQLQueryReturn[] returns = mapping.getQueryReturns();
318 int length = returns.length;
319 for ( int index = 0 ; index < length ; index++ ) {
320 queryReturns.add( returns[index] );
321 }
322 return this;
323 }
324
325 public SQLQuery addSynchronizedQuerySpace(String querySpace) {
326 if ( querySpaces == null ) {
327 querySpaces = new ArrayList();
328 }
329 querySpaces.add( querySpace );
330 return this;
331 }
332
333 public SQLQuery addSynchronizedEntityName(String entityName) {
334 return addQuerySpaces( getSession().getFactory().getEntityPersister( entityName ).getQuerySpaces() );
335 }
336
337 public SQLQuery addSynchronizedEntityClass(Class entityClass) {
338 return addQuerySpaces( getSession().getFactory().getEntityPersister( entityClass.getName() ).getQuerySpaces() );
339 }
340
341 private SQLQuery addQuerySpaces(Serializable[] spaces) {
342 if ( spaces != null ) {
343 if ( querySpaces == null ) {
344 querySpaces = new ArrayList();
345 }
346 for ( int i = 0; i < spaces.length; i++ ) {
347 querySpaces.add( spaces[i] );
348 }
349 }
350 return this;
351 }
352
353 public int executeUpdate() throws HibernateException {
354 Map namedParams = getNamedParams();
355 before();
356 try {
357 return getSession().executeNativeUpdate(
358 generateQuerySpecification( namedParams ),
359 getQueryParameters( namedParams )
360 );
361 }
362 finally {
363 after();
364 }
365 }
366
367 }