KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > util > BrokerHelper


1 package org.apache.ojb.broker.util;
2
3 /* Copyright 2002-2005 The Apache Software Foundation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 import java.sql.PreparedStatement JavaDoc;
19 import java.sql.ResultSet JavaDoc;
20 import java.sql.SQLException JavaDoc;
21 import java.util.ArrayList JavaDoc;
22 import java.util.Collection JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.Map JavaDoc;
26 import java.util.StringTokenizer JavaDoc;
27
28 import org.apache.commons.collections.CollectionUtils;
29 import org.apache.commons.collections.iterators.ArrayIterator;
30 import org.apache.commons.collections.map.ReferenceIdentityMap;
31 import org.apache.ojb.broker.Identity;
32 import org.apache.ojb.broker.ManageableCollection;
33 import org.apache.ojb.broker.MtoNImplementor;
34 import org.apache.ojb.broker.OJBRuntimeException;
35 import org.apache.ojb.broker.PBKey;
36 import org.apache.ojb.broker.PersistenceBrokerException;
37 import org.apache.ojb.broker.accesslayer.StatementManagerIF;
38 import org.apache.ojb.broker.accesslayer.sql.SqlExistStatement;
39 import org.apache.ojb.broker.core.PersistenceBrokerImpl;
40 import org.apache.ojb.broker.core.ValueContainer;
41 import org.apache.ojb.broker.core.proxy.IndirectionHandler;
42 import org.apache.ojb.broker.core.proxy.ProxyHelper;
43 import org.apache.ojb.broker.metadata.ClassDescriptor;
44 import org.apache.ojb.broker.metadata.CollectionDescriptor;
45 import org.apache.ojb.broker.metadata.FieldDescriptor;
46 import org.apache.ojb.broker.metadata.FieldHelper;
47 import org.apache.ojb.broker.metadata.MetadataException;
48 import org.apache.ojb.broker.metadata.MetadataManager;
49 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
50 import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
51 import org.apache.ojb.broker.platforms.Platform;
52 import org.apache.ojb.broker.query.Criteria;
53 import org.apache.ojb.broker.query.MtoNQuery;
54 import org.apache.ojb.broker.query.Query;
55 import org.apache.ojb.broker.query.QueryByCriteria;
56 import org.apache.ojb.broker.query.QueryBySQL;
57 import org.apache.ojb.broker.query.ReportQueryByCriteria;
58 import org.apache.ojb.broker.query.ReportQueryByMtoNCriteria;
59 import org.apache.ojb.broker.util.logging.LoggerFactory;
60 import org.apache.ojb.broker.util.sequence.SequenceManagerException;
61
62 /**
63  * This class contains helper methods primarily used by the {@link org.apache.ojb.broker.PersistenceBroker}
64  * implementation (e.g. contains methods to assign the the values of 'autoincrement' fields).
65  * <br/>
66  * Furthermore it was used to introduce new features related to {@link org.apache.ojb.broker.PersistenceBroker} - these
67  * new features and services (if they stand the test of time) will be moved to separate services in future.
68  *
69  * @author <a HREF="mailto:armin@codeAuLait.de">Armin Waibel</a>
70  * @version $Id: BrokerHelper.java,v 1.57.2.23 2005/12/21 22:27:47 tomdz Exp $
71  */

72 public class BrokerHelper
73 {
74     public static final String JavaDoc REPOSITORY_NAME_SEPARATOR = "#";
75     private PersistenceBrokerImpl m_broker;
76
77     public BrokerHelper(PersistenceBrokerImpl broker)
78     {
79         this.m_broker = broker;
80     }
81
82     /**
83      * splits up the name string and extract db url,
84      * user name and password and build a new PBKey
85      * instance - the token '#' is used to separate
86      * the substrings.
87      * @throws PersistenceBrokerException if given name was <code>null</code>
88      */

89     public static PBKey extractAllTokens(String JavaDoc name)
90     {
91         if(name == null)
92         {
93             throw new PersistenceBrokerException("Could not extract PBKey, given argument is 'null'");
94         }
95         String JavaDoc user = null;
96         String JavaDoc passwd = null;
97         StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(name, REPOSITORY_NAME_SEPARATOR);
98         String JavaDoc dbName = tok.nextToken();
99         if(tok.hasMoreTokens())
100         {
101             user = tok.nextToken();
102             if(user != null && user.trim().equals(""))
103             {
104                 user = null;
105             }
106         }
107         if(tok.hasMoreTokens())
108         {
109             if(user != null)
110                 passwd = tok.nextToken();
111         }
112         if(user != null && passwd == null)
113         {
114             passwd = "";
115         }
116         return new PBKey(dbName, user, passwd);
117     }
118
119     /**
120      * Check if the user of the given PBKey was <code>null</code>, if so we try to
121      * get user/password from the jdbc-connection-descriptor matching the given
122      * PBKey.getAlias().
123      */

124     public static PBKey crossCheckPBKey(PBKey key)
125     {
126         if(key.getUser() == null)
127         {
128             PBKey defKey = MetadataManager.getInstance().connectionRepository().getStandardPBKeyForJcdAlias(key.getAlias());
129             if(defKey != null)
130             {
131                 return defKey;
132             }
133         }
134         return key;
135     }
136
137     /**
138      * Answer the real ClassDescriptor for anObj
139      * ie. aCld may be an Interface of anObj, so the cld for anObj is returned
140      */

141     private ClassDescriptor getRealClassDescriptor(ClassDescriptor aCld, Object JavaDoc anObj)
142     {
143         ClassDescriptor result;
144
145         if(aCld.getClassOfObject() == ProxyHelper.getRealClass(anObj))
146         {
147             result = aCld;
148         }
149         else
150         {
151             result = aCld.getRepository().getDescriptorFor(anObj.getClass());
152         }
153
154         return result;
155     }
156
157     /**
158      * Returns an Array with an Objects PK VALUES if convertToSql is true, any
159      * associated java-to-sql conversions are applied. If the Object is a Proxy
160      * or a VirtualProxy NO conversion is necessary.
161      *
162      * @param objectOrProxy
163      * @param convertToSql
164      * @return Object[]
165      * @throws PersistenceBrokerException
166      */

167     public ValueContainer[] getKeyValues(ClassDescriptor cld, Object JavaDoc objectOrProxy, boolean convertToSql) throws PersistenceBrokerException
168     {
169         IndirectionHandler handler = ProxyHelper.getIndirectionHandler(objectOrProxy);
170
171         if(handler != null)
172         {
173             return getKeyValues(cld, handler.getIdentity(), convertToSql); //BRJ: convert Identity
174
}
175         else
176         {
177             ClassDescriptor realCld = getRealClassDescriptor(cld, objectOrProxy);
178             return getValuesForObject(realCld.getPkFields(), objectOrProxy, convertToSql);
179         }
180     }
181
182     /**
183      * Return primary key values of given Identity object.
184      *
185      * @param cld
186      * @param oid
187      * @return Object[]
188      * @throws PersistenceBrokerException
189      */

190     public ValueContainer[] getKeyValues(ClassDescriptor cld, Identity oid) throws PersistenceBrokerException
191     {
192         return getKeyValues(cld, oid, true);
193     }
194
195     /**
196      * Return key Values of an Identity
197      * @param cld
198      * @param oid
199      * @param convertToSql
200      * @return Object[]
201      * @throws PersistenceBrokerException
202      */

203     public ValueContainer[] getKeyValues(ClassDescriptor cld, Identity oid, boolean convertToSql) throws PersistenceBrokerException
204     {
205         FieldDescriptor[] pkFields = cld.getPkFields();
206         ValueContainer[] result = new ValueContainer[pkFields.length];
207         Object JavaDoc[] pkValues = oid.getPrimaryKeyValues();
208
209         try
210         {
211             for(int i = 0; i < result.length; i++)
212             {
213                 FieldDescriptor fd = pkFields[i];
214                 Object JavaDoc cv = pkValues[i];
215                 if(convertToSql)
216                 {
217                     // BRJ : apply type and value mapping
218
cv = fd.getFieldConversion().javaToSql(cv);
219                 }
220                 result[i] = new ValueContainer(cv, fd.getJdbcType());
221             }
222         }
223         catch(Exception JavaDoc e)
224         {
225             throw new PersistenceBrokerException("Can't generate primary key values for given Identity " + oid, e);
226         }
227         return result;
228     }
229
230     /**
231      * returns an Array with an Objects PK VALUES, with any java-to-sql
232      * FieldConversion applied. If the Object is a Proxy or a VirtualProxy NO
233      * conversion is necessary.
234      *
235      * @param objectOrProxy
236      * @return Object[]
237      * @throws PersistenceBrokerException
238      */

239     public ValueContainer[] getKeyValues(ClassDescriptor cld, Object JavaDoc objectOrProxy) throws PersistenceBrokerException
240     {
241         return getKeyValues(cld, objectOrProxy, true);
242     }
243
244     /**
245      * Decide if the given object value represents 'null'.<br/>
246      *
247      * - If given value is 'null' itself, true will be returned<br/>
248      *
249      * - If given value is instance of Number with value 0 and the field-descriptor
250      * represents a primitive field, true will be returned<br/>
251      *
252      * - If given value is instance of String with length 0 and the field-descriptor
253      * is a primary key, true will be returned<br/>
254      */

255     public boolean representsNull(FieldDescriptor fld, Object JavaDoc aValue)
256     {
257         if(aValue == null) return true;
258
259         boolean result = false;
260         if(((aValue instanceof Number JavaDoc) && (((Number JavaDoc) aValue).longValue() == 0)))
261         {
262             Class JavaDoc type = fld.getPersistentField().getType();
263             /*
264             AnonymousPersistentFields will *always* have a null type according to the
265             javadoc comments in AnonymousPersistentField.getType() and never represents
266             a primitve java field with value 0, thus we return always 'false' in this case.
267             (If the value object is null, the first check above return true)
268             */

269             if(type != null)
270             {
271                 result = type.isPrimitive();
272             }
273         }
274         // TODO: Do we need this check?? String could be nullified, why should we assume
275
// it's 'null' on empty string?
276
else if((aValue instanceof String JavaDoc) && (((String JavaDoc) aValue).length() == 0))
277         {
278             result = fld.isPrimaryKey();
279         }
280         return result;
281     }
282
283     /**
284      * Detect if the given object has a PK field represents a 'null' value.
285      */

286     public boolean hasNullPKField(ClassDescriptor cld, Object JavaDoc obj)
287     {
288         FieldDescriptor[] fields = cld.getPkFields();
289         boolean hasNull = false;
290         // an unmaterialized proxy object can never have nullified PK's
291
IndirectionHandler handler = ProxyHelper.getIndirectionHandler(obj);
292         if(handler == null || handler.alreadyMaterialized())
293         {
294             if(handler != null) obj = handler.getRealSubject();
295             FieldDescriptor fld;
296             for(int i = 0; i < fields.length; i++)
297             {
298                 fld = fields[i];
299                 hasNull = representsNull(fld, fld.getPersistentField().get(obj));
300                 if(hasNull) break;
301             }
302         }
303         return hasNull;
304     }
305
306     /**
307      * Set an autoincremented value in given object field that has already
308      * had a field conversion run on it, if an value for the given field is
309      * already set, it will be overridden - no further checks are done.
310      * <p>
311      * The data type of the value that is returned by this method is
312      * compatible with the java-world. The return value has <b>NOT</b>
313      * been run through a field conversion and converted to a corresponding
314      * sql-type.
315      *
316      * @return the autoincremented value set on given object
317      * @throws PersistenceBrokerException if there is an erros accessing obj field values
318      */

319     private Object JavaDoc setAutoIncrementValue(FieldDescriptor fd, Object JavaDoc obj)
320     {
321         PersistentField f = fd.getPersistentField();
322         try
323         {
324             // lookup SeqMan for a value matching db column an
325
Object JavaDoc result = m_broker.serviceSequenceManager().getUniqueValue(fd);
326             // reflect autoincrement value back into object
327
f.set(obj, result);
328             return result;
329         }
330         catch(MetadataException e)
331         {
332             throw new PersistenceBrokerException(
333                     "Error while trying to autoincrement field " + f.getDeclaringClass() + "#" + f.getName(),
334                     e);
335         }
336         catch(SequenceManagerException e)
337         {
338             throw new PersistenceBrokerException("Could not get key value", e);
339         }
340     }
341
342     /**
343      * Get the values of the fields for an obj
344      * Autoincrement values are automatically set.
345      * @param fields
346      * @param obj
347      * @throws PersistenceBrokerException
348      */

349     public ValueContainer[] getValuesForObject(FieldDescriptor[] fields, Object JavaDoc obj, boolean convertToSql, boolean assignAutoincrement) throws PersistenceBrokerException
350     {
351         ValueContainer[] result = new ValueContainer[fields.length];
352
353         for(int i = 0; i < fields.length; i++)
354         {
355             FieldDescriptor fd = fields[i];
356             Object JavaDoc cv = fd.getPersistentField().get(obj);
357
358             /*
359             handle autoincrement attributes if
360             - is a autoincrement field
361             - field represents a 'null' value, is nullified
362             and generate a new value
363             */

364             if(assignAutoincrement && fd.isAutoIncrement() && representsNull(fd, cv))
365             {
366                 /*
367                 setAutoIncrementValue returns a value that is
368                 properly typed for the java-world. This value
369                 needs to be converted to it's corresponding
370                 sql type so that the entire result array contains
371                 objects that are properly typed for sql.
372                 */

373                 cv = setAutoIncrementValue(fd, obj);
374             }
375             if(convertToSql)
376             {
377                 // apply type and value conversion
378
cv = fd.getFieldConversion().javaToSql(cv);
379             }
380             // create ValueContainer
381
result[i] = new ValueContainer(cv, fd.getJdbcType());
382         }
383         return result;
384     }
385
386     public ValueContainer[] getValuesForObject(FieldDescriptor[] fields, Object JavaDoc obj, boolean convertToSql) throws PersistenceBrokerException
387     {
388         return getValuesForObject(fields, obj, convertToSql, false);
389     }
390
391     /**
392      * Returns an array containing values for all non PK field READ/WRITE attributes of the object
393      * based on the specified {@link org.apache.ojb.broker.metadata.ClassDescriptor}.
394      * <br/>
395      * NOTE: This method doesn't do any checks on the specified {@link org.apache.ojb.broker.metadata.ClassDescriptor}
396      * the caller is reponsible to pass a valid descriptor.
397      *
398      * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} to extract the RW-fields
399      * @param obj The object with target fields to extract.
400      * @throws MetadataException if there is an erros accessing obj field values
401      */

402     public ValueContainer[] getNonKeyRwValues(ClassDescriptor cld, Object JavaDoc obj) throws PersistenceBrokerException
403     {
404         return getValuesForObject(cld.getNonPkRwFields(), obj, true);
405     }
406
407     /**
408      * Returns an array containing values for all READ/WRITE attributes of the object
409      * based on the specified {@link org.apache.ojb.broker.metadata.ClassDescriptor}.
410      * <br/>
411      * NOTE: This method doesn't do any checks on the specified {@link org.apache.ojb.broker.metadata.ClassDescriptor}
412      * the caller is reponsible to pass a valid descriptor.
413      *
414      * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} to extract the RW-fields
415      * @param obj The object with target fields to extract.
416      * @throws MetadataException if there is an erros accessing obj field values
417      */

418     public ValueContainer[] getAllRwValues(ClassDescriptor cld, Object JavaDoc obj) throws PersistenceBrokerException
419     {
420         return getValuesForObject(cld.getAllRwFields(), obj, true);
421     }
422
423     /**
424      * Extract an value array of the given {@link ValueContainer} array.
425      * @param containers
426      * @return An object array
427      */

428     public Object JavaDoc[] extractValueArray(ValueContainer[] containers)
429     {
430         Object JavaDoc[] result = new Object JavaDoc[containers.length];
431         for(int i = 0; i < containers.length; i++)
432         {
433             result[i] = containers[i].getValue();
434         }
435         return result;
436     }
437
438     /**
439      * returns true if the primary key fields are valid for store, else false.
440      * PK fields are valid if each of them is either an OJB managed
441      * attribute (autoincrement or locking) or if it contains
442      * a valid non-null value
443      * @param fieldDescriptors the array of PK fielddescriptors
444      * @param pkValues the array of PK values
445      * @return boolean
446      */

447     public boolean assertValidPksForStore(FieldDescriptor[] fieldDescriptors, Object JavaDoc[] pkValues)
448     {
449         int fieldDescriptorSize = fieldDescriptors.length;
450         for(int i = 0; i < fieldDescriptorSize; i++)
451         {
452             FieldDescriptor fld = fieldDescriptors[i];
453             /**
454              * a pk field is valid if it is either managed by OJB
455              * (autoincrement or locking) or if it does contain a
456              * valid non-null value.
457              */

458             if(!(fld.isAutoIncrement()
459                     || fld.isLocking()
460                     || !representsNull(fld, pkValues[i])))
461             {
462                 return false;
463             }
464         }
465         return true;
466     }
467
468     /**
469      * returns true if the primary key fields are valid for delete, else false.
470      * PK fields are valid if each of them contains a valid non-null value
471      * @param cld the ClassDescriptor
472      * @param obj the object
473      * @return boolean
474      */

475     public boolean assertValidPkForDelete(ClassDescriptor cld, Object JavaDoc obj)
476     {
477         if(!ProxyHelper.isProxy(obj))
478         {
479             FieldDescriptor fieldDescriptors[] = cld.getPkFields();
480             int fieldDescriptorSize = fieldDescriptors.length;
481             for(int i = 0; i < fieldDescriptorSize; i++)
482             {
483                 FieldDescriptor fd = fieldDescriptors[i];
484                 Object JavaDoc pkValue = fd.getPersistentField().get(obj);
485                 if (representsNull(fd, pkValue))
486                 {
487                     return false;
488                 }
489             }
490         }
491         return true;
492     }
493
494     /**
495      * Build a Count-Query based on aQuery
496      * @param aQuery
497      * @return The count query
498      */

499     public Query getCountQuery(Query aQuery)
500     {
501         if(aQuery instanceof QueryBySQL)
502         {
503             return getQueryBySqlCount((QueryBySQL) aQuery);
504         }
505         else if(aQuery instanceof ReportQueryByCriteria)
506         {
507             return getReportQueryByCriteriaCount((ReportQueryByCriteria) aQuery);
508         }
509         else
510         {
511             return getQueryByCriteriaCount((QueryByCriteria) aQuery);
512         }
513     }
514
515     /**
516      * Create a Count-Query for QueryBySQL
517      *
518      * @param aQuery
519      * @return The count query
520      */

521     private Query getQueryBySqlCount(QueryBySQL aQuery)
522     {
523         String JavaDoc countSql = aQuery.getSql();
524
525         int fromPos = countSql.toUpperCase().indexOf(" FROM ");
526         if(fromPos >= 0)
527         {
528             countSql = "select count(*)" + countSql.substring(fromPos);
529         }
530
531         int orderPos = countSql.toUpperCase().indexOf(" ORDER BY ");
532         if(orderPos >= 0)
533         {
534             countSql = countSql.substring(0, orderPos);
535         }
536
537         return new QueryBySQL(aQuery.getSearchClass(), countSql);
538     }
539
540     /**
541      * Create a Count-Query for QueryByCriteria
542      */

543     private Query getQueryByCriteriaCount(QueryByCriteria aQuery)
544     {
545         Class JavaDoc searchClass = aQuery.getSearchClass();
546         ReportQueryByCriteria countQuery = null;
547         Criteria countCrit = null;
548         String JavaDoc[] columns = new String JavaDoc[1];
549
550         // BRJ: copied Criteria without groupby, orderby, and prefetched relationships
551
if (aQuery.getCriteria() != null)
552         {
553             countCrit = aQuery.getCriteria().copy(false, false, false);
554         }
555
556         if (aQuery.isDistinct())
557         {
558             // BRJ: Count distinct is dbms dependent
559
// hsql/sapdb: select count (distinct(person_id || project_id)) from person_project
560
// mysql: select count (distinct person_id,project_id) from person_project
561
// [tomdz]
562
// Some databases have no support for multi-column count distinct (e.g. Derby)
563
// Here we use a SELECT count(*) FROM (SELECT DISTINCT ...) instead
564
//
565
// concatenation of pk-columns is a simple way to obtain a single column
566
// but concatenation is also dbms dependent:
567
//
568
// SELECT count(distinct concat(row1, row2, row3)) mysql
569
// SELECT count(distinct (row1 || row2 || row3)) ansi
570
// SELECT count(distinct (row1 + row2 + row3)) ms sql-server
571

572             FieldDescriptor[] pkFields = m_broker.getClassDescriptor(searchClass).getPkFields();
573             String JavaDoc[] keyColumns = new String JavaDoc[pkFields.length];
574
575             if (pkFields.length > 1)
576             {
577                 // TODO: Use ColumnName. This is a temporary solution because
578
// we cannot yet resolve multiple columns in the same attribute.
579
for (int idx = 0; idx < pkFields.length; idx++)
580                 {
581                     keyColumns[idx] = pkFields[idx].getColumnName();
582                 }
583             }
584             else
585             {
586                 for (int idx = 0; idx < pkFields.length; idx++)
587                 {
588                     keyColumns[idx] = pkFields[idx].getAttributeName();
589                 }
590             }
591             // [tomdz]
592
// TODO: Add support for databases that do not support COUNT DISTINCT over multiple columns
593
// if (getPlatform().supportsMultiColumnCountDistinct())
594
// {
595
// columns[0] = "count(distinct " + getPlatform().concatenate(keyColumns) + ")";
596
// }
597
// else
598
// {
599
// columns = keyColumns;
600
// }
601

602             columns[0] = "count(distinct " + getPlatform().concatenate(keyColumns) + ")";
603         }
604         else
605         {
606             columns[0] = "count(*)";
607         }
608
609         // BRJ: we have to preserve indirection table !
610
if (aQuery instanceof MtoNQuery)
611         {
612             MtoNQuery mnQuery = (MtoNQuery)aQuery;
613             ReportQueryByMtoNCriteria mnReportQuery = new ReportQueryByMtoNCriteria(searchClass, columns, countCrit);
614
615             mnReportQuery.setIndirectionTable(mnQuery.getIndirectionTable());
616             countQuery = mnReportQuery;
617         }
618         else
619         {
620             countQuery = new ReportQueryByCriteria(searchClass, columns, countCrit);
621         }
622
623         // BRJ: we have to preserve outer-join-settings (by André Markwalder)
624
for (Iterator JavaDoc outerJoinPath = aQuery.getOuterJoinPaths().iterator(); outerJoinPath.hasNext();)
625         {
626             String JavaDoc path = (String JavaDoc) outerJoinPath.next();
627
628             if (aQuery.isPathOuterJoin(path))
629             {
630                 countQuery.setPathOuterJoin(path);
631             }
632         }
633
634         //BRJ: add orderBy Columns asJoinAttributes
635
List JavaDoc orderBy = aQuery.getOrderBy();
636
637         if ((orderBy != null) && !orderBy.isEmpty())
638         {
639             String JavaDoc[] joinAttributes = new String JavaDoc[orderBy.size()];
640
641             for (int idx = 0; idx < orderBy.size(); idx++)
642             {
643                 joinAttributes[idx] = ((FieldHelper)orderBy.get(idx)).name;
644             }
645             countQuery.setJoinAttributes(joinAttributes);
646         }
647
648         // [tomdz]
649
// TODO:
650
// For those databases that do not support COUNT DISTINCT over multiple columns
651
// we wrap the normal SELECT DISTINCT that we just created, into a SELECT count(*)
652
// For this however we need a report query that gets its data from a sub query instead
653
// of a table (target class)
654
// if (aQuery.isDistinct() && !getPlatform().supportsMultiColumnCountDistinct())
655
// {
656
// }
657

658         return countQuery;
659     }
660
661     /**
662      * Create a Count-Query for ReportQueryByCriteria
663      */

664     private Query getReportQueryByCriteriaCount(ReportQueryByCriteria aQuery)
665     {
666         ReportQueryByCriteria countQuery = (ReportQueryByCriteria) getQueryByCriteriaCount(aQuery);
667
668         // BRJ: keep the original columns to build the Join
669
countQuery.setJoinAttributes(aQuery.getAttributes());
670
671         // BRJ: we have to preserve groupby information
672
Iterator JavaDoc iter = aQuery.getGroupBy().iterator();
673         while(iter.hasNext())
674         {
675             countQuery.addGroupBy((FieldHelper) iter.next());
676         }
677
678         return countQuery;
679     }
680
681     /**
682      * answer the platform
683      *
684      * @return the platform
685      */

686     private Platform getPlatform()
687     {
688         return m_broker.serviceSqlGenerator().getPlatform();
689     }
690
691
692     /*
693     NOTE: use weak key references to allow reclaiming
694     of no longer used ClassDescriptor instances
695     */

696     private Map JavaDoc sqlSelectMap = new ReferenceIdentityMap(ReferenceIdentityMap.WEAK, ReferenceIdentityMap.HARD);
697     /**
698      * TODO: This method should be moved to {@link org.apache.ojb.broker.accesslayer.JdbcAccess}
699      * before 1.1 release.
700      *
701      * This method checks if the requested object can be
702      * found in database (without object materialization).
703      *
704      * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the
705      * object/{@link org.apache.ojb.broker.Identity} to check.
706      * @param obj The <em>object</em> to check.
707      * @param oid The associated {@link org.apache.ojb.broker.Identity}.
708      * {@link org.apache.ojb.broker.Identity} of the object
709      * @return Return <em>true</em> if the object is already persisted, <em>false</em> if the object is transient.
710      */

711     public boolean doesExist(ClassDescriptor cld, Identity oid, Object JavaDoc obj)
712     {
713         boolean result = false;
714         String JavaDoc sql = (String JavaDoc) sqlSelectMap.get(cld);
715         if(sql == null)
716         {
717             sql = new SqlExistStatement(cld, LoggerFactory.getDefaultLogger()).getStatement();
718             sqlSelectMap.put(cld, sql);
719         }
720         ValueContainer[] pkValues;
721         if(oid == null)
722         {
723             pkValues = getKeyValues(cld, obj, true);
724         }
725         else
726         {
727             pkValues = getKeyValues(cld, oid);
728         }
729         StatementManagerIF sm = m_broker.serviceStatementManager();
730         PreparedStatement JavaDoc stmt = null;
731         ResultSet JavaDoc rs = null;
732         try
733         {
734             stmt = sm.getPreparedStatement(cld, sql, false, 1, false);
735             sm.bindValues(stmt, pkValues, 1);
736             rs = stmt.executeQuery();
737             result = rs.next();
738         }
739         catch(SQLException JavaDoc e)
740         {
741             throw ExceptionHelper.generateException("[BrokerHelper#doesExist] Can't check if specified" +
742                     " object is already persisted", e, sql, cld, pkValues, null, obj);
743         }
744         finally
745         {
746             sm.closeResources(stmt, rs);
747         }
748
749         return result;
750     }
751
752     /**
753      * This method concatenate the main object with all reference
754      * objects (1:1, 1:n and m:n) by hand. This method is needed when
755      * in the reference metadata definitions the auto-xxx setting was disabled.
756      * More info see OJB doc.
757      */

758     public void link(Object JavaDoc obj, boolean insert)
759     {
760         linkOrUnlink(true, obj, insert);
761     }
762
763     /**
764      * Unlink all references from this object.
765      * More info see OJB doc.
766      * @param obj Object with reference
767      */

768     public void unlink(Object JavaDoc obj)
769     {
770         linkOrUnlink(false, obj, false);
771     }
772
773     private void linkOrUnlink(boolean doLink, Object JavaDoc obj, boolean insert)
774     {
775         ClassDescriptor cld = m_broker.getDescriptorRepository().getDescriptorFor(obj.getClass());
776
777         if (cld.getObjectReferenceDescriptors().size() > 0)
778         {
779             // never returns null, thus we can direct call iterator
780
Iterator JavaDoc descriptors = cld.getObjectReferenceDescriptors().iterator();
781             while (descriptors.hasNext())
782             {
783                 ObjectReferenceDescriptor ord = (ObjectReferenceDescriptor) descriptors.next();
784                 linkOrUnlinkOneToOne(doLink, obj, ord, insert);
785             }
786         }
787         if (cld.getCollectionDescriptors().size() > 0)
788         {
789             // never returns null, thus we can direct call iterator
790
Iterator JavaDoc descriptors = cld.getCollectionDescriptors().iterator();
791             while (descriptors.hasNext())
792             {
793                 CollectionDescriptor cod = (CollectionDescriptor) descriptors.next();
794                 linkOrUnlinkXToMany(doLink, obj, cod, insert);
795             }
796         }
797     }
798
799     /**
800      * This method concatenate the main object and the specified reference
801      * object (1:1 reference a referenced object, 1:n and m:n reference a
802      * collection of referenced objects) by hand. This method is needed when
803      * in the reference metadata definitions the auto-xxx setting was disabled.
804      * More info see OJB doc.
805      *
806      * @param obj Object with reference
807      * @param ord the ObjectReferenceDescriptor of the reference
808      * @param insert flag signals insert operation
809      */

810     public void link(Object JavaDoc obj, ObjectReferenceDescriptor ord, boolean insert)
811     {
812        linkOrUnlink(true, obj, ord, insert);
813     }
814
815     /**
816      * This method concatenate the main object and the specified reference
817      * object (1:1 reference a referenced object, 1:n and m:n reference a
818      * collection of referenced objects) by hand. This method is needed when
819      * in the reference metadata definitions the auto-xxx setting was disabled.
820      * More info see OJB doc.
821      *
822      * @param obj Object with reference
823      * @param attributeName field name of the reference
824      * @param insert flag signals insert operation
825      * @return true if the specified reference was found and linking was successful
826      */

827     public boolean link(Object JavaDoc obj, String JavaDoc attributeName, boolean insert)
828     {
829        return linkOrUnlink(true, obj, attributeName, insert);
830     }
831
832     /**
833      * This method concatenate the main object and the specified reference
834      * object (1:1 reference a referenced object, 1:n and m:n reference a
835      * collection of referenced objects) by hand. This method is needed when
836      * in the reference metadata definitions the auto-xxx setting was disabled.
837      * More info see OJB doc.
838      *
839      * @param obj Object with reference
840      * @param attributeName field name of the reference
841      * @param reference The referenced object
842      * @param insert flag signals insert operation
843      * @return true if the specified reference was found and linking was successful
844      */

845     public boolean link(Object JavaDoc obj, String JavaDoc attributeName, Object JavaDoc reference, boolean insert)
846     {
847         ClassDescriptor cld = m_broker.getDescriptorRepository().getDescriptorFor(ProxyHelper.getRealClass(obj));
848         ObjectReferenceDescriptor ord;
849         boolean match = false;
850         // first look for reference then for collection
851
ord = cld.getObjectReferenceDescriptorByName(attributeName);
852         if (ord != null)
853         {
854             linkOrUnlinkOneToOne(true, obj, ord, insert);
855             match = true;
856         }
857         else
858         {
859             CollectionDescriptor cod = cld.getCollectionDescriptorByName(attributeName);
860             if (cod != null)
861             {
862                 linkOrUnlinkXToMany(true, obj, cod, insert);
863                 match = true;
864             }
865         }
866         return match;
867     }
868
869     /**
870      * Unlink the specified reference object.
871      * More info see OJB doc.
872      * @param source The source object with the specified reference field.
873      * @param attributeName The field name of the reference to unlink.
874      * @param target The referenced object to unlink.
875      */

876     public boolean unlink(Object JavaDoc source, String JavaDoc attributeName, Object JavaDoc target)
877     {
878         return linkOrUnlink(false, source, attributeName, false);
879     }
880
881     /**
882      * Unlink all referenced objects of the specified field.
883      * More info see OJB doc.
884      * @param source The source object with the specified reference.
885      * @param attributeName The field name of the reference to unlink.
886      */

887     public boolean unlink(Object JavaDoc source, String JavaDoc attributeName)
888     {
889         return linkOrUnlink(false, source, attributeName, false);
890     }
891
892     /**
893      * Unlink the specified reference from this object.
894      * More info see OJB doc.
895      *
896      * @param obj Object with reference
897      * @param ord the ObjectReferenceDescriptor of the reference
898      * @param insert flag signals insert operation
899      */

900     public void unlink(Object JavaDoc obj, ObjectReferenceDescriptor ord, boolean insert)
901     {
902        linkOrUnlink(false, obj, ord, insert);
903     }
904
905     private boolean linkOrUnlink(boolean doLink, Object JavaDoc obj, String JavaDoc attributeName, boolean insert)
906     {
907         boolean match = false;
908         ClassDescriptor cld = m_broker.getDescriptorRepository().getDescriptorFor(ProxyHelper.getRealClass(obj));
909         ObjectReferenceDescriptor ord;
910
911         // first look for reference then for collection
912
ord = cld.getObjectReferenceDescriptorByName(attributeName);
913         if (ord != null)
914         {
915             linkOrUnlinkOneToOne(doLink, obj, ord, insert);
916             match = true;
917         }
918         else
919         {
920             CollectionDescriptor cod = cld.getCollectionDescriptorByName(attributeName);
921             if (cod != null)
922             {
923                 linkOrUnlinkXToMany(doLink, obj, cod, insert);
924                 match = true;
925             }
926         }
927
928         return match;
929     }
930
931     private void linkOrUnlink(boolean doLink, Object JavaDoc obj, ObjectReferenceDescriptor ord, boolean insert)
932     {
933         if (ord instanceof CollectionDescriptor)
934         {
935             linkOrUnlinkXToMany(doLink, obj, (CollectionDescriptor) ord, insert);
936         }
937         else
938         {
939             linkOrUnlinkOneToOne(doLink, obj, ord, insert);
940         }
941     }
942
943     private void linkOrUnlinkXToMany(boolean doLink, Object JavaDoc obj, CollectionDescriptor cod, boolean insert)
944     {
945         if (doLink)
946         {
947             if (cod.isMtoNRelation())
948             {
949                 m_broker.linkMtoN(obj, cod, insert);
950             }
951             else
952             {
953                 m_broker.linkOneToMany(obj, cod, insert);
954             }
955         }
956         else
957         {
958             m_broker.unlinkXtoN(obj, cod);
959         }
960     }
961
962     private void linkOrUnlinkOneToOne(boolean doLink, Object JavaDoc obj, ObjectReferenceDescriptor ord, boolean insert)
963     {
964         /*
965         arminw: we need the class-descriptor where the reference is declared, thus we ask the
966         reference-descriptor for this, instead of using the class-descriptor of the specified
967         object. If the reference was declared within an interface (should never happen) we
968         only can use the descriptor of the real class.
969         */

970         ClassDescriptor cld = ord.getClassDescriptor();
971         if(cld.isInterface())
972         {
973             cld = m_broker.getDescriptorRepository().getDescriptorFor(ProxyHelper.getRealClass(obj));
974         }
975
976         if (doLink)
977         {
978             m_broker.linkOneToOne(obj, cld, ord, insert);
979         }
980         else
981         {
982             m_broker.unlinkFK(obj, cld, ord);
983             // in 1:1 relation we have to set relation to null
984
ord.getPersistentField().set(obj, null);
985         }
986     }
987
988     /**
989      * Unlink a bunch of 1:n or m:n objects.
990      *
991      * @param source The source object with reference.
992      * @param cds The {@link org.apache.ojb.broker.metadata.CollectionDescriptor} of the relation.
993      * @param referencesToUnlink List of referenced objects to unlink.
994      */

995     public void unlink(Object JavaDoc source, CollectionDescriptor cds, List JavaDoc referencesToUnlink)
996     {
997         for(int i = 0; i < referencesToUnlink.size(); i++)
998         {
999             unlink(source, cds, referencesToUnlink.get(i));
1000        }
1001    }
1002
1003    /**
1004     * Unlink a single 1:n or m:n object.
1005     *
1006     * @param source The source object with reference.
1007     * @param cds The {@link org.apache.ojb.broker.metadata.CollectionDescriptor} of the relation.
1008     * @param referenceToUnlink The referenced object to link.
1009     */

1010    public void unlink(Object JavaDoc source, CollectionDescriptor cds, Object JavaDoc referenceToUnlink)
1011    {
1012        if(cds.isMtoNRelation())
1013        {
1014            m_broker.deleteMtoNImplementor(new MtoNImplementor(cds, source, referenceToUnlink));
1015        }
1016        else
1017        {
1018            ClassDescriptor cld = m_broker.getClassDescriptor(referenceToUnlink.getClass());
1019            m_broker.unlinkFK(referenceToUnlink, cld, cds);
1020        }
1021    }
1022
1023    /**
1024     * Link a bunch of 1:n or m:n objects.
1025     *
1026     * @param source The source object with reference.
1027     * @param cds The {@link org.apache.ojb.broker.metadata.CollectionDescriptor} of the relation.
1028     * @param referencesToLink List of referenced objects to link.
1029     */

1030    public void link(Object JavaDoc source, CollectionDescriptor cds, List JavaDoc referencesToLink)
1031    {
1032        for(int i = 0; i < referencesToLink.size(); i++)
1033        {
1034            link(source, cds, referencesToLink.get(i));
1035        }
1036    }
1037
1038    /**
1039     * Link a single 1:n or m:n object.
1040     *
1041     * @param source The source object with the declared reference.
1042     * @param cds The {@link org.apache.ojb.broker.metadata.CollectionDescriptor} of the relation declared in source object.
1043     * @param referenceToLink The referenced object to link.
1044     */

1045    public void link(Object JavaDoc source, CollectionDescriptor cds, Object JavaDoc referenceToLink)
1046    {
1047        if(cds.isMtoNRelation())
1048        {
1049            m_broker.addMtoNImplementor(new MtoNImplementor(cds, source, referenceToLink));
1050        }
1051        else
1052        {
1053            ClassDescriptor cld = m_broker.getClassDescriptor(referenceToLink.getClass());
1054            m_broker.link(referenceToLink, cld, cds, source, false);
1055        }
1056    }
1057
1058    /**
1059     * Returns an Iterator instance for {@link java.util.Collection}, object Array or
1060     * {@link org.apache.ojb.broker.ManageableCollection} instances.
1061     *
1062     * @param collectionOrArray a none <em>null</em> object of type {@link java.util.Collection},
1063     * Array or {@link org.apache.ojb.broker.ManageableCollection}.
1064     * @return Iterator able to handle given collection object
1065     */

1066    public static Iterator JavaDoc getCollectionIterator(Object JavaDoc collectionOrArray)
1067    {
1068        Iterator JavaDoc colIterator;
1069        if (collectionOrArray instanceof ManageableCollection)
1070        {
1071            colIterator = ((ManageableCollection) collectionOrArray).ojbIterator();
1072        }
1073        else if (collectionOrArray instanceof Collection JavaDoc)
1074        {
1075            colIterator = ((Collection JavaDoc) collectionOrArray).iterator();
1076        }
1077        else if (collectionOrArray.getClass().isArray())
1078        {
1079            colIterator = new ArrayIterator(collectionOrArray);
1080        }
1081        else
1082        {
1083            throw new OJBRuntimeException( "Given object collection of type '"
1084                    + (collectionOrArray != null ? collectionOrArray.getClass().toString() : "null")
1085                + "' can not be managed by OJB. Use Array, Collection or ManageableCollection instead!");
1086        }
1087        return colIterator;
1088    }
1089
1090    /**
1091     * Returns an object array for {@link java.util.Collection}, array or
1092     * {@link org.apache.ojb.broker.ManageableCollection} instances.
1093     *
1094     * @param collectionOrArray a none <em>null</em> object of type {@link java.util.Collection},
1095     * Array or {@link org.apache.ojb.broker.ManageableCollection}.
1096     * @return Object array able to handle given collection or array object
1097     */

1098    public static Object JavaDoc[] getCollectionArray(Object JavaDoc collectionOrArray)
1099    {
1100        Object JavaDoc[] result;
1101        if (collectionOrArray instanceof Collection JavaDoc)
1102        {
1103            result = ((Collection JavaDoc) collectionOrArray).toArray();
1104        }
1105        else if (collectionOrArray instanceof ManageableCollection)
1106        {
1107            Collection JavaDoc newCol = new ArrayList JavaDoc();
1108            CollectionUtils.addAll(newCol, ((ManageableCollection) collectionOrArray).ojbIterator());
1109            result = newCol.toArray();
1110        }
1111        else if (collectionOrArray.getClass().isArray())
1112        {
1113            result = (Object JavaDoc[]) collectionOrArray;
1114        }
1115        else
1116        {
1117            throw new OJBRuntimeException( "Given object collection of type '"
1118                    + (collectionOrArray != null ? collectionOrArray.getClass().toString() : "null")
1119                + "' can not be managed by OJB. Use Array, Collection or ManageableCollection instead!");
1120        }
1121        return result;
1122    }
1123
1124    /**
1125     * Returns <em>true</em> if one or more anonymous FK fields are used.
1126     * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the main object.
1127     * @param rds The {@link org.apache.ojb.broker.metadata.ObjectReferenceDescriptor} of the referenced object.
1128     * @return <em>true</em> if one or more anonymous FK fields are used for specified reference.
1129     */

1130    public static boolean hasAnonymousKeyReference(ClassDescriptor cld, ObjectReferenceDescriptor rds)
1131    {
1132        boolean result = false;
1133        FieldDescriptor[] fkFields = rds.getForeignKeyFieldDescriptors(cld);
1134        for(int i = 0; i < fkFields.length; i++)
1135        {
1136            FieldDescriptor fkField = fkFields[i];
1137            if(fkField.isAnonymous())
1138            {
1139                result = true;
1140                break;
1141            }
1142        }
1143        return result;
1144    }
1145
1146// /**
1147
// * Use this method to extract the {@link org.apache.ojb.broker.metadata.ClassDescriptor} where
1148
// * the {@link org.apache.ojb.broker.metadata.ObjectReferenceDescriptor reference} is declared.
1149
// * It's possible that the reference is declared in a super-class.
1150
// * @param broker
1151
// * @param reference
1152
// * @param source
1153
// * @return
1154
// */
1155
// public static ClassDescriptor extractDescriptorForReference(PersistenceBroker broker, ObjectReferenceDescriptor reference, Object source)
1156
// {
1157
// /*
1158
// arminw: we need the class-descriptor where the reference is declared, thus we ask the
1159
// reference-descriptor for this, instead of using the class-descriptor of the specified
1160
// object. If the reference was declared within an interface (should never happen) we
1161
// only can use the descriptor of the real class.
1162
// */
1163
// ClassDescriptor cld = reference.getClassDescriptor();
1164
// if(cld.isInterface())
1165
// {
1166
// cld = broker.getDescriptorRepository().getDescriptorFor(ProxyHelper.getRealClass(source));
1167
// }
1168
// return cld;
1169
// }
1170

1171// /**
1172
// * Returns a {@link java.util.List} instance of the specified object in method argument,
1173
// * in which the argument must be of type {@link java.util.Collection}, array or
1174
// * {@link org.apache.ojb.broker.ManageableCollection}.
1175
// *
1176
// * @param collectionOrArray a none <em>null</em> object of type {@link java.util.Collection},
1177
// * Array or {@link org.apache.ojb.broker.ManageableCollection}.
1178
// * @return Object array able to handle given collection or array object
1179
// */
1180
// public static List getCollectionList(Object collectionOrArray)
1181
// {
1182
// List result = null;
1183
// if (collectionOrArray instanceof Collection)
1184
// {
1185
// result = ((Collection) collectionOrArray).toArray();
1186
// }
1187
// else if (collectionOrArray instanceof ManageableCollection)
1188
// {
1189
// Collection newCol = new ArrayList();
1190
// CollectionUtils.addAll(newCol, ((ManageableCollection) collectionOrArray).ojbIterator());
1191
// result = newCol.toArray();
1192
// }
1193
// else if (collectionOrArray.getClass().isArray())
1194
// {
1195
// result = (Object[]) collectionOrArray;
1196
// }
1197
// else
1198
// {
1199
// throw new OJBRuntimeException( "Given object collection of type '"
1200
// + (collectionOrArray != null ? collectionOrArray.getClass().toString() : "null")
1201
// + "' can not be managed by OJB. Use Array, Collection or ManageableCollection instead!");
1202
// }
1203
// return result;
1204
// }
1205
}
1206
Popular Tags