KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > jdo > spi > persistence > support > sqlstore > model > ClassDesc


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 /*
25  * ClassDesc.java
26  *
27  * Created on March 3, 2000
28  *
29  */

30
31 package com.sun.jdo.spi.persistence.support.sqlstore.model;
32
33 import org.netbeans.modules.dbschema.ColumnElement;
34 import org.netbeans.modules.dbschema.ColumnPairElement;
35 import org.netbeans.modules.dbschema.TableElement;
36 import com.sun.jdo.api.persistence.model.Model;
37 import com.sun.jdo.api.persistence.model.jdo.ConcurrencyGroupElement;
38 import com.sun.jdo.api.persistence.model.jdo.PersistenceClassElement;
39 import com.sun.jdo.api.persistence.model.jdo.PersistenceFieldElement;
40 import com.sun.jdo.api.persistence.model.jdo.RelationshipElement;
41 import com.sun.jdo.api.persistence.model.mapping.MappingClassElement;
42 import com.sun.jdo.api.persistence.model.mapping.MappingFieldElement;
43 import com.sun.jdo.api.persistence.model.mapping.MappingRelationshipElement;
44 import com.sun.jdo.api.persistence.model.mapping.impl.*;
45 import com.sun.jdo.api.persistence.support.*;
46 import com.sun.jdo.spi.persistence.support.sqlstore.*;
47 import com.sun.jdo.spi.persistence.support.sqlstore.sql.RetrieveDescImpl;
48 import com.sun.jdo.spi.persistence.support.sqlstore.sql.UpdateObjectDescImpl;
49 import com.sun.jdo.spi.persistence.support.sqlstore.sql.concurrency.*;
50 import com.sun.jdo.spi.persistence.support.sqlstore.sql.generator.UpdateQueryPlan;
51 import com.sun.jdo.spi.persistence.utility.I18NHelper;
52 import com.sun.jdo.spi.persistence.utility.StringHelper;
53 import com.sun.jdo.spi.persistence.utility.logging.Logger;
54
55 import java.lang.reflect.Field JavaDoc;
56 import java.util.*;
57
58 /**
59  *
60  */

61 public class ClassDesc
62         implements com.sun.jdo.spi.persistence.support.sqlstore.PersistenceConfig {
63
64     private ArrayList fetchGroups;
65
66     private int maxHierarchicalGroupID;
67
68     /**
69      * Used for batched update check. Set to true if this class has at least one
70      * local field that does not belong to DFG
71      */

72     private boolean hasLocalNonDFGField;
73
74     /** Contains all local and foreign fields. */
75     public ArrayList fields;
76
77     /** Contains all hidden fields. */
78     public ArrayList hiddenFields;
79
80     /** Contains all relationship fields. */
81     public ArrayList foreignFields;
82
83     /** Contains the fields used for version consistency validation. */
84     private LocalFieldDesc[] versionFields;
85
86     private ArrayList tables;
87
88     private Class JavaDoc pcClass;
89
90     private Class JavaDoc oidClass;
91
92     public int maxFields;
93
94     public int maxVisibleFields;
95
96     public int maxHiddenFields;
97
98     private Concurrency optimisticConcurrency;
99
100     private Concurrency checkDirtyConcurrency;
101
102     private Concurrency databaseConcurrency;
103
104     private Concurrency explicitConcurrency;
105
106     private MappingClassElementImpl mdConfig;
107
108     private ClassLoader JavaDoc classLoader;
109
110     private PersistenceClassElement pcElement;
111
112     private PersistenceFieldElement[] persistentFields;
113
114     private Field JavaDoc keyFields[];
115     private String JavaDoc keyFieldNames[];
116     private LocalFieldDesc[] keyFieldDescs;
117
118     /** The logger. */
119     private static Logger logger = LogHelperSQLStore.getLogger();
120
121     /** RetrieveDescriptor cache for navigation and reloading. */
122     private final Map retrieveDescCache = new HashMap();
123
124     /**
125      * RetrieveDescriptor cache for navigation queries. This cache
126      * holds foreign RetrieveDescriptors constrained by the relationship
127      * key values.
128      */

129     private final Map foreignRetrieveDescCache = new HashMap();
130
131     /** Retrieve descriptor for version consistency verification. */
132     private RetrieveDesc retrieveDescForVerification;
133     private Object JavaDoc retrieveDescForVerificationSynchObj = new Object JavaDoc();
134
135     /** UpdateQueryPlan cache. */
136     private final Map updateQueryPlanCache = new HashMap();
137
138     /** UpdateQueryPlan for insert. */
139     private UpdateQueryPlan updateQueryPlanForInsert;
140     private Object JavaDoc updateQueryPlanForInsertSynchObj = new Object JavaDoc();
141
142     /** UpdateQueryPlan for delete. */
143     private UpdateQueryPlan updateQueryPlanForDelete;
144     private Object JavaDoc updateQueryPlanForDeleteSynchObj = new Object JavaDoc();
145
146     /** I18N message handler. */
147     private final static ResourceBundle messages = I18NHelper.loadBundle(
148             "com.sun.jdo.spi.persistence.support.sqlstore.Bundle", // NOI18N
149
ClassDesc.class.getClassLoader());
150
151     public ClassDesc(MappingClassElement mdConfig, Class JavaDoc pcClass) {
152
153         this.mdConfig = (MappingClassElementImpl) mdConfig;
154         pcElement = this.mdConfig.getPersistenceElement();
155         this.pcClass = pcClass;
156         classLoader = pcClass.getClassLoader();
157
158         fields = new ArrayList();
159         foreignFields = new ArrayList();
160         tables = new ArrayList();
161         fetchGroups = new ArrayList();
162     }
163
164     public String JavaDoc toString() {
165         return getName();
166     }
167
168     /**
169      * Creates a new instance of <code>ClassDesc</code> for the given
170      * <code>pcClass</code>.
171      * @param pcClass The persistence capable class.
172      * @return A new instance of ClassDesc.
173      */

174     static ClassDesc newInstance(Class JavaDoc pcClass) {
175         Model model = Model.RUNTIME;
176         String JavaDoc className = pcClass.getName();
177         ClassLoader JavaDoc classLoader = pcClass.getClassLoader();
178         ClassDesc rc = null;
179
180         try {
181             MappingClassElement mdConfig =
182                model.getMappingClass(className, classLoader);
183
184             // Validate the model information for this class.
185
validateModel(model, className, classLoader);
186
187             rc = new ClassDesc(mdConfig, pcClass);
188
189         } catch (JDOException e) {
190             throw e;
191         } catch (IllegalArgumentException JavaDoc e) {
192             throw new JDOFatalUserException(I18NHelper.getMessage(messages,
193                 "core.configuration.loadfailed.class", className), e); // NOI18N
194
} catch (Exception JavaDoc e) {
195             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
196                 "core.configuration.loadfailed.class", className), e); // NOI18N
197
}
198
199         return rc;
200     }
201
202     /**
203      * Validate the mapping for the given class. If the result
204      * collection returned by validate() is not empty, it means
205      * it failed the test. After logging all the exceptions,
206      * throw a JDOUserException and inform the user to go back
207      * to the mapping tool to fix up the mapping file. The
208      * JDOUserException contains the validation messages aswell.
209      *
210      * @param model Runtime model.
211      * @param className Persistence capable class' name.
212      * @param classLoader Persistence capable class' loader.
213      */

214     static private void validateModel(Model model,
215                                       String JavaDoc className,
216                                       ClassLoader JavaDoc classLoader) {
217         Collection c = null;
218
219         if (!(c = model.validate(className, classLoader, null)).isEmpty()) {
220             Iterator iter = c.iterator();
221             StringBuffer JavaDoc validationMsgs = new StringBuffer JavaDoc();
222
223             while (iter.hasNext()) {
224                 Exception JavaDoc ex = (Exception JavaDoc) iter.next();
225                 String JavaDoc validationMsg = ex.getLocalizedMessage();
226
227                 logger.fine(I18NHelper.getMessage(messages,
228                     "core.configuration.validationproblem", // NOI18N
229
className, validationMsg));
230                 validationMsgs.append(validationMsg).append('\n'); // NOI18N
231
}
232             throw new JDOFatalUserException(I18NHelper.getMessage(messages,
233                 "core.configuration.validationfailed", // NOI18N
234
className, validationMsgs.toString()));
235         }
236     }
237
238     public void initialize(ConfigCache cache) {
239         boolean debug = logger.isLoggable();
240         if (debug) {
241             logger.fine("sqlstore.model.classdesc.persistconfiginit", mdConfig); // NOI18N
242
}
243
244         loadOidClass();
245         initializeFields();
246         computeTrackedPrimitiveFields();
247
248         initializeTables();
249         initializeJoinTables();
250         initializeVersionFields();
251         initializeConcurrency();
252
253         initializeKeyFields();
254         initializeFetchGroups();
255
256         fixupForeignReferences(cache);
257         fixupFieldProperties();
258         computeTrackedRelationshipFields();
259         cleanupTrackedFields();
260
261         // All fields should be initialized at this point. Now calculate
262
// the total number of hidden fields and fields.
263
if (hiddenFields != null) {
264             maxHiddenFields = hiddenFields.size();
265         }
266         maxFields = maxVisibleFields + maxHiddenFields;
267
268         if (debug) {
269             logger.fine("sqlstore.model.classdesc.persistconfiginit.exit"); // NOI18N
270
}
271     }
272
273     private void loadOidClass() {
274
275         if (oidClass != null) return;
276
277         String JavaDoc keyClassName = pcElement.getKeyClass();
278         String JavaDoc suffix = keyClassName.substring(keyClassName.length() - 4);
279
280         // First check whether the key class name ends with ".oid".
281
// If so, we need to convert it '.' to '$' because is it
282
// an inner class.
283
if (suffix.compareToIgnoreCase(".oid") == 0) { // NOI18N
284
StringBuffer JavaDoc buf = new StringBuffer JavaDoc(keyClassName);
285
286             buf.setCharAt(buf.length() - 4, '$');
287             keyClassName = buf.toString();
288         }
289
290         try {
291             oidClass = Class.forName(keyClassName, true, classLoader);
292         } catch (Throwable JavaDoc e) {
293             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
294                     "core.configuration.cantloadclass", keyClassName)); // NOI18N
295
}
296
297         if (logger.isLoggable()) {
298             logger.fine("sqlstore.model.classdesc.loadedclass", oidClass); // NOI18N
299
}
300     }
301
302     /**
303      * This method maps all the visible fields. It has the side-effect of computing
304      * the value for maxVisibleFields.
305      */

306     private void initializeFields() {
307         ArrayList concurrencyGroups = new ArrayList();
308         persistentFields = pcElement.getFields();
309
310         for (int i = 0; i < persistentFields.length; i++) {
311             PersistenceFieldElement pcf = persistentFields[i];
312             MappingFieldElementImpl mdf = (MappingFieldElementImpl) mdConfig.getField(pcf.getName());
313
314             if (mdf == null) {
315                 throw new JDOFatalUserException(I18NHelper.getMessage(messages,
316                         "core.configuration.fieldnotmapped", // NOI18N
317
pcf.getName(), pcElement.getName()));
318             }
319
320             FieldDesc f;
321
322             if (!(mdf instanceof MappingRelationshipElement)) {
323                 f = createLocalField(mdf);
324             } else {
325                 f = createForeignField((RelationshipElement) pcf, (MappingRelationshipElementImpl) mdf);
326             }
327
328             initializeFetchAndConcurrencyGroup(f, pcf, mdf, concurrencyGroups);
329
330             if (mdf.isReadOnly()) {
331                 f.sqlProperties |= FieldDesc.PROP_READ_ONLY;
332             }
333
334             try {
335                 f.setupDesc(pcClass, pcf.getName());
336             } catch (JDOException e) {
337                 throw e;
338             } catch (Exception JavaDoc e) {
339                 throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
340                 "core.configuration.loadfailed.field", // NOI18N
341
pcf.getName(), pcElement.getName()), e);
342             }
343
344             f.absoluteID = pcf.getFieldNumber();
345
346             addField(f);
347
348             if (logger.isLoggable(Logger.FINEST)) {
349                 Object JavaDoc[] items = new Object JavaDoc[] {f.getName(),new Integer JavaDoc(f.absoluteID)};
350                 logger.finest("sqlstore.model.classdesc.fieldinfo", items); // NOI18N
351
}
352         }
353
354         this.maxVisibleFields = fields.size();
355     }
356
357     /**
358      * Creates an instance of <code>LocalFieldDesc</code>.
359
360      * @param mdf Input <code>MappingFieldElementImpl</code>
361      */

362     private LocalFieldDesc createLocalField(MappingFieldElementImpl mdf) {
363         ArrayList columnDesc = mdf.getColumnObjects();
364
365         // Make sure this field is properly mapped.
366
if ((columnDesc == null) || (columnDesc.size() == 0)) {
367             throw new JDOFatalUserException(I18NHelper.getMessage(messages,
368                     "core.configuration.fieldnotmapped", // NOI18N
369
mdf.getName(), pcElement.getName()));
370         }
371         return new LocalFieldDesc(this, columnDesc);
372     }
373
374     /**
375      * Creates an instance of <code>LocalFieldDesc</code> for <code>column</code>
376      * that corresponds to a hidden field. Adds the newly created field to
377      * <code>hiddenFields</code> and <code>declaredHiddenFields</code>.
378      *
379      * @param column The input column.
380      * @return New instance of <code>LocalFieldDesc</code>
381      */

382     private LocalFieldDesc createLocalHiddenField(ColumnElement column) {
383         ArrayList columnDesc = new ArrayList();
384         columnDesc.add(column);
385         LocalFieldDesc lf = new LocalFieldDesc(this, columnDesc);
386
387         if (hiddenFields == null) {
388             hiddenFields = new ArrayList();
389         }
390         hiddenFields.add(lf);
391
392         // AbsouluteID for hidden fields must be < 0.
393
lf.absoluteID = -hiddenFields.size();
394
395         if (logger.isLoggable(Logger.FINEST)) {
396             Object JavaDoc[] items = new Object JavaDoc[] {pcClass,lf.getName(),column.getName().getFullName()};
397             logger.finest("sqlstore.model.classdesc.getlocalfielddesc", items); // NOI18N
398
}
399
400         return lf;
401     }
402
403     private FieldDesc createForeignField(RelationshipElement fpcf,
404                                          MappingRelationshipElementImpl fmdf) {
405
406         ForeignFieldDesc ff = new ForeignFieldDesc(this);
407
408         addForeignField(ff);
409
410         ff.cardinalityLWB = fpcf.getLowerBound();
411         ff.cardinalityUPB = fpcf.getUpperBound();
412         ff.deleteAction = fpcf.getDeleteAction();
413
414         initializeColumnLists(ff, fmdf);
415
416         // This is a workaround to make sure that that cardinalityUPB is not <= 1
417
// if the field is of type collection.
418
if (Model.RUNTIME.isCollection(fpcf.getCollectionClass()) &&
419                 (ff.cardinalityUPB <= 1)) {
420             ff.cardinalityUPB = Integer.MAX_VALUE;
421         }
422
423         if (ff.cardinalityUPB > 1) {
424             try {
425                 ff.setComponentType(Class.forName(fpcf.getElementClass(), true, classLoader));
426             } catch (Throwable JavaDoc e) {
427               logger.log(Logger.WARNING, "sqlstore.exception.log", e);
428             }
429         }
430
431         return ff;
432     }
433
434     private void initializeColumnLists(ForeignFieldDesc ff, MappingRelationshipElementImpl fmdf) {
435         ArrayList assocPairs = fmdf.getAssociatedColumnObjects();
436         ArrayList pairs = fmdf.getColumnObjects();
437         ArrayList localColumns = new ArrayList();
438         ArrayList foreignColumns = new ArrayList();
439
440         // We need to go through each local column and extract the foreign column.
441
if ((assocPairs == null) || (assocPairs.size() == 0)) {
442             for (int i = 0; i < pairs.size(); i++) {
443                 ColumnPairElement fce = (ColumnPairElement) pairs.get(i);
444                 localColumns.add(fce.getLocalColumn());
445                 foreignColumns.add(fce.getReferencedColumn());
446             }
447
448             ff.localColumns = localColumns;
449             ff.foreignColumns = foreignColumns;
450         } else {
451             ArrayList assocLocalColumns = new ArrayList();
452             ArrayList assocForeignColumns = new ArrayList();
453
454             for (int i = 0; i < pairs.size(); i++) {
455                 ColumnPairElement alc = (ColumnPairElement) pairs.get(i);
456                 localColumns.add(alc.getLocalColumn());
457                 assocLocalColumns.add(alc.getReferencedColumn());
458             }
459
460             for (int i = 0; i < assocPairs.size(); i++) {
461                 ColumnPairElement afc = (ColumnPairElement) assocPairs.get(i);
462                 assocForeignColumns.add(afc.getLocalColumn());
463                 foreignColumns.add(afc.getReferencedColumn());
464             }
465
466             ff.localColumns = localColumns;
467             ff.assocLocalColumns = assocLocalColumns;
468             ff.assocForeignColumns = assocForeignColumns;
469             ff.foreignColumns = foreignColumns;
470         }
471     }
472
473     private void initializeFetchAndConcurrencyGroup(FieldDesc f,
474                                                     PersistenceFieldElement pcf,
475                                                     MappingFieldElementImpl mdf,
476                                                     ArrayList concurrencyGroups) {
477         f.fetchGroup = mdf.getFetchGroup();
478
479         // RESOLVE: For now, we can only handle one concurrency group per field
480
// MBO:
481
// I can call method getConcurrencyGroups w/o exception using the latest mapping files.
482
// Please note the mapping files do not include any concurrency group info,
483
// thus getConcurrencyGroups returns an empty array.
484
ConcurrencyGroupElement cgroups[] = pcf.getConcurrencyGroups();
485         //ConcurrencyGroupElement cgroups[] = null;
486

487         if ((cgroups == null) || (cgroups.length == 0)) {
488             if (f.fetchGroup == FieldDesc.GROUP_DEFAULT) {
489                 f.concurrencyGroup = f.fetchGroup;
490             }
491         } else {
492             ConcurrencyGroupElement cge = cgroups[0];
493             int index = 0;
494
495             if ((index = concurrencyGroups.indexOf(cge)) == -1) {
496                 index = concurrencyGroups.size();
497                 concurrencyGroups.add(cge);
498             }
499
500             f.concurrencyGroup = index;
501         }
502     }
503
504     private void createSecondaryTableKey(TableDesc table, MappingReferenceKeyElementImpl mappingSecondaryKey) {
505
506         ColumnPairElement pairs[] = mappingSecondaryKey.getColumnPairs();
507         KeyDesc referencingKey = new KeyDesc();
508         KeyDesc referencedKey = new KeyDesc();
509         TableDesc secondaryTable = findTableDesc(((MappingTableElementImpl) mappingSecondaryKey.getTable()).getTableObject());
510
511         for (int i = 0; i < pairs.length; i++) {
512             ColumnPairElement pair = pairs[i];
513
514             ColumnElement lc = pair.getLocalColumn();
515             ColumnElement fc = pair.getReferencedColumn();
516
517             referencingKey.addColumn(lc);
518
519             FieldDesc lf = getLocalFieldDesc(lc);
520             referencingKey.addField(lf);
521
522             // We need to force field for the referencing key to be in the DFG
523
// so it will always be loaded. This is to facilitate updating
524
// secondary tables that requires this field to be loaded
525
// for constraint purposes.
526
lf.fetchGroup = FieldDesc.GROUP_DEFAULT;
527
528             referencedKey.addColumn(fc);
529             referencedKey.addField(getLocalFieldDesc(fc));
530         }
531
532         table.addSecondaryTableKey(new ReferenceKeyDesc(secondaryTable, referencingKey, referencedKey));
533         secondaryTable.setPrimaryTableKey(new ReferenceKeyDesc(table, referencedKey, referencingKey));
534     }
535
536     /**
537      * This method maps all the tables.
538      */

539     private void initializeTables() {
540         ArrayList mdTables = mdConfig.getTables();
541
542         createTables(mdTables);
543         processSecondaryTables(mdTables);
544     }
545
546     private void createTables(ArrayList mdTables) {
547         for (int i = 0; i < mdTables.size(); i++) {
548             MappingTableElementImpl mdt = (MappingTableElementImpl) mdTables.get(i);
549             TableDesc t = new TableDesc(mdt.getTableObject());
550
551             ArrayList keys = mdt.getKeyObjects();
552             KeyDesc key = new KeyDesc();
553             t.setKey(key);
554             key.addColumns(keys);
555
556             for (int j = 0; j < keys.size(); j++) {
557                 ColumnElement c = (ColumnElement) keys.get(j);
558
559                 if (c != null) {
560                     key.addField(getLocalFieldDesc(c));
561                 }
562             }
563
564             addTableDesc(t);
565         }
566     }
567
568     /**
569      * Validity checks on secondary tables:
570      * 1) Ensure that every secondary table has a TableDesc
571      * 2) Every referencing key is the same length as the table's key
572      * Build the referencing keys.
573      * NOTE: This method assumes that the entries of <code>mdTables</code>
574      * and <code>tables</code> are sorted in the same order.
575      */

576     private void processSecondaryTables(ArrayList mdTables) {
577
578         for (int i = 0; i < tables.size(); i++) {
579             MappingTableElementImpl mdt = (MappingTableElementImpl) mdTables.get(i);
580             TableDesc t = (TableDesc) tables.get(i);
581             ArrayList secondaryKeys = mdt.getReferencingKeys();
582
583             for (int j = 0; j < secondaryKeys.size(); j++) {
584                 MappingReferenceKeyElementImpl mappingSecondaryKey = (MappingReferenceKeyElementImpl) secondaryKeys.get(j);
585                 createSecondaryTableKey(t, mappingSecondaryKey);
586             }
587         }
588     }
589
590     private void initializeJoinTables() {
591         Iterator iter = foreignFields.iterator();
592
593         while (iter.hasNext()) {
594             ForeignFieldDesc ff = (ForeignFieldDesc) iter.next();
595
596             if (ff.useJoinTable()) {
597                 TableElement joinTable = ((ColumnElement) ff.assocLocalColumns.get(0)).getDeclaringTable();
598                 TableDesc joinTableDesc = findTableDesc(joinTable);
599
600                 if (joinTableDesc == null) {
601                     joinTableDesc = new TableDesc(joinTable);
602
603                     // Mark this table as a join table
604
joinTableDesc.setJoinTable(true);
605                     addTableDesc(joinTableDesc);
606                 }
607               }
608         }
609     }
610
611     /**
612      * Returns a list of fields in fetchGroup <code>groupID</code>.
613      *
614      * @param groupID Fetch group id.
615      * @return List of fields in fetchGroup <code>groupID</code>. The list
616      * for <code>FieldDesc.GROUP_NONE</code> is empty.
617      * @see #initializeFetchGroups
618      */

619     public ArrayList getFetchGroup(int groupID) {
620         int index = 0;
621
622         if (groupID >= FieldDesc.GROUP_NONE) {
623             index = groupID;
624         } else if (groupID < FieldDesc.GROUP_NONE) {
625             index = -groupID + maxHierarchicalGroupID;
626         }
627
628         for (int i = fetchGroups.size(); i <= index; i++) {
629             fetchGroups.add(null);
630         }
631
632         ArrayList group = (ArrayList) fetchGroups.get(index);
633
634         if (group == null) {
635             group = new ArrayList();
636             fetchGroups.set(index, group);
637         }
638
639         return group;
640     }
641
642     private void addField(FieldDesc f) {
643         fields.add(f);
644     }
645
646     private void addForeignField(ForeignFieldDesc f) {
647         foreignFields.add(f);
648     }
649
650     /**
651      * Compute the fetch group lists for the declared fields.
652      * A fetch group lists the fields sharing the same value for
653      * <code>FieldDesc.fetchGroup</code>.
654      * Note: Fields that aren't in any fetch group must not be added
655      * to <code>fetchGroups[FieldDesc.GROUP_NONE]</code>.
656      *
657      * @see #getFetchGroup
658      */

659     private void initializeFetchGroups() {
660
661         for (int i = 0; i < 2; i++) {
662             ArrayList theFields = null;
663
664             if (i == 0) {
665                 theFields = fields;
666             } else {
667                 // It is possible to have hidden fields in the DFG.
668
if ((theFields = hiddenFields) == null)
669                     continue;
670             }
671
672             for (int j = 0; j < theFields.size(); j++) {
673                 FieldDesc f = (FieldDesc) theFields.get(j);
674
675                 // Do not add the field to the fetch group for GROUP_NONE.
676
if (f.fetchGroup > FieldDesc.GROUP_NONE) {
677                     getFetchGroup(f.fetchGroup).add(f);
678                 }
679
680                 // Check declared visible fields only.
681
if (i == 0 && !f.isRelationshipField()
682                         && f.fetchGroup != FieldDesc.GROUP_DEFAULT) {
683                     hasLocalNonDFGField = true;
684                 }
685             }
686         }
687
688         this.maxHierarchicalGroupID = fetchGroups.size() - 1;
689
690         for (int i = 0; i < fields.size(); i++) {
691             FieldDesc f = (FieldDesc) fields.get(i);
692
693             // Do not add the field to the fetch group for GROUP_NONE.
694
if (f.fetchGroup < FieldDesc.GROUP_NONE) {
695                 getFetchGroup(f.fetchGroup).add(f);
696             }
697         }
698     }
699
700     private void initializeConcurrency() {
701         optimisticConcurrency = new ConcurrencyOptVerify();
702         optimisticConcurrency.configPersistence(this);
703         checkDirtyConcurrency = new ConcurrencyCheckDirty();
704         checkDirtyConcurrency.configPersistence(this);
705         databaseConcurrency = new ConcurrencyDBNative();
706         databaseConcurrency.configPersistence(this);
707         explicitConcurrency = new ConcurrencyDBExplicit();
708         explicitConcurrency.configPersistence(this);
709     }
710
711     private void initializeKeyFields() {
712         boolean debug = logger.isLoggable(Logger.FINEST);
713         if (oidClass == null)
714             return;
715
716         keyFields = oidClass.getFields();
717         keyFieldNames = new String JavaDoc[keyFields.length];
718         keyFieldDescs = new LocalFieldDesc[keyFields.length];
719
720         if (debug) {
721             logger.finest("sqlstore.model.classdesc.createsqldesc", oidClass); // NOI18N
722
}
723
724         for (int i = 0; i < keyFields.length; i++) {
725             Field JavaDoc kf = keyFields[i];
726             String JavaDoc name = kf.getName();
727             keyFieldNames[i] = name;
728
729             if (name.equals("serialVersionUID")) { // NOI18N
730
continue;
731             }
732
733             LocalFieldDesc f = getLocalFieldDesc(name);
734
735             if (f != null) {
736                 if (debug) {
737                     logger.finest("sqlstore.model.classdesc.pkfield", f.getName()); // NOI18N
738
}
739
740                 // The fetch group for pk fields should always be DFG.
741
f.fetchGroup = FieldDesc.GROUP_DEFAULT;
742                 f.sqlProperties &= ~(FieldDesc.PROP_REF_INTEGRITY_UPDATES);
743                 f.sqlProperties &= ~(FieldDesc.PROP_IN_CONCURRENCY_CHECK);
744                 f.sqlProperties |= FieldDesc.PROP_PRIMARY_KEY_FIELD;
745
746                 keyFieldDescs[i] = f;
747             } else {
748                 throw new JDOFatalUserException(I18NHelper.getMessage(messages,
749                         "core.configuration.noneexistentpkfield", // NOI18N
750
name, oidClass.getName(), pcClass.getName()));
751             }
752         }
753     }
754
755     /**
756      * Initialize the list of field descriptors of version consistency fields.
757      * The names of the version fields are obtained from
758      * {@link MappingClassElement#getVersionFields}.
759      */

760     private void initializeVersionFields() {
761         int size = mdConfig.getVersionFields().size();
762         Iterator versionFieldIterator = mdConfig.getVersionFields().iterator();
763         versionFields = new LocalFieldDesc[size];
764
765         for (int i = 0; i < size; i++) {
766             MappingFieldElement mdField = (MappingFieldElement) versionFieldIterator.next();
767             LocalFieldDesc f = (LocalFieldDesc) getField(mdField.getName());
768
769             if (f != null) {
770                 if (logger.isLoggable()) {
771                     logger.finest("sqlstore.model.classdesc.vcfield", f.getName()); // NOI18N
772
}
773
774                 versionFields[i] = f;
775                 registerVersionFieldWithTable(f);
776
777                 // The fetch group for version fields should always be DFG.
778
f.fetchGroup = FieldDesc.GROUP_DEFAULT;
779                 f.sqlProperties &= ~(FieldDesc.PROP_REF_INTEGRITY_UPDATES);
780                 f.sqlProperties |= FieldDesc.PROP_VERSION_FIELD;
781             } else {
782                 throw new JDOFatalUserException(I18NHelper.getMessage(messages,
783                         "core.configuration.noneexistentvcfield", // NOI18N
784
mdField.getName(), pcClass.getName()));
785             }
786         }
787     }
788
789     /**
790      * Registers the version field <cod>versionField</code> with the
791      * corresponding table.
792      *
793      * @param versionField Field used in version consistency check.
794      */

795     private void registerVersionFieldWithTable(LocalFieldDesc versionField) {
796         // Version field must be mapped to exactly one column.
797
ColumnElement ce = (ColumnElement) versionField.getColumnElements().next();
798         Iterator iter = tables.iterator();
799
800         while (iter.hasNext()) {
801             TableDesc table = (TableDesc) iter.next();
802
803             if (!table.isJoinTable()) {
804                 if (ce.getDeclaringTable() == table.getTableElement()) {
805                     table.setVersionField(versionField);
806                     break;
807                 }
808             }
809         }
810     }
811
812     /**
813      * Computes all the primitive tracked fields.
814      * Primitive fields track each other if they are mapped to same columns.
815      * One of them is made the primary tracked field as per precedence rules
816      * on the field types. This field is used to bind values to columns while
817      * updating the database.
818      */

819     private void computeTrackedPrimitiveFields() {
820         // Compute the list of primitive fields to track for each primitive field.
821
for (int i = 0; i < fields.size(); i++) {
822             FieldDesc f = (FieldDesc) fields.get(i);
823
824             if (!f.isRelationshipField()) {
825                 LocalFieldDesc lf = (LocalFieldDesc) f;
826
827                 lf.computeTrackedPrimitiveFields();
828                 lf.computePrimaryTrackedPrimitiveField();
829             }
830         }
831     }
832
833     /**
834      * Computes the tracked relationship fields. Relationships are tracked in
835      * the following way:
836      * <ul>
837      * <li>A relationship field tracks a local field, if it's mappped
838      * to the same columns. The relationship will be updated when the
839      * local field is set.</li>
840      * <li>Relationship fields track each other if they are mapped to
841      * the same columns. The first field in the field order is marked
842      * as primary tracked field. If the foreign key columns are
843      * explicitly mapped, this field also tracks the local foreign key
844      * field. The foreign key columns are explicitly mapped in
845      * overlapping pk/fk situations. The other relationship fields
846      * mapped to the same columns are updated when the primary tracked
847      * relationship field is set.</li>
848      * </ul>
849      * @see FieldDesc#PROP_PRIMARY_TRACKED_FIELD
850      * @see FieldDesc#PROP_SECONDARY_TRACKED_FIELD
851      */

852     private void computeTrackedRelationshipFields() {
853         // Compute the list of fields to track for each field.
854
for (int i = 0; i < 2; i++) {
855             ArrayList theFields = null;
856
857             // We first check all the visible fields and then the hidden fields.
858
if (i == 0) {
859                 theFields = this.fields;
860             } else {
861                 if ((theFields = this.hiddenFields) == null) {
862                     continue;
863                 }
864             }
865
866             for (int j = 0; j < theFields.size(); j++) {
867                 FieldDesc f = (FieldDesc) theFields.get(j);
868
869                 f.computeTrackedRelationshipFields();
870             }
871         }
872     }
873
874     /**
875      * Remove redundant ForeignFieldDescs from each LocalFieldDesc's tracked field list.
876      */

877     private void cleanupTrackedFields() {
878
879         for (int i = 0; i < fields.size(); i++) {
880             FieldDesc f = (FieldDesc) fields.get(i);
881
882             if (f instanceof LocalFieldDesc) {
883                 ((LocalFieldDesc) f).cleanupTrackedFields();
884             }
885         }
886     }
887
888     /**
889      * The fixupForeignReferences method finds all the references to foreign
890      * classes in this configuration. It then builds the appropriate run-time
891      * information (ForeignFieldDesc) for the foreign reference from the
892      * meta-data.
893      */

894     private void fixupForeignReferences(ConfigCache cache) {
895
896         for (int i = 0; i < foreignFields.size(); i++) {
897             ForeignFieldDesc ff = (ForeignFieldDesc) foreignFields.get(i);
898             Class JavaDoc classType = null;
899
900             if ((classType = ff.getComponentType()) == null) {
901                 classType = ff.getType();
902             }
903
904             ClassDesc foreignConfig = (ClassDesc) cache.getPersistenceConfig(classType);
905
906             if (foreignConfig == null) continue;
907
908             // Look up the inverse relationship field name if there is any.
909
String JavaDoc irName = pcElement.getRelationship(ff.getName()).getInverseRelationshipName();
910             ForeignFieldDesc inverseField = null;
911
912             if (irName != null) {
913                 inverseField = (ForeignFieldDesc) foreignConfig.getField(irName);
914             }
915
916             ff.fixupForeignReference(foreignConfig, inverseField);
917         }
918     }
919
920     private void fixupFieldProperties() {
921
922         for (int i = 0; i < foreignFields.size(); i++) {
923             ForeignFieldDesc ff = (ForeignFieldDesc) foreignFields.get(i);
924             ff.fixupFieldProperties();
925         }
926     }
927
928     LocalFieldDesc getLocalFieldDesc(ColumnElement column) {
929         LocalFieldDesc result;
930
931         for (int i = 0; i < 2; i++) {
932             ArrayList theFields = null;
933
934             if (i == 0) {
935                 theFields = fields;
936             } else {
937                 theFields = hiddenFields;
938             }
939
940             if (theFields != null) {
941                 for (int j = 0; j < theFields.size(); j++) {
942                     FieldDesc f = (FieldDesc) theFields.get(j);
943
944                     if (f instanceof LocalFieldDesc) {
945                         result = (LocalFieldDesc) f;
946
947                         for (int k = 0; k < result.columnDescs.size(); k++) {
948                             ColumnElement c = (ColumnElement) result.columnDescs.get(k);
949
950                             // if (c.equals(column))
951
if (c.getName().getFullName().compareTo(column.getName().getFullName()) == 0) {
952                                 // If f is a tracked field and it is not the primary, we continue
953
// searching.
954
if ((f.getTrackedFields() != null) &&
955                                         ((f.sqlProperties & FieldDesc.PROP_PRIMARY_TRACKED_FIELD) == 0)) {
956                                     continue;
957                                 }
958
959                                 return result;
960                             }
961                         }
962                     }
963                 }
964             }
965         }
966
967         // If we didn't find the field associated with the column, we need to
968
// create a hidden field and add it to the hiddenFields list.
969
result = createLocalHiddenField(column);
970
971         return result;
972     }
973
974     /**
975      * Returns the local field descriptor for the field <code>name</code>.
976      *
977      * @param name Field name.
978      * @return Local field descriptor for the field <code>name</code>.
979      * @throws JDOFatalInternalException if the field is not defined for this class.
980      */

981     public LocalFieldDesc getLocalFieldDesc(String JavaDoc name) {
982         FieldDesc desc = getField(name);
983
984         if (desc == null) {
985             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
986                         "core.generic.unknownfield", // NOI18N
987
name, getName()));
988         }
989
990         if (!(desc instanceof LocalFieldDesc)) {
991             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
992                         "core.generic.notinstanceof", // NOI18N
993
desc.getClass().getName(), "LocalFieldDesc")); // NOI18N
994
}
995
996         return ((LocalFieldDesc) desc);
997     }
998
999     public TableDesc findTableDesc(TableElement mdTable) {
1000        for (int i = 0; i < tables.size(); i++) {
1001            TableDesc t = (TableDesc) tables.get(i);
1002
1003            if (t.getTableElement().equals(mdTable)) {
1004                return t;
1005            }
1006        }
1007
1008        return null;
1009    }
1010
1011    private void addTableDesc(TableDesc t) {
1012        // setConsistencyLevel of this table before adding it to our list
1013
if (!t.isJoinTable()) {
1014            // JoinTables represent relationships instead of "real" objects,
1015
// they should never have a special consistencyLevel.
1016
t.setConsistencyLevel(mdConfig.getConsistencyLevel());
1017        }
1018        tables.add(t);
1019    }
1020
1021    public int getTableIndex(TableDesc tableDesc) {
1022        return tables.indexOf(tableDesc);
1023    }
1024
1025    public FieldDesc getField(String JavaDoc name) {
1026        for (int i = 0; i < fields.size(); i++) {
1027            FieldDesc f = (FieldDesc) fields.get(i);
1028
1029            if ((f != null) && (f.getName().compareTo(name) == 0)) {
1030                return f;
1031            }
1032        }
1033
1034        if (hiddenFields != null) {
1035            for (int i = 0; i < hiddenFields.size(); i++) {
1036                FieldDesc f = (FieldDesc) hiddenFields.get(i);
1037
1038                if (f.getName().compareTo(name) == 0) {
1039                    return f;
1040                }
1041            }
1042        }
1043
1044        return null;
1045    }
1046
1047    public FieldDesc getField(int index) {
1048        if (index >= 0) {
1049            return (FieldDesc) fields.get(index);
1050        } else {
1051            return (FieldDesc) hiddenFields.get(-(index + 1));
1052        }
1053    }
1054
1055    public Class JavaDoc getPersistenceCapableClass() {
1056        return pcClass;
1057    }
1058
1059    public Class JavaDoc getOidClass() {
1060        return oidClass;
1061    }
1062
1063    public String JavaDoc getName() {
1064        return pcClass.getName();
1065    }
1066
1067    public Iterator getTables() {
1068        return tables.iterator();
1069    }
1070
1071    public TableDesc getPrimaryTable() {
1072        return (TableDesc) tables.get(0);
1073    }
1074
1075    public boolean isNavigable() {
1076        return mdConfig.isNavigable();
1077    }
1078
1079    public boolean hasVersionConsistency() {
1080        return mdConfig.getConsistencyLevel() == MappingClassElement.VERSION_CONSISTENCY;
1081    }
1082
1083    public LocalFieldDesc[] getVersionFields() {
1084        return versionFields;
1085    }
1086
1087    public Concurrency getConcurrency(boolean optimistic) {
1088        // Following algo is used to determine which concurrency to return:
1089
// consistency level specified in model(represented by the variable consistencyLevel),
1090
// takes precedence over
1091
// concurrency specified by transaction (represented by parameter 'optimistic' to this method)
1092

1093        Concurrency concurrency = null;
1094        int consistencyLevel = mdConfig.getConsistencyLevel();
1095
1096        if (consistencyLevel == MappingClassElement.NONE_CONSISTENCY) {
1097            // No consistency level specified in model
1098
if (optimistic) {
1099                concurrency = (Concurrency) optimisticConcurrency.clone();
1100            } else {
1101                concurrency = (Concurrency) databaseConcurrency.clone();
1102            }
1103
1104        } else {
1105            // currently, we would not try to interprete consistencyLevel as bitmap
1106
// When we implment consistencyLevel like 2+, 3+, we would have to interprete
1107
// consistencyLevel as bitmap and implement some changes in class hierarchy
1108
// starting with interface Concurrency
1109
switch(consistencyLevel) {
1110
1111                // We would never reach this code because we are in the else part
1112
// case MappingClassElement.NONE_CONSISTENCY :
1113
// concurrency = (Concurrency) databaseConcurrency.clone();
1114
// break;
1115

1116            case MappingClassElement.CHECK_MODIFIED_AT_COMMIT_CONSISTENCY :
1117                concurrency = (Concurrency) checkDirtyConcurrency.clone();
1118                break;
1119
1120            case MappingClassElement.LOCK_WHEN_LOADED_CONSISTENCY :
1121                // This consistency level is implemeted inside SelectStatement and
1122
// not by any object implementing Concurrency
1123
concurrency = (Concurrency) explicitConcurrency.clone();
1124                break;
1125
1126            case MappingClassElement.VERSION_CONSISTENCY :
1127                // This consistency level is implemented inside ClassDesc and
1128
// UpdateQueryPlan and not by any object implementing Concurrency.
1129
// Selects are handled in ClassDesc, updates/deletes in
1130
// UpdateQueryPlan.
1131
concurrency = (Concurrency) databaseConcurrency.clone();
1132                break;
1133
1134            // **Note**
1135
// Plese change text for the exception thrown in default below when
1136
// we start supporting new consistency levels. The text lists currently supported
1137
// consistency level
1138
default :
1139                throw new JDOUnsupportedOptionException(I18NHelper.getMessage(messages,
1140                    "core.configuration.unsupportedconsistencylevel", pcClass));// NOI18N
1141
}
1142        }
1143
1144        return concurrency;
1145    }
1146
1147    /**
1148     * Determines whether this classDesc has the
1149     * CHECK_MODIFIED_AT_COMMIT_CONSISTENCY level.
1150     * @return <code>true</code> if this has the
1151     * CHECK_MODIFIED_AT_COMMIT_CONSISTENCY level;
1152     * <code>false</code> otherwise.
1153     */

1154    public boolean hasModifiedCheckAtCommitConsistency() {
1155        return(mdConfig.getConsistencyLevel() ==
1156                MappingClassElement.CHECK_MODIFIED_AT_COMMIT_CONSISTENCY);
1157    }
1158
1159    public boolean isPKField(int index) {
1160        return persistentFields[index].isKey();
1161    }
1162
1163    /**
1164     * Return the key fields as array of java.lang.reflect.Field instances.
1165     * @return The key fields as array of java.lang.reflect.Field instances.
1166     */

1167    public Field JavaDoc[] getKeyFields() {
1168        return keyFields;
1169    }
1170
1171    /**
1172     * Returns the key field names as array of String.
1173     * @return The key field names as array of String.
1174     */

1175    public String JavaDoc[] getKeyFieldNames() {
1176        return keyFieldNames;
1177    }
1178
1179    /**
1180     * Returns the descriptors for key fields as array of LocalFieldDesc.
1181     * @return The descriptors for key fields as array of LocalFieldDesc.
1182     */

1183    public LocalFieldDesc[] getKeyFieldDescs() {
1184        return keyFieldDescs;
1185    }
1186
1187    /**
1188     * Returns a new <code>SQLStateManager</code> instance,
1189     * initialized with passed store manager and this instance of
1190     * the runtime class model.
1191     *
1192     * @param store Store manager, an instance of SQLStoreManager.
1193     * @return A new <code>SQLStateManager</code> instance.
1194     */

1195    public SQLStateManager newStateManagerInstance(PersistenceStore store) {
1196        return new SQLStateManager(store, this);
1197    }
1198
1199    /**
1200     * Returns a RetrieveDescriptor which represent a SQL query selecting a pc
1201     * instance by pk-fields. Please note that the RDs are cached, so the method
1202     * first checks the cache. If there is no corresponding RetrieveDescriptor
1203     * in the cache, it creates a new one and stores it in the cache. If the
1204     * additionalField is not null, the method retrieves the field indicated by
1205     * it along with the query. Fetch group fields will be added when the query
1206     * plan is build.
1207     *
1208     * Note, the reason to introduce the RetrieveDesc cache in ClassDesc and not
1209     * in the store manager is, that we can have the cache per class, where
1210     * the store manager could only provide one big cache for all pc classes.
1211     *
1212     * @param additionalField The field to be retrieved in addition to the
1213     * DFG fields.
1214     * @param store The store manager.
1215     * @return A RetrieveDescriptor selecting a pc instance by pk-fields.
1216     * @see #getRetrieveDescForFKQuery
1217     */

1218    public RetrieveDesc getRetrieveDescForPKQuery(FieldDesc additionalField, PersistenceStore store) {
1219        RetrieveDescImpl rd = null;
1220        String JavaDoc cacheKey = generateRDCacheKey(additionalField);
1221
1222        synchronized (retrieveDescCache) {
1223            // Cache lookup.
1224
rd = (RetrieveDescImpl) retrieveDescCache.get(cacheKey);
1225            // Generate a new RD if there isn't one be found in the cache.
1226
if (rd == null) {
1227                rd = (RetrieveDescImpl) store.getRetrieveDesc(pcClass);
1228                if (additionalField != null) {
1229                    RetrieveDesc frd = null;
1230                    String JavaDoc name = additionalField.getName();
1231                    // If the additionalField is not null, we will retrieve
1232
// the field indicated by it along with the query.
1233
if (additionalField instanceof ForeignFieldDesc) {
1234                        Class JavaDoc additionalClass = ((ForeignFieldDesc) additionalField).
1235                                foreignConfig.getPersistenceCapableClass();
1236
1237                        frd = store.getRetrieveDesc(additionalClass);
1238                    }
1239
1240                    rd.addPrefetchedField(name, frd);
1241                }
1242
1243                addPKConstraints(rd);
1244                // Cache fillup.
1245
retrieveDescCache.put(cacheKey, rd);
1246            }
1247        }
1248
1249        return rd;
1250    }
1251
1252    /**
1253     * Returns a RetrieveDescriptor which represent a SQL query selecting pc
1254     * instances by the relationship key. The relationship key is taken from
1255     * the foreign field <code>foreignField</code> and used as query constraint.
1256     * Please note that the RDs are cached, so the method first checks the cache.
1257     * If there is no corresponding RetrieveDescriptor in the cache, it creates
1258     * a new one and stores it in the cache. FetchGroup fields will be added
1259     * when the query plan is build, see <code>SelectQueryPlan#processFetchGroups</code>.
1260     *
1261     * Note, the reason to introduce the RetrieveDesc cache in ClassDesc and not
1262     * in the store manager is, that we can have the cache per class, where
1263     * the store manager could only provide one big cache for all pc classes.
1264     *
1265     * @param foreignField The relationship field to be retrieved.
1266     * Following is true for this field.
1267     * <ul>
1268     * <li> It is part of an independent fetch group with only one field in the fetch group.
1269     * <p>Or
1270     * <P>Not part of any fetch group.
1271     * </li>
1272     * <li>It is not mapped to a join table. </li>
1273     * </ul>
1274     * @param store The store manager.
1275     * @return A RetrieveDescriptor selecting pc instance(s) corresponding to
1276     * the foreign field
1277     * @see #getRetrieveDescForPKQuery
1278     */

1279    public RetrieveDesc getRetrieveDescForFKQuery(ForeignFieldDesc foreignField, PersistenceStore store) {
1280        RetrieveDescImpl rd = null;
1281        String JavaDoc cacheKey = generateRDCacheKey(foreignField);
1282
1283        synchronized (foreignRetrieveDescCache) {
1284            // Cache lookup.
1285
rd = (RetrieveDescImpl) foreignRetrieveDescCache.get(cacheKey);
1286            // Generate a new RD if there isn't one be found in the cache.
1287
if (rd == null) {
1288                rd = (RetrieveDescImpl) store.getRetrieveDesc(foreignField.foreignConfig.getPersistenceCapableClass());
1289
1290                addFKConstraints(rd, foreignField);
1291                // Cache fillup.
1292
foreignRetrieveDescCache.put(cacheKey, rd);
1293            }
1294        }
1295
1296        return rd;
1297    }
1298
1299    /**
1300     * Gets RetrieveDescriptor(rd) for verifying a VC instance. The returned rd
1301     * is set up to expect constraints for pk followed by constraints for version
1302     * fields.
1303     * @param store
1304     * @return Instance of retrieve Descriptor for verifying a VC instance.
1305     */

1306    public RetrieveDesc getRetrieveDescForVerificationQuery(PersistenceStore store) {
1307
1308        assert hasVersionConsistency();
1309
1310        synchronized(retrieveDescForVerificationSynchObj) {
1311            if (retrieveDescForVerification == null) {
1312                RetrieveDescImpl rd = (RetrieveDescImpl) store.getRetrieveDesc(pcClass);
1313
1314                int index = addPKConstraints(rd);
1315                rd.addParameterConstraints(versionFields, index);
1316                rd.setOption(RetrieveDescImpl.OPT_VERIFY);
1317
1318                retrieveDescForVerification = rd;
1319            }
1320        }
1321        return retrieveDescForVerification;
1322    }
1323
1324    /**
1325     * Generate for each keyField 'keyfield = ?' in the where clause
1326     * with InputParamValues for later binding of the actual query parameters.
1327     */

1328    private int addPKConstraints(RetrieveDescImpl rd) {
1329        // PK Constraints are always added first hence startIndex == 0
1330
rd.addParameterConstraints(keyFieldDescs, 0);
1331
1332        return keyFieldDescs.length;
1333    }
1334
1335    /**
1336     * Generate the condition on the relationship key in the where clause
1337     * with InputParamValues for later binding of the actual query parameters.
1338     */

1339    private void addFKConstraints(RetrieveDescImpl rd, ForeignFieldDesc foreignField) {
1340        for (int i = 0; i < foreignField.foreignFields.size(); i++) {
1341            LocalFieldDesc fff = (LocalFieldDesc) foreignField.foreignFields.get(i);
1342            rd.addParameterConstraint(fff, i);
1343        }
1344    }
1345
1346    /**
1347     * Generates the key for a RetrieveDescriptor for the cache lookup.
1348     * The key has one of two forms:
1349     * <li>
1350     * Fully qualified classname of the pcclass, if additionalField is null.
1351     * </li>
1352     * <li>
1353     * Fully qualified classname of the pcclass + '/' + additionalFieldName +
1354     * '/' + additionalFieldID, otherwise.
1355     * </li>
1356     * @param additionalField The field to be retrieved in addition to the
1357     * DFG fields.
1358     * @return The generated cache key as a String.
1359     */

1360    private String JavaDoc generateRDCacheKey(FieldDesc additionalField)
1361    {
1362        StringBuffer JavaDoc key = new StringBuffer JavaDoc();
1363
1364        key.append(pcClass.getName());
1365
1366        if (additionalField != null) {
1367            // using '/' as separator between class and fieldname
1368
key.append('/');
1369            key.append(additionalField.getName());
1370            key.append('/');
1371            key.append(additionalField.absoluteID);
1372        }
1373
1374        return key.toString();
1375    }
1376
1377    /**
1378     * Returns true, if this class has got local fields not in the default
1379     * fetch group. Because UpdateQueryPlans for updates are cached depending
1380     * on the set of updated fields {@link #getUpdateQueryPlanForUpdate}, we
1381     * might need to compare the updated fields btw. two instances when
1382     * batching is enabled.
1383     *
1384     * @return True, if this class has got local fields not in the default
1385     * fetch group.
1386     * @see SQLStateManager#requiresImmediateFlush
1387     */

1388    public boolean hasLocalNonDFGFields() {
1389        // All instances with modified DFG fields can be batched with
1390
// the same statement. If there's a primitive field outside
1391
// the DFG, we need to compare modified fields to check if the
1392
// same statement can be used.
1393
return hasLocalNonDFGField;
1394    }
1395
1396    /**
1397     * Retrieves the update query plan for the specified descriptor.
1398     * @param desc the descriptor
1399     * @param store the store manager
1400     * @return
1401     */

1402    public UpdateQueryPlan getUpdateQueryPlan(
1403            UpdateObjectDescImpl desc, SQLStoreManager store)
1404    {
1405        switch (desc.getUpdateAction()) {
1406            case ActionDesc.LOG_CREATE:
1407                return getUpdateQueryPlanForInsert(desc, store);
1408            case ActionDesc.LOG_DESTROY:
1409                return getUpdateQueryPlanForDelete(desc, store);
1410            case ActionDesc.LOG_UPDATE:
1411                return getUpdateQueryPlanForUpdate(desc, store);
1412            default:
1413                // TBD: error message + I18N
1414
// error unknown action
1415
return null;
1416        }
1417    }
1418
1419    /** */
1420    private UpdateQueryPlan getUpdateQueryPlanForInsert(
1421            UpdateObjectDescImpl desc, SQLStoreManager store)
1422    {
1423        synchronized(updateQueryPlanForInsertSynchObj) {
1424            if (updateQueryPlanForInsert == null) {
1425                updateQueryPlanForInsert = buildQueryPlan(store, desc);
1426            }
1427        }
1428        return updateQueryPlanForInsert;
1429    }
1430
1431    /** */
1432    private UpdateQueryPlan getUpdateQueryPlanForDelete(
1433            UpdateObjectDescImpl desc, SQLStoreManager store)
1434    {
1435        synchronized(updateQueryPlanForDeleteSynchObj) {
1436            if (updateQueryPlanForDelete == null) {
1437                updateQueryPlanForDelete = buildQueryPlan(store, desc);
1438            }
1439        }
1440        return updateQueryPlanForDelete;
1441    }
1442
1443    /** */
1444    private UpdateQueryPlan getUpdateQueryPlanForUpdate(
1445            UpdateObjectDescImpl desc, SQLStoreManager store)
1446    {
1447        String JavaDoc key = getSortedFieldNumbers(desc.getUpdatedFields());
1448        UpdateQueryPlan plan;
1449        synchronized(updateQueryPlanCache) {
1450            plan = (UpdateQueryPlan)updateQueryPlanCache.get(key);
1451            if (plan == null) {
1452                plan = buildQueryPlan(store, desc);
1453                updateQueryPlanCache.put(key, plan);
1454            }
1455        }
1456        return plan;
1457    }
1458
1459    /**
1460     * Builds and initializes a new query plan based on the information
1461     * passed with the <code>desc<//code> parameter. The returned plan
1462     * and its related data structures will be readonly.
1463     *
1464     * @param store Store manager
1465     * @param desc Update information, including the update action and
1466     * modified fields as appropriate.
1467     *
1468     * @return A new query plan. The returned plan will be readonly.
1469     */

1470    private UpdateQueryPlan buildQueryPlan(SQLStoreManager store,
1471                                           UpdateObjectDescImpl desc) {
1472        UpdateQueryPlan plan;
1473
1474        plan = new UpdateQueryPlan(desc, store);
1475        plan.build(true);
1476
1477        // Initialize the text for all statements. After this point,
1478
// the plan and its related data structures will be readonly.
1479
plan.getStatements();
1480
1481        return plan;
1482    }
1483
1484    /**
1485     * The methods returns the string representation of the sorted field numbers
1486     * of the FieldDescs.
1487     * The key is the string representation of the sorted field number list
1488     * of updated fields.
1489     * @param fields the list of FieldDescs
1490     * @return the sorted field number string
1491     */

1492    private String JavaDoc getSortedFieldNumbers(List fields)
1493    {
1494        // Use the array of field numbers of the updated fields as the key
1495
int size = fields.size();
1496        int [] fieldNos = new int[size];
1497        for (int i = 0; i < size; i++) {
1498            FieldDesc f = (FieldDesc)fields.get(i);
1499            fieldNos[i] = f.absoluteID;
1500        }
1501        Arrays.sort(fieldNos);
1502        return StringHelper.intArrayToSeparatedList(fieldNos, ","); //NOI18N
1503
}
1504
1505}
1506
Popular Tags