001: /*
002: * Copyright 2006 Luca Garulli (luca.garulli@assetdata.it)
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.romaframework.aspect.persistence.jdo;
018:
019: import java.lang.reflect.Field;
020: import java.lang.reflect.Modifier;
021: import java.util.Collection;
022: import java.util.HashMap;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026: import java.util.StringTokenizer;
027:
028: import javax.jdo.PersistenceManager;
029: import javax.jdo.PersistenceManagerFactory;
030: import javax.jdo.Query;
031:
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.jpox.store.rdbms.adapter.SQLConstants;
035: import org.romaframework.aspect.persistence.PersistenceAspect;
036: import org.romaframework.aspect.persistence.PersistenceException;
037: import org.romaframework.aspect.persistence.QueryByExample;
038: import org.romaframework.aspect.persistence.QueryByFilter;
039: import org.romaframework.aspect.persistence.QueryByFilterItem;
040: import org.romaframework.aspect.persistence.QueryByFilterOrder;
041: import org.romaframework.aspect.persistence.QueryByText;
042: import org.romaframework.core.flow.ObjectContext;
043: import org.romaframework.core.schema.SchemaClassDefinition;
044: import org.romaframework.core.schema.SchemaField;
045: import org.romaframework.core.schema.SchemaHelper;
046: import org.romaframework.core.schema.SchemaManager;
047:
048: public class JDOPersistenceHelper {
049:
050: private static final int MODE_FIELD_THEN_PAR = 0;
051:
052: private static final int MODE_PAR_THEN_FIELD = 1;
053:
054: public static Query getQuery(PersistenceManager iManager,
055: org.romaframework.aspect.persistence.Query iQuerySource,
056: Class iCandidateClass, int iRangeFrom, int iRangeTo,
057: boolean iSubClasses, String iMode) {
058: Query query = null;
059:
060: try {
061: if (iQuerySource.getStrategy() != PersistenceAspect.STRATEGY_STANDARD
062: && iMode != null)
063: iManager.getFetchPlan().addGroup(iMode);
064:
065: query = iManager.newQuery();
066: if (iCandidateClass != null) {
067: if (iSubClasses)
068: query.setClass(iCandidateClass);
069: else
070: query.setCandidates(iManager.getExtent(
071: iCandidateClass, false));
072: }
073: } catch (Throwable e) {
074: log.error("[JDOPersistenceAspect.getQuery]", e);
075: throw new PersistenceException("getQuery", e);
076: }
077:
078: return query;
079: }
080:
081: public static List prepareQuery(PersistenceManager iManager,
082: org.romaframework.aspect.persistence.Query iQuerySource,
083: Query iQueryToExecute, Query iCountQuery,
084: HashMap<String, Object> parValues) {
085: try {
086: // FIRST PAGE: GET RESULT NUMBER
087: if (iQuerySource.getRangeFrom() == 0) {
088: // BEFORE TO EXECUTE COUNT TOTAL ITEMS
089: iCountQuery.setUnique(true);
090: iCountQuery.setResult("count(this)");
091:
092: long totalItems = -1;
093:
094: if (parValues != null)
095: totalItems = (Long) iCountQuery
096: .executeWithMap(parValues);
097: else
098: totalItems = (Long) iCountQuery.execute();
099:
100: iQuerySource.setTotalItems((int) totalItems);
101:
102: iCountQuery.closeAll();
103: }
104:
105: if (iQuerySource.getRangeFrom() > -1
106: && iQuerySource.getRangeTo() > -1)
107: iQueryToExecute.setRange(iQuerySource.getRangeFrom(),
108: iQuerySource.getRangeTo());
109:
110: // EXECUTE QUERY CONSIDERING PAGING
111: List result;
112: if (parValues != null)
113: result = (List) iQueryToExecute
114: .executeWithMap(parValues);
115: else
116: result = (List) iQueryToExecute.execute();
117:
118: return result;
119: } catch (Throwable e) {
120: log.error("[JDOPersistenceAspect.prepareQuery]", e);
121: throw new PersistenceException("prepareQuery", e);
122: }
123: }
124:
125: public static List queryByExample(PersistenceManager manager,
126: QueryByExample iQuery) throws PersistenceException {
127: if (log.isDebugEnabled())
128: log.debug("[JDOPersistenceAspect.queryByExample] Class: "
129: + iQuery.getCandidateClass() + " filter object: "
130: + iQuery);
131:
132: // TODO Use SchemaClass to use method/field getters and setters
133:
134: // EXTRACT QUERY BASED ON FILER OBJECT
135: QueryByFilter filter = new QueryByFilter(iQuery
136: .getCandidateClass());
137: filter.setRangeFrom(iQuery.getRangeFrom(), iQuery.getRangeTo());
138: filter.setSubClasses(iQuery.isSubClasses());
139: filter.setMode(iQuery.getMode());
140: filter.setStrategy(iQuery.getStrategy());
141:
142: if (iQuery.getFilter() != null) {
143: Field[] fields = SchemaHelper.getFields(iQuery
144: .getCandidateClass());
145: Object fieldValue;
146: String operator;
147: for (int i = 0; i < fields.length; ++i) {
148: try {
149: if (Modifier.isStatic(fields[i].getModifiers())
150: || Modifier.isTransient(fields[i]
151: .getModifiers()))
152: // JUMP STATIC AND TRANSIENT FIELDS
153: continue;
154:
155: if (fields[i].getName().startsWith("jdo"))
156: // IGNORE ALL JDO FIELDS
157: continue;
158:
159: if (!fields[i].isAccessible())
160: fields[i].setAccessible(true);
161:
162: fieldValue = fields[i].get(iQuery.getFilter());
163: if (fieldValue != null) {
164: if (fieldValue instanceof Collection
165: || fieldValue instanceof Map)
166: continue;
167: if (fieldValue instanceof String
168: && ((String) fieldValue).length() == 0)
169: // EMPTY STRING, IGNORE FOR FILTERING
170: continue;
171:
172: if (fields[i].getType().equals(String.class))
173: operator = QueryByFilter.FIELD_LIKE;
174: else
175: operator = QueryByFilter.FIELD_EQUALS;
176:
177: // INSERT INTO QUERY PREDICATE
178: filter.addItem(fields[i].getName(), operator,
179: fieldValue);
180: }
181: } catch (Exception e) {
182: log.error("[JDOPersistenceAspect.queryByExample]",
183: e);
184: }
185: }
186: }
187:
188: QueryByFilter addFilter = iQuery.getAdditionalFilter();
189: if (addFilter != null) {
190: filter.setSubClasses(addFilter.isSubClasses());
191:
192: // COPY ALL ITEMS TO THE MAIN FILTER
193: for (Iterator<QueryByFilterItem> it = addFilter
194: .getItemsIterator(); it.hasNext();) {
195: filter.addItem(it.next());
196: }
197:
198: // COPY ALL ORDER CLAUSES TO THE MAIN FILTER
199: for (Iterator<QueryByFilterOrder> it = addFilter
200: .getOrdersIterator(); it.hasNext();) {
201: filter.addOrder(it.next());
202: }
203: }
204:
205: List result = queryByFilter(manager, filter);
206: iQuery.setTotalItems(filter.getTotalItems());
207: return result;
208: }
209:
210: public static List queryByFilter(PersistenceManager manager,
211: QueryByFilter iQueryFilter) throws PersistenceException {
212: if (iQueryFilter == null)
213: return null;
214:
215: if (log.isDebugEnabled())
216: log.debug("[JDOPersistenceAspect.queryByExample] Class: "
217: + iQueryFilter.getCandidateClass()
218: + " filter map: " + iQueryFilter);
219:
220: List result = null;
221:
222: SchemaClassDefinition def = ObjectContext.getInstance()
223: .getComponent(SchemaManager.class).getClassInfo(
224: iQueryFilter.getCandidateClass()
225: .getSimpleName());
226:
227: // EXTRACT QUERY BASED ON FILER OBJECT
228: StringBuffer queryText = new StringBuffer();
229: StringBuffer queryParameters = new StringBuffer();
230: StringBuffer queryVariables = new StringBuffer();
231: StringBuffer queryOrders = new StringBuffer();
232:
233: HashMap<String, Object> parValues = new HashMap<String, Object>();
234:
235: formatParameters(iQueryFilter, def, queryText, queryParameters,
236: queryVariables, parValues, null);
237:
238: Query query = getQuery(manager, iQueryFilter, iQueryFilter
239: .getCandidateClass(), iQueryFilter.getRangeFrom(),
240: iQueryFilter.getRangeTo(), iQueryFilter.isSubClasses(),
241: iQueryFilter.getMode());
242: Query countQuery = getQuery(manager, iQueryFilter, iQueryFilter
243: .getCandidateClass(), iQueryFilter.getRangeFrom(),
244: iQueryFilter.getRangeTo(), iQueryFilter.isSubClasses(),
245: iQueryFilter.getMode());
246:
247: setOrdering(iQueryFilter, query, queryOrders);
248:
249: if (queryParameters.length() > 0) {
250: query.declareParameters(queryParameters.toString());
251: countQuery.declareParameters(queryParameters.toString());
252: }
253:
254: if (queryVariables.length() > 0) {
255: query.declareVariables(queryVariables.toString());
256: countQuery.declareVariables(queryVariables.toString());
257: }
258:
259: query.setFilter(queryText.toString());
260: countQuery.setFilter(queryText.toString());
261:
262: List tempResult = prepareQuery(manager, iQueryFilter, query,
263: countQuery, parValues);
264:
265: if (iQueryFilter.getTotalItems() == -1)
266: iQueryFilter.setTotalItems(tempResult != null ? tempResult
267: .size() : 0);
268:
269: result = retrieveObjects(manager, iQueryFilter, tempResult);
270:
271: closeQuery(tempResult, query, iQueryFilter.getStrategy());
272:
273: return result;
274: }
275:
276: private static void closeQuery(List result, Query query,
277: byte iStrategy) {
278: if (iStrategy == PersistenceAspect.STRATEGY_DETACHING
279: || iStrategy == PersistenceAspect.STRATEGY_TRANSIENT)
280: // CLOSE IMMEDIATELY THE QUERY TO SAVE RESOURCES
281: query.close(result);
282: }
283:
284: private static void formatParameters(QueryByFilter iQueryFilter,
285: SchemaClassDefinition def, StringBuffer queryText,
286: StringBuffer queryParameters, StringBuffer queryVariables,
287: HashMap<String, Object> parValues, String iObjectName) {
288: Object itemValue;
289: String parName;
290: String tempParName;
291: String typeName;
292: QueryByFilterItem item;
293: String fieldName;
294: SchemaField field;
295:
296: for (Iterator<QueryByFilterItem> it = iQueryFilter
297: .getItemsIterator(); it.hasNext();) {
298: item = it.next();
299:
300: parName = "par_" + item.getFieldName().replace('.', '_');
301: if (parValues.containsKey(parName)) {
302: tempParName = parName;
303: int dupl = 0;
304:
305: while (parValues.containsKey(parName)) {
306: parName = tempParName + (++dupl);
307: }
308: }
309:
310: fieldName = item.getFieldName();
311:
312: // SEARCH IF FIELD NAME IS A RESERVER KEYWORK
313: String upperCaseFieldName = fieldName.toUpperCase();
314: StringTokenizer tokenizer = new StringTokenizer(
315: SQLConstants.SQL99_RESERVED_WORDS, ",");
316: while (tokenizer.hasMoreTokens()) {
317: if (upperCaseFieldName.equals(tokenizer.nextToken())) {
318: // PROTECT FIELD NAME BY PREFIXING 'THIS.'
319: fieldName = THIS_PREFIX_KEYWORD + fieldName;
320: break;
321: }
322: }
323:
324: field = def.getField(item.getFieldName());
325:
326: if (field == null)
327: throw new PersistenceException(
328: "Cannot execute query: field "
329: + item.getFieldName()
330: + " not found in class "
331: + iQueryFilter.getCandidateClass()
332: .getSimpleName());
333:
334: // GET FIELD VALUE
335: itemValue = item.getFieldValue();
336:
337: // if (itemValue == null)
338: // itemValue = "null";
339:
340: switch (getOperatorMode(item.getFieldOperator())) {
341: case MODE_FIELD_THEN_PAR:
342: typeName = field.getTypeClass().getName();
343: break;
344: case MODE_PAR_THEN_FIELD:
345: typeName = itemValue.getClass().getName();
346: break;
347: default:
348: return;
349: }
350:
351: if (Collection.class.isAssignableFrom(field.getTypeClass()))
352: // COLLECTION: GET EMBEDDED TYPE
353: typeName = ((Class) SchemaHelper.getEmbeddedType(field))
354: .getName();
355:
356: // INSERT PREDICATE OPERATOR
357: if (queryText.length() > 0) {
358: queryText
359: .append(translatePredicateOperatorBegin(iQueryFilter
360: .getPredicateOperator()));
361: }
362:
363: // INSERT PARAMETER
364: if (iObjectName != null) {
365: queryText.append(iObjectName);
366: queryText.append(".");
367: }
368:
369: switch (getOperatorMode(item.getFieldOperator())) {
370: case MODE_FIELD_THEN_PAR:
371: queryText.append(fieldName);
372: queryText.append(translateFieldOperatorBegin(item
373: .getFieldOperator()));
374: queryText.append(parName);
375: break;
376: case MODE_PAR_THEN_FIELD:
377: queryText.append(parName);
378: queryText.append(translateFieldOperatorBegin(item
379: .getFieldOperator()));
380: queryText.append(fieldName);
381: break;
382:
383: default:
384: break;
385: }
386:
387: queryText.append(translateFieldOperatorEnd(item
388: .getFieldOperator()));
389:
390: queryText.append(translatePredicateOperatorEnd(iQueryFilter
391: .getPredicateOperator()));
392:
393: if (item.getFieldValue() instanceof QueryByFilter) {
394: // NESTED FILTER: EXECUTE MYSELF RECURSIVELY
395: Class nestedClass = field.getTypeClass();
396: if (nestedClass == null)
397: nestedClass = (Class) SchemaHelper
398: .getEmbeddedType(def.getField(fieldName));
399:
400: SchemaClassDefinition nestedDef = ObjectContext
401: .getInstance()
402: .getComponent(SchemaManager.class)
403: .getClassInfo(nestedClass, null);
404:
405: // DECLARE VARIABLE
406: if (queryVariables.length() > 0)
407: queryVariables.append(", ");
408:
409: queryVariables.append(typeName);
410: queryVariables.append(" ");
411: queryVariables.append(parName);
412:
413: formatParameters((QueryByFilter) item.getFieldValue(),
414: nestedDef, queryText, queryParameters,
415: queryVariables, parValues, parName);
416: } else {
417: // DECLARE PARAMETER
418: if (queryParameters.length() > 0)
419: queryParameters.append(", ");
420:
421: queryParameters.append(typeName);
422: queryParameters.append(" ");
423: queryParameters.append(parName);
424:
425: if (item.getFieldOperator().equals(
426: QueryByFilter.FIELD_LIKE))
427: itemValue = ".*"
428: + itemValue.toString().toUpperCase() + ".*";
429:
430: parValues.put(parName, itemValue);
431: }
432: }
433: }
434:
435: public static List queryByText(PersistenceManager manager,
436: QueryByText iQuery) throws PersistenceException {
437: if (log.isDebugEnabled())
438: log.debug("[JDOPersistenceAspect.queryByText] "
439: + iQuery.getText());
440: return queryByText(manager, iQuery, iQuery.getCandidateClass(),
441: iQuery.getText(), iQuery.getRangeFrom(), iQuery
442: .getRangeTo(), iQuery.isSubClasses(), iQuery
443: .getMode());
444: }
445:
446: public static void setOrdering(QueryByFilter iQueryFilter,
447: Query query, StringBuffer queryOrders) {
448: // SET ORDERING IF ANY
449: QueryByFilterOrder order;
450: for (Iterator<QueryByFilterOrder> it = iQueryFilter
451: .getOrdersIterator(); it.hasNext();) {
452: order = it.next();
453:
454: if (queryOrders.length() > 0)
455: queryOrders.append(", ");
456:
457: queryOrders.append(order.getFieldName());
458: queryOrders.append(translateOrderingMode(order
459: .getFieldOrder()));
460: }
461:
462: if (queryOrders.length() > 0)
463: query.setOrdering(queryOrders.toString());
464: }
465:
466: public static int getOperatorMode(String iFieldOperator) {
467: if (iFieldOperator.equals(QueryByFilter.FIELD_IN))
468: return MODE_PAR_THEN_FIELD;
469:
470: return MODE_FIELD_THEN_PAR;
471: }
472:
473: public static String translateFieldOperatorBegin(
474: String iFieldOperator) {
475: if (iFieldOperator.equals(QueryByFilter.FIELD_EQUALS))
476: return " == ";
477: else if (iFieldOperator.equals(QueryByFilter.FIELD_MINOR))
478: return " < ";
479: else if (iFieldOperator.equals(QueryByFilter.FIELD_MAJOR))
480: return " > ";
481: else if (iFieldOperator
482: .equals(QueryByFilter.FIELD_MINOR_EQUALS))
483: return " <= ";
484: else if (iFieldOperator
485: .equals(QueryByFilter.FIELD_MAJOR_EQUALS))
486: return " >= ";
487: else if (iFieldOperator.equals(QueryByFilter.FIELD_NOT_EQUALS))
488: return " != ";
489: else if (iFieldOperator.equals(QueryByFilter.FIELD_LIKE))
490: return ".toUpperCase().matches(";
491: else if (iFieldOperator.equals(QueryByFilter.FIELD_CONTAINS))
492: return ".contains(";
493: else if (iFieldOperator.equals(QueryByFilter.FIELD_IN))
494: return ".contains(";
495:
496: return "";
497: }
498:
499: public static String translateFieldOperatorEnd(String iFieldOperator) {
500: if (iFieldOperator.equals(QueryByFilter.FIELD_LIKE))
501: return ")";
502: else if (iFieldOperator.equals(QueryByFilter.FIELD_CONTAINS))
503: return ")";
504: else if (iFieldOperator.equals(QueryByFilter.FIELD_IN))
505: return ")";
506: return "";
507: }
508:
509: public static String translatePredicateOperatorBegin(
510: String iPredicateOperator) {
511: if (iPredicateOperator.equals(QueryByFilter.PREDICATE_AND))
512: return " && ";
513: else if (iPredicateOperator.equals(QueryByFilter.PREDICATE_OR))
514: return " || ";
515: else if (iPredicateOperator.equals(QueryByFilter.PREDICATE_NOT))
516: return " !";
517: return "";
518: }
519:
520: public static String translatePredicateOperatorEnd(
521: String iPredicateOperator) {
522: return "";
523: }
524:
525: public static String translateOrderingMode(String iOrderMode) {
526: if (iOrderMode.equals(QueryByFilter.ORDER_ASC))
527: return " ascending";
528: else if (iOrderMode.equals(QueryByFilter.ORDER_DESC))
529: return " descending";
530: return "";
531: }
532:
533: public static List queryByText(PersistenceManager manager,
534: org.romaframework.aspect.persistence.Query iQuerySource,
535: Class iCandidateClass, String iQuery, int iRangeFrom,
536: int iRangeTo, boolean iSubClasses, String iMode)
537: throws PersistenceException {
538: if (log.isDebugEnabled())
539: log.debug("[JDOPersistenceAspect.query] " + iQuery);
540:
541: List result = null;
542:
543: // EXECUTE QUERY
544: Query query = getQuery(manager, iQuerySource, iCandidateClass,
545: iRangeFrom, iRangeTo, iSubClasses, iMode);
546: Query countQuery = getQuery(manager, iQuerySource,
547: iCandidateClass, iRangeFrom, iRangeTo, iSubClasses,
548: iMode);
549:
550: query.setFilter(iQuery);
551: countQuery.setFilter(iQuery);
552:
553: prepareQuery(manager, iQuerySource, query, countQuery, null);
554:
555: List tempResult = (List) query.execute();
556: result = retrieveObjects(manager, iQuerySource, tempResult);
557:
558: closeQuery(result, query, iQuerySource.getStrategy());
559:
560: if (iQuerySource.getTotalItems() == -1)
561: iQuerySource.setTotalItems(result != null ? result.size()
562: : 0);
563:
564: if (log.isDebugEnabled())
565: log.debug("[JDOPersistenceAspect.query] Result: "
566: + result.size());
567:
568: return result;
569: }
570:
571: public static Object retrieveObject(PersistenceManager manager,
572: String iMode, byte iStrategy, Object iObject) {
573: if (iObject == null)
574: return null;
575:
576: Object tempResult;
577:
578: switch (iStrategy) {
579: case PersistenceAspect.STRATEGY_DETACHING:
580: tempResult = manager.detachCopy(iObject);
581: break;
582: case PersistenceAspect.STRATEGY_TRANSIENT:
583: tempResult = iObject;
584: manager.makeTransient(tempResult, iMode != null);
585: break;
586: default:
587: tempResult = iObject;
588: }
589: return tempResult;
590: }
591:
592: public static List retrieveObjects(PersistenceManager manager,
593: org.romaframework.aspect.persistence.Query iQuerySource,
594: List tempResult) {
595: switch (iQuerySource.getStrategy()) {
596: case PersistenceAspect.STRATEGY_DETACHING:
597: tempResult = (List) manager.detachCopyAll(tempResult);
598: break;
599: case PersistenceAspect.STRATEGY_TRANSIENT:
600: manager.makeTransientAll(tempResult,
601: iQuerySource.getMode() != null);
602: break;
603: }
604: return tempResult;
605: }
606:
607: public static void closeManager(PersistenceManager manager) {
608: if (manager != null && !manager.isClosed()) {
609: if (manager.currentTransaction().isActive())
610: manager.currentTransaction().rollback();
611:
612: manager.close();
613: }
614: }
615:
616: /**
617: * Get a configured JDO PersistenceManager instance from factory.
618: *
619: * @return PersistenceManager from the factory
620: */
621: public static PersistenceManager getPersistenceManager(
622: PersistenceManagerFactory factory) {
623: PersistenceManager pm = factory.getPersistenceManager();
624:
625: // SET NO LIMIT FOR FETCHING LINKED OBJECTS
626: pm.getFetchPlan().setMaxFetchDepth(-1);
627:
628: return pm;
629: }
630:
631: private static Log log = LogFactory
632: .getLog(JDOPersistenceHelper.class);
633:
634: private static final String THIS_PREFIX_KEYWORD = "this.";
635: }
|