PageRenderTime 41ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/com/google/appengine/datanucleus/query/JPQLQuery.java

http://datanucleus-appengine.googlecode.com/
Java | 289 lines | 181 code | 32 blank | 76 comment | 51 complexity | df412cba6f7ac6935ff678dcb0084be7 MD5 | raw file
Possible License(s): Apache-2.0
  1. /**********************************************************************
  2. Copyright (c) 2009 Google Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. **********************************************************************/
  13. package com.google.appengine.datanucleus.query;
  14. import org.datanucleus.ClassLoaderResolver;
  15. import org.datanucleus.exceptions.NucleusException;
  16. import org.datanucleus.exceptions.NucleusFatalUserException;
  17. import org.datanucleus.metadata.AbstractClassMetaData;
  18. import com.google.appengine.datanucleus.DatastoreManager;
  19. import com.google.appengine.datanucleus.MetaDataUtils;
  20. import org.datanucleus.query.evaluator.JPQLEvaluator;
  21. import org.datanucleus.query.evaluator.JavaQueryEvaluator;
  22. import org.datanucleus.ExecutionContext;
  23. import org.datanucleus.store.Extent;
  24. import org.datanucleus.store.StoreManager;
  25. import org.datanucleus.store.connection.ManagedConnection;
  26. import org.datanucleus.store.connection.ManagedConnectionResourceListener;
  27. import org.datanucleus.store.query.AbstractJPQLQuery;
  28. import org.datanucleus.store.query.AbstractQueryResult;
  29. import org.datanucleus.store.query.CandidateIdsQueryResult;
  30. import org.datanucleus.store.query.Query;
  31. import org.datanucleus.store.query.QueryInvalidParametersException;
  32. import org.datanucleus.util.NucleusLogger;
  33. import java.util.ArrayList;
  34. import java.util.Iterator;
  35. import java.util.List;
  36. import java.util.Map;
  37. /**
  38. * Implementation of JPQL for the app engine datastore.
  39. *
  40. * @author Erick Armbrust <earmbrust@google.com>
  41. */
  42. public class JPQLQuery extends AbstractJPQLQuery {
  43. /**
  44. * The underlying Datastore query implementation.
  45. */
  46. private final DatastoreQuery datastoreQuery;
  47. /**
  48. * Constructs a new query instance that uses the given StoreManager and ExecutionContext.
  49. * @param storeMgr StoreManager
  50. * @param ec ExecutionContext
  51. */
  52. public JPQLQuery(StoreManager storeMgr, ExecutionContext ec) {
  53. this(storeMgr, ec, (JPQLQuery) null);
  54. }
  55. /**
  56. * Constructs a new query instance having the same criteria as the given query.
  57. * @param storeMgr StoreManager
  58. * @param ec ExecutionContext
  59. * @param q The query from which to copy criteria.
  60. */
  61. public JPQLQuery(StoreManager storeMgr, ExecutionContext ec, JPQLQuery q) {
  62. super(storeMgr, ec, q);
  63. datastoreQuery = new DatastoreQuery(this);
  64. }
  65. /**
  66. * Constructor for a JPQL query where the query is specified using the "Single-String" format.
  67. * @param storeMgr StoreManager
  68. * @param ec ExecutionContext
  69. * @param query The JPQL query string.
  70. */
  71. public JPQLQuery(StoreManager storeMgr, ExecutionContext ec, String query) {
  72. super(storeMgr, ec, query);
  73. datastoreQuery = new DatastoreQuery(this);
  74. }
  75. /**
  76. * Convenience method to return whether the query should be evaluated in-memory.
  77. * @return Use in-memory evaluation?
  78. */
  79. protected boolean evaluateInMemory() {
  80. if (candidateCollection != null) {
  81. if (compilation != null && compilation.getSubqueryAliases() != null) {
  82. // TODO In-memory evaluation of subqueries isn't fully implemented yet, so remove this when it is
  83. NucleusLogger.QUERY.warn("In-memory evaluator doesn't currently handle subqueries completely so evaluating in datastore");
  84. return false;
  85. }
  86. // Return true unless the user has explicitly said no
  87. Object val = getExtension(EXTENSION_EVALUATE_IN_MEMORY);
  88. if (val == null) {
  89. return true;
  90. }
  91. return Boolean.valueOf((String)val);
  92. }
  93. return super.evaluateInMemory();
  94. }
  95. /**
  96. * {@inheritDoc}
  97. */
  98. @Override
  99. protected Object performExecute(Map parameters) {
  100. if (type == org.datanucleus.store.query.Query.BULK_UPDATE) {
  101. throw new NucleusFatalUserException("Bulk Update statements are not supported.");
  102. }
  103. long startTime = System.currentTimeMillis();
  104. if (NucleusLogger.QUERY.isDebugEnabled()) {
  105. NucleusLogger.QUERY.debug(LOCALISER.msg("021046", "JPQL", getSingleStringQuery(), null));
  106. }
  107. if (type == Query.BULK_UPDATE) {
  108. // TODO Support this
  109. throw new NucleusException("JPQL Bulk UPDATE is not yet supported");
  110. }
  111. if (candidateCollection == null &&
  112. type == Query.SELECT && resultClass == null && result == null) {
  113. // Check for cached query results
  114. List<Object> cachedResults = getQueryManager().getDatastoreQueryResult(this, parameters);
  115. if (cachedResults != null) {
  116. // Query results are cached, so return those
  117. return new CandidateIdsQueryResult(this, cachedResults);
  118. }
  119. }
  120. Object results = null;
  121. if (evaluateInMemory()) {
  122. // Evaluating in-memory so build up list of candidates
  123. List candidates = null;
  124. if (candidateCollection != null) {
  125. candidates = new ArrayList(candidateCollection);
  126. } else {
  127. Extent ext = getStoreManager().getExtent(ec, candidateClass, subclasses);
  128. candidates = new ArrayList();
  129. Iterator iter = ext.iterator();
  130. while (iter.hasNext()) {
  131. candidates.add(iter.next());
  132. }
  133. }
  134. // Evaluate in-memory over the candidate instances
  135. JavaQueryEvaluator resultMapper = new JPQLEvaluator(this, candidates, compilation,
  136. parameters, ec.getClassLoaderResolver());
  137. results = resultMapper.execute(true, true, true, true, true);
  138. }
  139. else {
  140. // Evaluate in-datastore
  141. boolean inmemoryWhenUnsupported = getEvaluateInMemoryWhenUnsupported();
  142. QueryData qd = datastoreQuery.compile(compilation, parameters, inmemoryWhenUnsupported);
  143. if (NucleusLogger.QUERY.isDebugEnabled()) {
  144. // Log the query
  145. NucleusLogger.QUERY.debug("Query compiled as : " + qd.getDatastoreQueryAsString());
  146. }
  147. results = datastoreQuery.performExecute(qd);
  148. boolean filterInMemory = false;
  149. boolean orderInMemory = false;
  150. boolean resultInMemory = (result != null || grouping != null || having != null || resultClass != null);
  151. if (inmemoryWhenUnsupported) {
  152. // Set filter/order flags according to what the query can manage in-datastore
  153. filterInMemory = !datastoreQuery.isFilterComplete();
  154. if (ordering != null) {
  155. if (filterInMemory) {
  156. orderInMemory = true;
  157. } else {
  158. if (!datastoreQuery.isOrderComplete()) {
  159. orderInMemory = true;
  160. }
  161. }
  162. }
  163. }
  164. // Evaluate any remaining parts in-memory
  165. if (filterInMemory || resultInMemory || orderInMemory) {
  166. JavaQueryEvaluator resultMapper = new JPQLEvaluator(this, (List)results, compilation,
  167. parameters, ec.getClassLoaderResolver());
  168. results = resultMapper.execute(filterInMemory, orderInMemory,
  169. resultInMemory, resultClass != null, false);
  170. }
  171. if (results instanceof AbstractQueryResult) {
  172. // Lazy loading results : add listener to the connection so we can get a callback when the connection is flushed.
  173. final AbstractQueryResult qr = (AbstractQueryResult)results;
  174. final ManagedConnection mconn = getStoreManager().getConnection(ec);
  175. ManagedConnectionResourceListener listener = new ManagedConnectionResourceListener() {
  176. public void managedConnectionPreClose() {
  177. // Disconnect the query from this ManagedConnection (read in unread rows etc)
  178. qr.disconnect();
  179. }
  180. public void managedConnectionPostClose() {}
  181. public void resourcePostClose() {
  182. mconn.removeListener(this);
  183. }
  184. public void transactionFlushed() {}
  185. public void transactionPreClose() {
  186. // Disconnect the query from this ManagedConnection (read in unread rows etc)
  187. qr.disconnect();
  188. }
  189. };
  190. mconn.addListener(listener);
  191. qr.addConnectionListener(listener);
  192. }
  193. }
  194. if (NucleusLogger.QUERY.isDebugEnabled()) {
  195. NucleusLogger.QUERY.debug(LOCALISER.msg("021074", "JPQL",
  196. "" + (System.currentTimeMillis() - startTime)));
  197. }
  198. return results;
  199. }
  200. boolean getEvaluateInMemoryWhenUnsupported() {
  201. // Use StoreManager setting and allow override in query extensions
  202. boolean inmemory = storeMgr.getBooleanProperty("datanucleus.appengine.query.inMemoryWhenUnsupported");
  203. return getBooleanExtensionProperty(DatastoreManager.QUERYEXT_INMEMORY_WHEN_UNSUPPORTED, inmemory);
  204. }
  205. // Exposed for tests.
  206. DatastoreQuery getDatastoreQuery() {
  207. return datastoreQuery;
  208. }
  209. @Override
  210. protected boolean supportsTimeout() {
  211. return true; // GAE/J Datastore supports timeouts
  212. }
  213. @Override
  214. public void setUnique(boolean unique) {
  215. // Workaround a DataNucleus bug.
  216. // The superclass implementation discards the comiled query when this is set,
  217. // but since jpql param values are set _before_ the query is executed,
  218. // discarding the compiled query discards the parameter values as well and
  219. // we have no way of getting them back.
  220. this.unique = unique;
  221. }
  222. @Override
  223. protected void checkParameterTypesAgainstCompilation(Map parameterValues) {
  224. // Disabled as part of our DataNuc 1.1.3 upgrade so that we can be
  225. // continue to allow multi-value properties and implicit conversions.
  226. // TODO(maxr) Re-enable the checks that don't break multi-value filters
  227. // and implicit conversions.
  228. }
  229. @Override
  230. protected void applyImplicitParameterValueToCompilation(String name, Object value) {
  231. try {
  232. super.applyImplicitParameterValueToCompilation(name, value);
  233. } catch (QueryInvalidParametersException e) {
  234. // swallow this exception - need to disable the type checking so we can
  235. // be friendly about implicit conversions
  236. }
  237. }
  238. @Override
  239. public void setSubclasses(boolean subclasses) {
  240. // TODO Enable this!
  241. // We support only queries that also return subclasses if all subclasses belong to the same kind.
  242. if (subclasses) {
  243. DatastoreManager storeMgr = (DatastoreManager) ec.getStoreManager();
  244. ClassLoaderResolver clr = ec.getClassLoaderResolver();
  245. AbstractClassMetaData acmd = storeMgr.getMetaDataManager().getMetaDataForClass(getCandidateClass(), clr);
  246. if (!MetaDataUtils.isNewOrSuperclassTableInheritanceStrategy(acmd)) {
  247. throw new NucleusFatalUserException(
  248. "The App Engine datastore only supports queries that return subclass entities with the " +
  249. "SINGLE_TABLE interitance mapping strategy.");
  250. }
  251. }
  252. super.setSubclasses(subclasses);
  253. }
  254. }