KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > oracle > toplink > essentials > internal > descriptors > ObjectBuilder


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
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
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 in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21 // Copyright (c) 1998, 2006, Oracle. All rights reserved.
22
package oracle.toplink.essentials.internal.descriptors;
23
24 import java.io.*;
25 import java.util.*;
26 import oracle.toplink.essentials.exceptions.*;
27 import oracle.toplink.essentials.expressions.*;
28 import oracle.toplink.essentials.internal.expressions.*;
29 import oracle.toplink.essentials.internal.helper.*;
30 import oracle.toplink.essentials.internal.identitymaps.*;
31 import oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager;
32 import oracle.toplink.essentials.internal.sessions.*;
33 import oracle.toplink.essentials.logging.SessionLog;
34 import oracle.toplink.essentials.mappings.*;
35 import oracle.toplink.essentials.mappings.foundation.*;
36 import oracle.toplink.essentials.queryframework.*;
37 import oracle.toplink.essentials.querykeys.*;
38 import oracle.toplink.essentials.descriptors.DescriptorEventManager;
39 import oracle.toplink.essentials.sessions.ObjectCopyingPolicy;
40 import oracle.toplink.essentials.sessions.SessionProfiler;
41 import oracle.toplink.essentials.sessions.DatabaseRecord;
42 import oracle.toplink.essentials.internal.sessions.AbstractRecord;
43 import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
44 import oracle.toplink.essentials.internal.sessions.AbstractSession;
45 import oracle.toplink.essentials.descriptors.ClassDescriptor;
46
47 /**
48  * <p><b>Purpose</b>: Object builder is one of the behaviour class attached to descriptor.
49  * It is responsible for building objects, rows, and extracting primary keys from
50  * the object and the rows.
51  *
52  * @author Sati
53  * @since TOPLink/Java 1.0
54  */

55 public class ObjectBuilder implements Cloneable JavaDoc, Serializable {
56     protected ClassDescriptor descriptor;
57     protected Map mappingsByAttribute;
58     protected Map mappingsByField;
59     protected Map readOnlyMappingsByField;
60     protected Vector primaryKeyMappings;
61     protected Vector primaryKeyClassifications;
62     protected transient Vector nonPrimaryKeyMappings;
63     protected transient Expression primaryKeyExpression;
64
65     /** PERF: Cache mapping that use joining. */
66     protected Vector joinedAttributes = null;
67
68     /** PERF: Cache mappings that require cloning. */
69     protected List cloningMappings;
70
71     public ObjectBuilder(ClassDescriptor descriptor) {
72         this.mappingsByField = new HashMap(20);
73         this.readOnlyMappingsByField = new HashMap(20);
74         this.mappingsByAttribute = new HashMap(20);
75         this.primaryKeyMappings = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(5);
76         this.nonPrimaryKeyMappings = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(10);
77         this.cloningMappings = new ArrayList(10);
78         this.descriptor = descriptor;
79     }
80
81     /**
82      * Create a new row/record for the object builder.
83      * This allows subclasses to define different record types.
84      */

85     public AbstractRecord createRecord() {
86         return new DatabaseRecord();
87     }
88
89     /**
90      * Create a new row/record for the object builder.
91      * This allows subclasses to define different record types.
92      */

93     public AbstractRecord createRecord(int size) {
94         return new DatabaseRecord(size);
95     }
96
97     /**
98      * Add the primary key and its value to the databaseRow for all the non default tables.
99      * This method is used while writing into the multiple tables.
100      */

101     public void addPrimaryKeyForNonDefaultTable(AbstractRecord databaseRow) {
102         // this method has been revised so it calls addPrimaryKeyForNonDefaultTable(DatabaseRow, Object, Session) is similar.
103
// the session and object are null in this case.
104
addPrimaryKeyForNonDefaultTable(databaseRow, null, null);
105     }
106
107     /**
108      * Add the primary key and its value to the databaseRow for all the non default tables.
109      * This method is used while writing into the multiple tables.
110      */

111     public void addPrimaryKeyForNonDefaultTable(AbstractRecord databaseRow, Object JavaDoc object, AbstractSession session) {
112         if (!getDescriptor().hasMultipleTables()) {
113             return;
114         }
115         Enumeration tablesEnum = getDescriptor().getTables().elements();
116
117         // Skip first table.
118
tablesEnum.nextElement();
119         while (tablesEnum.hasMoreElements()) {
120             DatabaseTable table = (DatabaseTable)tablesEnum.nextElement();
121             Map keyMapping = (Map)getDescriptor().getAdditionalTablePrimaryKeyFields().get(table);
122
123             // Loop over the additionalTablePK fields and add the PK info for the table. The join might
124
// be between a fk in the source table and pk in secondary table.
125
if (keyMapping != null) {
126                 Iterator primaryKeyFieldEnum = keyMapping.keySet().iterator();
127                 Iterator secondaryKeyFieldEnum = keyMapping.values().iterator();
128                 while (primaryKeyFieldEnum.hasNext()) {
129                     DatabaseField primaryKeyField = (DatabaseField)primaryKeyFieldEnum.next();
130                     DatabaseField secondaryKeyField = (DatabaseField)secondaryKeyFieldEnum.next();
131                     Object JavaDoc primaryValue = databaseRow.get(primaryKeyField);
132
133                     // normally the primary key has a value, however if the multiple tables were joined by a foreign
134
// key the foreign key has a value.
135
if ((primaryValue == null) && (!databaseRow.containsKey(primaryKeyField))) {
136                         if (object != null) {
137                             DatabaseMapping mapping = getMappingForField(secondaryKeyField);
138                             if (mapping == null) {
139                                 throw DescriptorException.missingMappingForField(secondaryKeyField, getDescriptor());
140                             }
141                             mapping.writeFromObjectIntoRow(object, databaseRow, session);
142                         }
143                         databaseRow.put(primaryKeyField, databaseRow.get(secondaryKeyField));
144                     } else {
145                         databaseRow.put(secondaryKeyField, primaryValue);
146                     }
147                 }
148             }
149         }
150     }
151
152     /**
153      * INTERNAL:
154      * Assign returned row to object
155      */

156     public void assignReturnRow(Object JavaDoc object, AbstractSession writeSession, AbstractRecord row) throws DatabaseException {
157         writeSession.log(SessionLog.FINEST, SessionLog.QUERY, "assign_return_row", row);
158
159         // Require a query context to read into an object.
160
ReadObjectQuery query = new ReadObjectQuery();
161         query.setSession(writeSession);
162
163         // To avoid processing the same mapping twice,
164
// maintain Collection of mappings already used.
165
HashSet handledMappings = new HashSet(row.size());
166         for (int index = 0; index < row.size(); index++) {
167             DatabaseField field = (DatabaseField)row.getFields().elementAt(index);
168             assignReturnValueForField(object, query, row, field, handledMappings);
169         }
170     }
171
172     /**
173      * INTERNAL:
174      * Assign values from objectRow to the object through all the mappings corresponding to the field.
175      */

176     public void assignReturnValueForField(Object JavaDoc object, ReadObjectQuery query, AbstractRecord row, DatabaseField field, Collection handledMappings) {
177         DatabaseMapping mapping = getMappingForField(field);
178         if (mapping != null) {
179             assignReturnValueToMapping(object, query, row, field, mapping, handledMappings);
180         }
181         Vector mappingVector = getReadOnlyMappingsForField(field);
182         if (mappingVector != null) {
183             for (int j = 0; j < mappingVector.size(); j++) {
184                 mapping = (DatabaseMapping)mappingVector.elementAt(j);
185                 assignReturnValueToMapping(object, query, row, field, mapping, handledMappings);
186             }
187         }
188     }
189
190     /**
191      * INTERNAL:
192      * Assign values from objectRow to the object through the mapping.
193      */

194     protected void assignReturnValueToMapping(Object JavaDoc object, ReadObjectQuery query, AbstractRecord row, DatabaseField field, DatabaseMapping mapping, Collection handledMappings) {
195         if (handledMappings.contains(mapping)) {
196             return;
197         }
198         Object JavaDoc attributeValue;
199         if (mapping.isAggregateObjectMapping()) {
200             attributeValue = ((AggregateObjectMapping)mapping).readFromReturnRowIntoObject(row, object, query, handledMappings);
201         } else if (mapping.isDirectToFieldMapping()) {
202             attributeValue = mapping.readFromRowIntoObject(row, null, object, query);
203         } else {
204             query.getSession().log(SessionLog.FINEST, SessionLog.QUERY, "field_for_unsupported_mapping_returned", field, getDescriptor());
205         }
206     }
207
208     /**
209      * INTERNAL:
210      * Update the object primary key by fetching a new sequence number from the accessor.
211      * This assume the uses sequence numbers check has already been done.
212      * @return the sequence value or null if not assigned.
213      * @exception DatabaseException - an error has occurred on the database.
214      */

215     public Object JavaDoc assignSequenceNumber(Object JavaDoc object, AbstractSession writeSession) throws DatabaseException {
216         DatabaseField sequenceNumberField = getDescriptor().getSequenceNumberField();
217         Object JavaDoc existingValue = getBaseValueForField(sequenceNumberField, object);
218
219         //** sequencing refactoring
220
if (existingValue != null) {
221             if (!writeSession.getSequencing().shouldOverrideExistingValue(object.getClass(), existingValue)) {
222                 return null;
223             }
224         }
225         Object JavaDoc sequenceValue = writeSession.getSequencing().getNextValue(object.getClass());
226
227         //CR#2272
228
writeSession.log(SessionLog.FINEST, SessionLog.SEQUENCING, "assign_sequence", sequenceValue, object);
229
230         // Check that the value is not null, this occurs on Sybase identity only **
231
if (sequenceValue == null) {
232             return null;
233         }
234
235         // Now add the value to the object, this gets ugly.
236
AbstractRecord tempRow = createRecord(1);
237         tempRow.put(sequenceNumberField, sequenceValue);
238
239         // Require a query context to read into an object.
240
ReadObjectQuery query = new ReadObjectQuery();
241         query.setSession(writeSession);
242         DatabaseMapping mapping = getBaseMappingForField(sequenceNumberField);
243         Object JavaDoc sequenceIntoObject = getParentObjectForField(sequenceNumberField, object);
244
245         // the following method will return the converted value for the sequence
246
Object JavaDoc convertedSequenceValue = mapping.readFromRowIntoObject(tempRow, null, sequenceIntoObject, query);
247
248         return convertedSequenceValue;
249     }
250
251     /**
252      * Each mapping is recursed to assign values from the databaseRow to the attributes in the domain object.
253      */

254     public void buildAttributesIntoObject(Object JavaDoc domainObject, AbstractRecord databaseRow, ObjectBuildingQuery query, JoinedAttributeManager joinManager, boolean forRefresh) throws DatabaseException {
255         AbstractSession executionSession = query.getSession().getExecutionSession(query);
256
257         // PERF: Avoid synchronized enumerator as is concurrency bottleneck.
258
Vector mappings = getDescriptor().getMappings();
259
260         // PERF: Cache if all mappings should be read.
261
boolean readAllMappings = query.shouldReadAllMappings();
262         int mappingsSize = mappings.size();
263         for (int index = 0; index < mappingsSize; index++) {
264             DatabaseMapping mapping = (DatabaseMapping)mappings.get(index);
265             if (readAllMappings || query.shouldReadMapping(mapping)) {
266                 mapping.readFromRowIntoObject(databaseRow, joinManager, domainObject, query, executionSession);
267             }
268         }
269
270         // PERF: Avoid events if no listeners.
271
if (getDescriptor().getEventManager().hasAnyEventListeners()) {
272             // Need to run post build or refresh selector, currently check with the query for this,
273
// I'm not sure which should be called it case of refresh building a new object, currently refresh is used...
274
oracle.toplink.essentials.descriptors.DescriptorEvent event = new oracle.toplink.essentials.descriptors.DescriptorEvent(domainObject);
275             event.setQuery(query);
276             event.setSession(query.getSession());
277             event.setRecord(databaseRow);
278             if (forRefresh) {
279                 //this method can be called from different places within TopLink. We may be
280
//executing refresh query but building the object not refreshing so we must
281
//throw the appropriate event.
282
//bug 3325315
283
event.setEventCode(DescriptorEventManager.PostRefreshEvent);
284             } else {
285                 event.setEventCode(DescriptorEventManager.PostBuildEvent);
286             }
287             getDescriptor().getEventManager().executeEvent(event);
288         }
289     }
290
291     /**
292      * Returns the clone of the specified object. This is called only from unit of work.
293      * The clonedDomainObject sent as parameter is always a working copy from the unit of work.
294      */

295     public Object JavaDoc buildBackupClone(Object JavaDoc clone, UnitOfWorkImpl unitOfWork) {
296         // The copy policy builds clone
297
Object JavaDoc backup = getDescriptor().getCopyPolicy().buildClone(clone, unitOfWork);
298
299         // PERF: Avoid synchronized enumerator as is concurrency bottleneck.
300
List mappings = getCloningMappings();
301         for (int index = 0; index < mappings.size(); index++) {
302             ((DatabaseMapping)mappings.get(index)).buildBackupClone(clone, backup, unitOfWork);
303         }
304
305         return backup;
306     }
307
308     /**
309      * Build and return the expression to use as the where clause to delete an object.
310      * The row is passed to allow the version number to be extracted from it.
311      */

312     public Expression buildDeleteExpression(DatabaseTable table, AbstractRecord row) {
313         if (getDescriptor().usesOptimisticLocking() && (getDescriptor().getTables().firstElement().equals(table))) {
314             return getDescriptor().getOptimisticLockingPolicy().buildDeleteExpression(table, primaryKeyExpression, row);
315         } else {
316             return buildPrimaryKeyExpression(table);
317         }
318     }
319
320     /**
321      * Return a new instance of the receiver's javaClass.
322      */

323     public Object JavaDoc buildNewInstance() {
324         return getDescriptor().getInstantiationPolicy().buildNewInstance();
325     }
326
327     /**
328      * Return an instance of the recievers javaClass. Set the attributes of an instance
329      * from the values stored in the database row.
330      */

331     public Object JavaDoc buildObject(ObjectBuildingQuery query, AbstractRecord databaseRow, JoinedAttributeManager joinManager) throws DatabaseException, QueryException {
332         // Profile object building.
333
AbstractSession session = query.getSession();
334         session.startOperationProfile(SessionProfiler.OBJECT_BUILDING);
335
336         Vector primaryKey = extractPrimaryKeyFromRow(databaseRow, session);
337
338         // Check for null primary key, this is not allowed.
339
if ((primaryKey == null) && (!query.hasPartialAttributeExpressions()) && (!getDescriptor().isAggregateCollectionDescriptor())) {
340             // Profile object building.
341
session.endOperationProfile(SessionProfiler.OBJECT_BUILDING);
342
343             //BUG 3168689: EJBQL: "Select Distinct s.customer from SpouseBean s"
344
//BUG 3168699: EJBQL: "Select s.customer from SpouseBean s where s.id = '6'"
345
//If we return either a single null, or a Collection containing at least
346
//one null, then we want the nulls returned/included if the indicated
347
//property is set in the query. (As opposed to throwing an Exception).
348
if (query.getProperty("return null if primary key is null") != null) {
349                 return null;
350             } else {
351                 throw QueryException.nullPrimaryKeyInBuildingObject(query, databaseRow);
352             }
353         }
354         ClassDescriptor concreteDescriptor = getDescriptor();
355         if (concreteDescriptor.hasInheritance() && concreteDescriptor.getInheritancePolicy().shouldReadSubclasses()) {
356             Class JavaDoc classValue = concreteDescriptor.getInheritancePolicy().classFromRow(databaseRow, session);
357             concreteDescriptor = session.getDescriptor(classValue);
358             if ((concreteDescriptor == null) && query.hasPartialAttributeExpressions()) {
359                 concreteDescriptor = getDescriptor();
360             }
361             if (concreteDescriptor == null) {
362                 // Profile object building.
363
session.endOperationProfile(SessionProfiler.OBJECT_BUILDING);
364                 throw QueryException.noDescriptorForClassFromInheritancePolicy(query, classValue);
365             }
366         }
367         Object JavaDoc domainObject = null;
368         try {
369             if (session.isUnitOfWork()) {
370                 // Do not wrap yet if in UnitOfWork, as there is still much more
371
// processing ahead.
372
domainObject = buildObjectInUnitOfWork(query, joinManager, databaseRow, (UnitOfWorkImpl)session, primaryKey, concreteDescriptor);
373             } else {
374                 domainObject = buildObject(query, databaseRow, session, primaryKey, concreteDescriptor, joinManager);
375
376                 // wrap the object if the query requires it.
377
if (query.shouldUseWrapperPolicy()) {
378                     domainObject = concreteDescriptor.getObjectBuilder().wrapObject(domainObject, session);
379                 }
380             }
381         } finally {
382             session.endOperationProfile(SessionProfiler.OBJECT_BUILDING);
383         }
384         return domainObject;
385     }
386
387     /**
388      * For executing all reads on the UnitOfWork, the session when building
389      * objects from rows will now be the UnitOfWork. Usefull if the rows were
390      * read via a dirty write connection and we want to avoid putting uncommitted
391      * data in the global cache.
392      * <p>
393      * Decides whether to call either buildWorkingCopyCloneFromRow (bypassing
394      * shared cache) or buildWorkingCopyCloneNormally (placing the result in the
395      * shared cache).
396      */

397     protected Object JavaDoc buildObjectInUnitOfWork(ObjectBuildingQuery query, JoinedAttributeManager joinManager, AbstractRecord databaseRow, UnitOfWorkImpl unitOfWork, Vector primaryKey, ClassDescriptor concreteDescriptor) throws DatabaseException, QueryException {
398         // When in transaction we are reading via the write connection
399
// and so do not want to corrupt the shared cache with dirty objects.
400
// Hence we build and refresh clones directly from the database row.
401
if ((unitOfWork.getCommitManager().isActive() || unitOfWork.wasTransactionBegunPrematurely()) && !unitOfWork.isClassReadOnly(concreteDescriptor.getJavaClass())) {
402             // It is easier to switch once to the correct builder here.
403
return concreteDescriptor.getObjectBuilder().buildWorkingCopyCloneFromRow(query, joinManager, databaseRow, unitOfWork, primaryKey);
404         }
405
406         return buildWorkingCopyCloneNormally(query, databaseRow, unitOfWork, primaryKey, concreteDescriptor, joinManager);
407     }
408
409     /**
410      * buildWorkingCopyCloneFromRow is an alternative to this which is the
411      * normal behavior.
412      * A row is read from the database, an original is built/refreshed/returned
413      * from the shared cache, and the original is registered/conformed/reverted
414      * in the UnitOfWork.
415      * <p>
416      * This default behavior is only safe when the query is executed on a read
417      * connection, otherwise uncommitted data might get loaded into the shared
418      * cache.
419      * <p>
420      * Represents the way TopLink has always worked.
421      */

422     protected Object JavaDoc buildWorkingCopyCloneNormally(ObjectBuildingQuery query, AbstractRecord databaseRow, UnitOfWorkImpl unitOfWork, Vector primaryKey, ClassDescriptor concreteDescriptor, JoinedAttributeManager joinManager) throws DatabaseException, QueryException {
423         // This is normal case when we are not in transaction.
424
// Pass the query off to the parent. Let it build the object and
425
// cache it normally, then register/refresh it.
426
AbstractSession session = unitOfWork.getParentIdentityMapSession(query);
427         Object JavaDoc original = null;
428         Object JavaDoc clone = null;
429
430         // forwarding queries to different sessions is now as simple as setting
431
// the session on the query.
432
query.setSession(session);
433         if (session.isUnitOfWork()) {
434             original = buildObjectInUnitOfWork(query, joinManager, databaseRow, (UnitOfWorkImpl)session, primaryKey, concreteDescriptor);
435         } else {
436             original = buildObject(query, databaseRow, session, primaryKey, concreteDescriptor, joinManager);
437         }
438         query.setSession(unitOfWork);
439         //GFBug#404 Pass in joinManager or not based on if shouldCascadeCloneToJoinedRelationship is set to true
440
if (unitOfWork.shouldCascadeCloneToJoinedRelationship()) {
441             clone = query.registerIndividualResult(original, unitOfWork, false, joinManager);// false == no longer building directly from rows.
442
} else {
443             clone = query.registerIndividualResult(original, unitOfWork, false, null);// false == no longer building directly from rows.
444
}
445         return clone;
446     }
447
448     /**
449      * Return an instance of the recievers javaClass. Set the attributes of an instance
450      * from the values stored in the database row.
451      */

452     protected Object JavaDoc buildObject(ObjectBuildingQuery query, AbstractRecord databaseRow, AbstractSession session, Vector primaryKey, ClassDescriptor concreteDescriptor, JoinedAttributeManager joinManager) throws DatabaseException, QueryException {
453         Object JavaDoc domainObject = null;
454
455         //cache key is used for object locking
456
CacheKey cacheKey = null;
457         try {
458             // Check if the objects exists in the identity map.
459
if (query.shouldMaintainCache()) {
460                 //lock the object in the IM
461
// PERF: Only use deferred locking if required.
462
// CR#3876308 If joining is used, deferred locks are still required.
463
if (DeferredLockManager.SHOULD_USE_DEFERRED_LOCKS && (concreteDescriptor.shouldAcquireCascadedLocks() || joinManager.hasJoinedAttributes())) {
464                     cacheKey = session.getIdentityMapAccessorInstance().acquireDeferredLock(primaryKey, concreteDescriptor.getJavaClass(), concreteDescriptor);
465                     domainObject = cacheKey.getObject();
466
467                     int counter = 0;
468                     while ((domainObject == null) && (counter < 1000)) {
469                         if (cacheKey.getMutex().getActiveThread() == Thread.currentThread()) {
470                             break;
471                         }
472                         //must release lock here to prevent acquiring multiple deferred locks but only
473
//releasing one at the end of the build object call.
474
//BUG 5156075
475
cacheKey.releaseDeferredLock();
476
477                         //sleep and try again if we arenot the owner of the lock for CR 2317
478
// prevents us from modifying a cache key that another thread has locked.
479
try {
480                             Thread.sleep(10);
481                         } catch (InterruptedException JavaDoc exception) {
482                         }
483                         cacheKey = session.getIdentityMapAccessorInstance().acquireDeferredLock(primaryKey, concreteDescriptor.getJavaClass(), concreteDescriptor);
484                         domainObject = cacheKey.getObject();
485                         counter++;
486                     }
487                     if (counter == 1000) {
488                         throw ConcurrencyException.maxTriesLockOnBuildObjectExceded(cacheKey.getMutex().getActiveThread(), Thread.currentThread());
489                     }
490                 } else {
491                     cacheKey = session.getIdentityMapAccessorInstance().acquireLock(primaryKey, concreteDescriptor.getJavaClass(), concreteDescriptor);
492                     domainObject = cacheKey.getObject();
493                 }
494             }
495
496             if (domainObject == null) {
497                 if (query.isReadObjectQuery() && ((ReadObjectQuery)query).shouldLoadResultIntoSelectionObject()) {
498                     domainObject = ((ReadObjectQuery)query).getSelectionObject();
499                 } else {
500                     domainObject = concreteDescriptor.getObjectBuilder().buildNewInstance();
501                 }
502
503                 // The object must be registered before building its attributes to resolve circular dependancies.
504
if (query.shouldMaintainCache()) {
505                     cacheKey.setObject(domainObject);
506
507                     copyQueryInfoToCacheKey(cacheKey, query, databaseRow, session, concreteDescriptor);
508
509                     //set the fetch group to the domain object
510
if (concreteDescriptor.hasFetchGroupManager()) {
511                         concreteDescriptor.getFetchGroupManager().setObjectFetchGroup(domainObject, query.getFetchGroup());
512                     }
513                 }
514
515                 concreteDescriptor.getObjectBuilder().buildAttributesIntoObject(domainObject, databaseRow, query, joinManager, false);
516             } else {
517                 if (query.isReadObjectQuery() && ((ReadObjectQuery)query).shouldLoadResultIntoSelectionObject()) {
518                     copyInto(domainObject, ((ReadObjectQuery)query).getSelectionObject());
519                     domainObject = ((ReadObjectQuery)query).getSelectionObject();
520                 }
521
522                 //check if the cached object has been invalidated
523
boolean isInvalidated = concreteDescriptor.getCacheInvalidationPolicy().isInvalidated(cacheKey, query.getExecutionTime());
524
525                 //CR #4365 - Queryid comparison used to prevent infinit recursion on refresh object cascade all
526
//if the concurrency manager is locked by the merge process then no refresh is required.
527
// bug # 3388383 If this thread does not have the active lock then someone is building the object so in order to maintain data integrity this thread will not
528
// fight to overwrite the object ( this also will avoid potential deadlock situations
529
if ((cacheKey.getMutex().getActiveThread() == Thread.currentThread()) && ((query.shouldRefreshIdentityMapResult() || concreteDescriptor.shouldAlwaysRefreshCache() || isInvalidated) && ((cacheKey.getLastUpdatedQueryId() != query.getQueryId()) && !cacheKey.getMutex().isLockedByMergeManager()))) {
530                     //cached object might be partially fetched, only refresh the fetch group attributes of the query if
531
//the cached partial object is not invalidated and does not contain all data for the fetch group.
532
if (concreteDescriptor.hasFetchGroupManager() && concreteDescriptor.getFetchGroupManager().isPartialObject(domainObject)) {
533                         //only ObjectLevelReadQuery and above support partial objects
534
revertFetchGroupData(domainObject, concreteDescriptor, cacheKey, ((ObjectLevelReadQuery)query), joinManager, databaseRow, session);
535                     } else {
536                         boolean refreshRequired = true;
537                         if (concreteDescriptor.usesOptimisticLocking()) {
538                             OptimisticLockingPolicy policy = concreteDescriptor.getOptimisticLockingPolicy();
539                             Object JavaDoc cacheValue = policy.getValueToPutInCache(databaseRow, session);
540                             if (concreteDescriptor.shouldOnlyRefreshCacheIfNewerVersion()) {
541                                 refreshRequired = policy.isNewerVersion(databaseRow, domainObject, primaryKey, session);
542                                 if (!refreshRequired) {
543                                     cacheKey.setReadTime(query.getExecutionTime());
544                                 }
545                             }
546                             if (refreshRequired) {
547                                 //update the wriet lock value
548
cacheKey.setWriteLockValue(cacheValue);
549                             }
550                         }
551                         if (refreshRequired) {
552                             //CR #4365 - used to prevent infinit recursion on refresh object cascade all
553
cacheKey.setLastUpdatedQueryId(query.getQueryId());
554                             concreteDescriptor.getObjectBuilder().buildAttributesIntoObject(domainObject, databaseRow, query, joinManager, true);
555                             cacheKey.setReadTime(query.getExecutionTime());
556                         }
557                     }
558                 } else if (concreteDescriptor.hasFetchGroupManager() && (concreteDescriptor.getFetchGroupManager().isPartialObject(domainObject) && (!concreteDescriptor.getFetchGroupManager().isObjectValidForFetchGroup(domainObject, query.getFetchGroup())))) {
559                     //the fetched object is not sufficient for the fetch group of the query
560
//refresh attributes of the query's fetch group
561
concreteDescriptor.getObjectBuilder().buildAttributesIntoObject(domainObject, databaseRow, query, joinManager, false);
562                     concreteDescriptor.getFetchGroupManager().unionFetchGroupIntoObject(domainObject, query.getFetchGroup());
563                 }
564                 // 3655915: a query with join/batch'ing that gets a cache hit
565
// may require some attributes' valueholders to be re-built.
566
else if (joinManager.hasJoinedAttributeExpressions()) {
567                     for (Iterator e = joinManager.getJoinedAttributeExpressions().iterator();
568                              e.hasNext();) {
569                         QueryKeyExpression qke = (QueryKeyExpression)e.next();
570
571                         // only worry about immediate attributes
572
if (qke.getBaseExpression().isExpressionBuilder()) {
573                             DatabaseMapping dm = getMappingForAttributeName(qke.getName());
574
575                             if (dm == null) {
576                                 throw ValidationException.missingMappingForAttribute(concreteDescriptor, qke.getName(), this.toString());
577                             } else {
578                                 // Bug 4230655 - do not replace instantiated valueholders
579
Object JavaDoc attributeValue = dm.getAttributeValueFromObject(domainObject);
580                                 if (!((attributeValue != null) && dm.isForeignReferenceMapping() && ((ForeignReferenceMapping)dm).usesIndirection() && ((ForeignReferenceMapping)dm).getIndirectionPolicy().objectIsInstantiated(attributeValue))) {
581                                     dm.readFromRowIntoObject(databaseRow, joinManager, domainObject, query, query.getSession().getExecutionSession(query));
582                                 }
583                             }
584                         }
585                     }
586                 }
587             }
588         } finally {
589             if (query.shouldMaintainCache() && (cacheKey != null)) {
590                 // bug 2681401:
591
// in case of exception (for instance, thrown by buildNewInstance())
592
// cacheKey.getObject() may be null.
593
if (cacheKey.getObject() != null) {
594                     cacheKey.updateAccess();
595                 }
596
597                 // PERF: Only use deferred locking if required.
598
if (DeferredLockManager.SHOULD_USE_DEFERRED_LOCKS && (concreteDescriptor.shouldAcquireCascadedLocks() || joinManager.hasJoinedAttributes())) {
599                     cacheKey.releaseDeferredLock();
600                 } else {
601                     cacheKey.release();
602                 }
603             }
604         }
605
606         return domainObject;
607     }
608
609     /**
610      * Clean up the cached object data and only revert the fetch group data back to the cached object.
611      */

612     private void revertFetchGroupData(Object JavaDoc domainObject, ClassDescriptor concreteDescriptor, CacheKey cacheKey, ObjectBuildingQuery query, JoinedAttributeManager joinManager, AbstractRecord databaseRow, AbstractSession session) {
613         //the cached object is either invalidated, or staled as the version is newer, or a refresh is explicitly set on the query.
614
//clean all data of the cache object.
615
concreteDescriptor.getFetchGroupManager().reset(domainObject);
616         //read in the fetch group data only
617
concreteDescriptor.getObjectBuilder().buildAttributesIntoObject(domainObject, databaseRow, query, joinManager, false);
618         //set fetch group refrence to the cached object
619
concreteDescriptor.getFetchGroupManager().setObjectFetchGroup(domainObject, query.getFetchGroup());
620         //set refresh on fetch group
621
concreteDescriptor.getFetchGroupManager().setRefreshOnFetchGroupToObject(domainObject, (query.shouldRefreshIdentityMapResult() || concreteDescriptor.shouldAlwaysRefreshCache()));
622         //set query id to prevent infinite recursion on refresh object cascade all
623
cacheKey.setLastUpdatedQueryId(query.getQueryId());
624         //register the object into the IM and set the write lock object if applied.
625
if (concreteDescriptor.usesOptimisticLocking()) {
626             OptimisticLockingPolicy policy = concreteDescriptor.getOptimisticLockingPolicy();
627             cacheKey.setWriteLockValue(policy.getValueToPutInCache(databaseRow, session));
628         }
629         cacheKey.setReadTime(query.getExecutionTime());
630         //validate the cached object
631
cacheKey.setInvalidationState(CacheKey.CHECK_INVALIDATION_POLICY);
632     }
633
634     /**
635      * Return a container which contains the instances of the receivers javaClass.
636      * Set the fields of the instance to the values stored in the database rows.
637      */

638     public Object JavaDoc buildObjectsInto(ReadAllQuery query, Vector databaseRows, Object JavaDoc domainObjects) throws DatabaseException {
639         Set identitySet = null;
640         for (Enumeration rowsEnum = databaseRows.elements(); rowsEnum.hasMoreElements();) {
641             AbstractRecord databaseRow = (AbstractRecord)rowsEnum.nextElement();
642
643             // Skip null rows from 1-m joining duplicate row filtering.
644
if (databaseRow != null) {
645                 Object JavaDoc domainObject = buildObject(query, databaseRow, query.getJoinedAttributeManager());
646
647                 // Avoid duplicates if -m joining was used and a cache hit occured.
648
if (query.getJoinedAttributeManager().isToManyJoin()) {
649                     if (identitySet == null) {
650                         identitySet = new TopLinkIdentityHashSet(databaseRows.size());
651                     }
652                     if (!identitySet.contains(domainObject)) {
653                         identitySet.add(domainObject);
654                         query.getContainerPolicy().addInto(domainObject, domainObjects, query.getSession());
655                     }
656                 } else {
657                     query.getContainerPolicy().addInto(domainObject, domainObjects, query.getSession());
658                 }
659             }
660         }
661
662         return domainObjects;
663     }
664
665     /**
666      * Build the primary key expression for the secondary table.
667      */

668     public Expression buildPrimaryKeyExpression(DatabaseTable table) throws DescriptorException {
669         if (getDescriptor().getTables().firstElement().equals(table)) {
670             return getPrimaryKeyExpression();
671         }
672
673         Map keyMapping = (Map)getDescriptor().getAdditionalTablePrimaryKeyFields().get(table);
674         if (keyMapping == null) {
675             throw DescriptorException.multipleTablePrimaryKeyNotSpecified(getDescriptor());
676         }
677
678         ExpressionBuilder builder = new ExpressionBuilder();
679         Expression expression = null;
680         for (Iterator primaryKeyEnum = keyMapping.values().iterator(); primaryKeyEnum.hasNext();) {
681             DatabaseField field = (DatabaseField)primaryKeyEnum.next();
682             expression = (builder.getField(field).equal(builder.getParameter(field))).and(expression);
683         }
684
685         return expression;
686     }
687
688     /**
689      * Build the primary key expression from the specified primary key values.
690      */

691     public Expression buildPrimaryKeyExpressionFromKeys(Vector primaryKeyValues, AbstractSession session) {
692         Expression expression = null;
693         Expression subExpression;
694         Expression builder = new ExpressionBuilder();
695         List primaryKeyFields = getDescriptor().getPrimaryKeyFields();
696
697         for (int index = 0; index < primaryKeyFields.size(); index++) {
698             Object JavaDoc value = primaryKeyValues.get(index);
699             DatabaseField field = (DatabaseField)primaryKeyFields.get(index);
700             if (value != null) {
701                 subExpression = builder.getField(field).equal(value);
702                 expression = subExpression.and(expression);
703             }
704         }
705
706         return expression;
707     }
708
709     /**
710      * Build the primary key expression from the specified domain object.
711      */

712     public Expression buildPrimaryKeyExpressionFromObject(Object JavaDoc domainObject, AbstractSession session) {
713         return buildPrimaryKeyExpressionFromKeys(extractPrimaryKeyFromObject(domainObject, session), session);
714     }
715
716     /**
717      * Build the row representation of an object.
718      */

719     public AbstractRecord buildRow(Object JavaDoc object, AbstractSession session) {
720         return buildRow(createRecord(), object, session);
721     }
722
723     /**
724      * Build the row representation of an object.
725      */

726     public AbstractRecord buildRow(AbstractRecord databaseRow, Object JavaDoc object, AbstractSession session) {
727         // PERF: Avoid synchronized enumerator as is concurrency bottleneck.
728
Vector mappings = getDescriptor().getMappings();
729         int mappingsSize = mappings.size();
730         for (int index = 0; index < mappingsSize; index++) {
731             DatabaseMapping mapping = (DatabaseMapping)mappings.get(index);
732             mapping.writeFromObjectIntoRow(object, databaseRow, session);
733         }
734
735         // If this descriptor is involved in inheritence add the class type.
736
if (getDescriptor().hasInheritance()) {
737             getDescriptor().getInheritancePolicy().addClassIndicatorFieldToRow(databaseRow);
738         }
739
740         // If this descriptor has multiple tables then we need to append the primary keys for
741
// the non default tables.
742
if (!getDescriptor().isAggregateDescriptor()) {
743             addPrimaryKeyForNonDefaultTable(databaseRow);
744         }
745
746         return databaseRow;
747     }
748
749     /**
750      * Build the row representation of the object for update. The row built does not
751      * contain entries for uninstantiated attributes.
752      */

753     public AbstractRecord buildRowForShallowInsert(Object JavaDoc object, AbstractSession session) {
754         return buildRowForShallowInsert(createRecord(), object, session);
755     }
756
757     /**
758      * Build the row representation of the object for update. The row built does not
759      * contain entries for uninstantiated attributes.
760      */

761     public AbstractRecord buildRowForShallowInsert(AbstractRecord databaseRow, Object JavaDoc object, AbstractSession session) {
762         // PERF: Avoid synchronized enumerator as is concurrency bottleneck.
763
Vector mappings = getDescriptor().getMappings();
764         int mappingsSize = mappings.size();
765         for (int index = 0; index < mappingsSize; index++) {
766             DatabaseMapping mapping = (DatabaseMapping)mappings.get(index);
767             mapping.writeFromObjectIntoRowForShallowInsert(object, databaseRow, session);
768         }
769
770         // If this descriptor is involved in inheritence add the class type.
771
if (getDescriptor().hasInheritance()) {
772             getDescriptor().getInheritancePolicy().addClassIndicatorFieldToRow(databaseRow);
773         }
774
775         // If this descriptor has multiple tables then we need to append the primary keys for
776
// the non default tables.
777
if (!getDescriptor().isAggregateDescriptor()) {
778             addPrimaryKeyForNonDefaultTable(databaseRow);
779         }
780
781         return databaseRow;
782     }
783
784     /**
785      * Build the row representation of an object.
786      */

787     public AbstractRecord buildRowWithChangeSet(ObjectChangeSet objectChangeSet, AbstractSession session) {
788         return buildRowWithChangeSet(createRecord(), objectChangeSet, session);
789     }
790
791     /**
792      * Build the row representation of an object.
793      */

794     public AbstractRecord buildRowWithChangeSet(AbstractRecord databaseRow, ObjectChangeSet objectChangeSet, AbstractSession session) {
795         for (Enumeration changeRecords = objectChangeSet.getChanges().elements();
796                  changeRecords.hasMoreElements();) {
797             ChangeRecord changeRecord = (ChangeRecord)changeRecords.nextElement();
798             DatabaseMapping mapping = changeRecord.getMapping();
799             mapping.writeFromObjectIntoRowWithChangeRecord(changeRecord, databaseRow, session);
800         }
801
802         // If this descriptor is involved in inheritence add the class type.
803
if (getDescriptor().hasInheritance()) {
804             getDescriptor().getInheritancePolicy().addClassIndicatorFieldToRow(databaseRow);
805         }
806
807         return databaseRow;
808     }
809
810     /**
811      * Build the row representation of the object for update. The row built does not
812      * contain entries for uninstantiated attributes.
813      */

814     public AbstractRecord buildRowForShallowInsertWithChangeSet(ObjectChangeSet objectChangeSet, AbstractSession session) {
815         return buildRowForShallowInsertWithChangeSet(createRecord(), objectChangeSet, session);
816     }
817
818     /**
819      * Build the row representation of the object for update. The row built does not
820      * contain entries for uninstantiated attributes.
821      */

822     public AbstractRecord buildRowForShallowInsertWithChangeSet(AbstractRecord databaseRow, ObjectChangeSet objectChangeSet, AbstractSession session) {
823         for (Iterator changeRecords = objectChangeSet.getChanges().iterator();
824                  changeRecords.hasNext();) {
825             ChangeRecord changeRecord = (ChangeRecord)changeRecords.next();
826             DatabaseMapping mapping = changeRecord.getMapping();
827             mapping.writeFromObjectIntoRowForShallowInsertWithChangeRecord(changeRecord, databaseRow, session);
828         }
829
830         // If this descriptor is involved in inheritence add the class type.
831
if (getDescriptor().hasInheritance()) {
832             getDescriptor().getInheritancePolicy().addClassIndicatorFieldToRow(databaseRow);
833         }
834
835         // If this descriptor has multiple tables then we need to append the primary keys for
836
// the non default tables.
837
if (!getDescriptor().isAggregateDescriptor()) {
838             addPrimaryKeyForNonDefaultTable(databaseRow);
839         }
840
841         return databaseRow;
842     }
843
844     /**
845      * Build the row representation of an object. The row built is used only for translations
846      * for the expressions in the expresion framework.
847      */

848     public AbstractRecord buildRowForTranslation(Object JavaDoc object, AbstractSession session) {
849         AbstractRecord databaseRow = createRecord();
850
851         for (Iterator mappings = getPrimaryKeyMappings().iterator(); mappings.hasNext();) {
852             DatabaseMapping mapping = (DatabaseMapping)mappings.next();
853             if (mapping != null) {
854                 mapping.writeFromObjectIntoRow(object, databaseRow, session);
855             }
856         }
857
858         // If this descriptor has multiple tables then we need to append the primary keys for
859
// the non default tables, this is require for m-m, dc defined in the Builder that prefixes the wrong table name.
860
// Ideally the mappings should take part in building the translation row so they can add required values.
861
addPrimaryKeyForNonDefaultTable(databaseRow, object, session);
862
863         return databaseRow;
864     }
865
866     /**
867      * Build the row representation of the object for update. The row built does not
868      * contain entries for uninstantiated attributes.
869      */

870     public AbstractRecord buildRowForUpdate(WriteObjectQuery query) {
871         AbstractRecord databaseRow = createRecord();
872
873         for (Iterator mappings = getNonPrimaryKeyMappings().iterator();
874                  mappings.hasNext();) {
875             DatabaseMapping mapping = (DatabaseMapping)mappings.next();
876             mapping.writeFromObjectIntoRowForUpdate(query, databaseRow);
877         }
878
879         // If this descriptor is involved in inheritence and is an Aggregate, add the class type.
880
// Added Nov 8, 2000 Mostly by PWK but also JED
881
// Prs 24801
882
// Modified Dec 11, 2000 TGW with assitance from PWK
883
// Prs 27554
884
if (getDescriptor().hasInheritance() && getDescriptor().isAggregateDescriptor()) {
885             if (query.getObject() != null) {
886                 if (query.getBackupClone() == null) {
887                     getDescriptor().getInheritancePolicy().addClassIndicatorFieldToRow(databaseRow);
888                 } else {
889                     if (!query.getObject().getClass().equals(query.getBackupClone().getClass())) {
890                         getDescriptor().getInheritancePolicy().addClassIndicatorFieldToRow(databaseRow);
891                     }
892                 }
893             }
894         }
895
896         return databaseRow;
897     }
898
899     /**
900      * Build the row representation of the object for update. The row built does not
901      * contain entries for uninstantiated attributes.
902      */

903     public AbstractRecord buildRowForUpdateWithChangeSet(WriteObjectQuery query) {
904         AbstractRecord databaseRow = createRecord();
905
906         for (Iterator changeRecords = query.getObjectChangeSet().getChanges().iterator();
907                  changeRecords.hasNext();) {
908             ChangeRecord changeRecord = (ChangeRecord)changeRecords.next();
909             DatabaseMapping mapping = changeRecord.getMapping();
910             mapping.writeFromObjectIntoRowWithChangeRecord(changeRecord, databaseRow, query.getSession());
911         }
912
913         return databaseRow;
914     }
915
916     /**
917      * Build the row representation of an object.
918      */

919     public AbstractRecord buildRowForWhereClause(ObjectLevelModifyQuery query) {
920         AbstractRecord databaseRow = createRecord();
921
922         for (Iterator mappings = getDescriptor().getMappings().iterator();
923                  mappings.hasNext();) {
924             DatabaseMapping mapping = (DatabaseMapping)mappings.next();
925             mapping.writeFromObjectIntoRowForWhereClause(query, databaseRow);
926         }
927
928         // If this descriptor has multiple tables then we need to append the primary keys for
929
// the non default tables.
930
if (!getDescriptor().isAggregateDescriptor()) {
931             addPrimaryKeyForNonDefaultTable(databaseRow);
932         }
933
934         return databaseRow;
935     }
936
937     /**
938      * Build the row from the primary key values.
939      */

940     public AbstractRecord buildRowFromPrimaryKeyValues(Vector key, AbstractSession session) {
941         AbstractRecord databaseRow = createRecord(key.size());
942         int keySize = key.size();
943         for (int index = 0; index < keySize; index++) {
944             DatabaseField field = (DatabaseField)getDescriptor().getPrimaryKeyFields().get(index);
945             Object JavaDoc value = key.elementAt(index);
946             value = session.getPlatform(getDescriptor().getJavaClass()).getConversionManager().convertObject(value, field.getType());
947             databaseRow.put(field, value);
948         }
949
950         return databaseRow;
951     }
952
953     /**
954      * Build the row of all of the fields used for insertion.
955      */

956     public AbstractRecord buildTemplateInsertRow(AbstractSession session) {
957         AbstractRecord databaseRow = createRecord();
958         buildTemplateInsertRow(session, databaseRow);
959         return databaseRow;
960     }
961
962     public void buildTemplateInsertRow(AbstractSession session, AbstractRecord databaseRow) {
963         for (Iterator mappings = getDescriptor().getMappings().iterator();
964                  mappings.hasNext();) {
965             DatabaseMapping mapping = (DatabaseMapping)mappings.next();
966             mapping.writeInsertFieldsIntoRow(databaseRow, session);
967         }
968
969         // If this descriptor is involved in inheritence add the class type.
970
if (getDescriptor().hasInheritance()) {
971             getDescriptor().getInheritancePolicy().addClassIndicatorFieldToInsertRow(databaseRow);
972         }
973
974         // If this descriptor has multiple tables then we need to append the primary keys for
975
// the non default tables.
976
if (!getDescriptor().isAggregateDescriptor()) {
977             addPrimaryKeyForNonDefaultTable(databaseRow);
978         }
979
980         if (getDescriptor().usesOptimisticLocking()) {
981             getDescriptor().getOptimisticLockingPolicy().addLockFieldsToUpdateRow(databaseRow, session);
982         }
983
984         //** sequencing refactoring
985
if (getDescriptor().usesSequenceNumbers() && session.getSequencing().shouldAcquireValueAfterInsert(getDescriptor().getJavaClass())) {
986             databaseRow.remove(getDescriptor().getSequenceNumberField());
987         }
988     }
989
990     /**
991      * Build the row representation of the object for update. The row built does not
992      * contain entries for uninstantiated attributes.
993      */

994     public AbstractRecord buildTemplateUpdateRow(AbstractSession session) {
995         AbstractRecord databaseRow = createRecord();
996
997         for (Iterator mappings = getNonPrimaryKeyMappings().iterator();
998                  mappings.hasNext();) {
999             DatabaseMapping mapping = (DatabaseMapping)mappings.next();
1000            mapping.writeUpdateFieldsIntoRow(databaseRow, session);
1001        }
1002
1003        if (getDescriptor().usesOptimisticLocking()) {
1004            getDescriptor().getOptimisticLockingPolicy().addLockFieldsToUpdateRow(databaseRow, session);
1005        }
1006
1007        return databaseRow;
1008    }
1009
1010    /**
1011     * Build and return the expression to use as the where clause to an update object.
1012     * The row is passed to allow the version number to be extracted from it.
1013     */

1014    public Expression buildUpdateExpression(DatabaseTable table, AbstractRecord transactionRow, AbstractRecord modifyRow) {
1015        // Only the first table must use the lock check.
1016
Expression primaryKeyExpression = buildPrimaryKeyExpression(table);
1017        if (getDescriptor().usesOptimisticLocking()) {
1018            return getDescriptor().getOptimisticLockingPolicy().buildUpdateExpression(table, primaryKeyExpression, transactionRow, modifyRow);
1019        } else {
1020            return primaryKeyExpression;
1021        }
1022    }
1023
1024    /**
1025     * INTERNAL:
1026     * Build just the primary key mappings into the object.
1027     */

1028    public void buildPrimaryKeyAttributesIntoObject(Object JavaDoc original, AbstractRecord databaseRow, ObjectBuildingQuery query) throws DatabaseException, QueryException {
1029        AbstractSession executionSession = query.getSession().getExecutionSession(query);
1030
1031        // PERF: Avoid synchronized enumerator as is concurrency bottleneck.
1032
Vector mappings = getPrimaryKeyMappings();
1033        int mappingsSize = mappings.size();
1034        for (int i = 0; i < mappingsSize; i++) {
1035            DatabaseMapping mapping = (DatabaseMapping)mappings.get(i);
1036            mapping.buildShallowOriginalFromRow(databaseRow, original, query, executionSession);
1037        }
1038    }
1039
1040    /**
1041     * INTERNAL:
1042     * For reading through the write connection when in transaction,
1043     * We need a partially populated original, so that we
1044     * can build a clone using the copy policy, even though we can't
1045     * put this original in the shared cache yet; just build a
1046     * shallow original (i.e. just enough to copy over the primary
1047     * key and some direct attributes) and keep it on the UOW.
1048     */

1049    public void buildAttributesIntoShallowObject(Object JavaDoc original, AbstractRecord databaseRow, ObjectBuildingQuery query) throws DatabaseException, QueryException {
1050        AbstractSession executionSession = query.getSession().getExecutionSession(query);
1051
1052        // PERF: Avoid synchronized enumerator as is concurrency bottleneck.
1053
Vector pkMappings = getPrimaryKeyMappings();
1054        int mappingsSize = pkMappings.size();
1055        for (int i = 0; i < mappingsSize; i++) {
1056            DatabaseMapping mapping = (DatabaseMapping)pkMappings.get(i);
1057
1058            //if (query.shouldReadMapping(mapping)) {
1059
if (!mapping.isDirectToFieldMapping()) {
1060                mapping.buildShallowOriginalFromRow(databaseRow, original, query, executionSession);
1061            }
1062        }
1063        Vector mappings = getDescriptor().getMappings();
1064        mappingsSize = mappings.size();
1065        for (int i = 0; i < mappingsSize; i++) {
1066            DatabaseMapping mapping = (DatabaseMapping)mappings.get(i);
1067
1068            //if (query.shouldReadMapping(mapping)) {
1069
if (mapping.isDirectToFieldMapping()) {
1070                mapping.buildShallowOriginalFromRow(databaseRow, original, query, executionSession);
1071            }
1072        }
1073    }
1074
1075    /**
1076     * INTERNAL:
1077     * For reading through the write connection when in transaction,
1078     * populate the clone directly from the database row.
1079     */

1080    public void buildAttributesIntoWorkingCopyClone(Object JavaDoc clone, ObjectBuildingQuery query, JoinedAttributeManager joinManager, AbstractRecord databaseRow, UnitOfWorkImpl unitOfWork, boolean forRefresh) throws DatabaseException, QueryException {
1081        AbstractSession executionSession = unitOfWork;
1082        //execution session is UnitOfWork as these objects are being built within
1083
//the unit of work
1084
Vector mappings = getDescriptor().getMappings();
1085        int mappingsSize = mappings.size();
1086        for (int i = 0; i < mappingsSize; i++) {
1087            DatabaseMapping mapping = (DatabaseMapping)mappings.get(i);
1088            if (query.shouldReadMapping(mapping)) {
1089                mapping.buildCloneFromRow(databaseRow, joinManager, clone, query, unitOfWork, executionSession);
1090            }
1091        }
1092
1093        // PERF: Avoid events if no listeners.
1094
if (getDescriptor().getEventManager().hasAnyEventListeners()) {
1095            // Need to run post build or refresh selector, currently check with the query for this,
1096
// I'm not sure which should be called it case of refresh building a new object, currently refresh is used...
1097
oracle.toplink.essentials.descriptors.DescriptorEvent event = new oracle.toplink.essentials.descriptors.DescriptorEvent(clone);
1098            event.setQuery(query);
1099            event.setSession(query.getSession());
1100            event.setRecord(databaseRow);
1101            if (forRefresh) {
1102                event.setEventCode(DescriptorEventManager.PostRefreshEvent);
1103            } else {
1104                event.setEventCode(DescriptorEventManager.PostBuildEvent);
1105            }
1106            getDescriptor().getEventManager().executeEvent(event);
1107        }
1108    }
1109
1110    /**
1111     * INTERNAL:
1112     * Builds a working copy clone directly from the database row.
1113     * This is the key method that allows us to execute queries against a
1114     * UnitOfWork while in transaction and not cache the results in the shared
1115     * cache. This is because we might violate transaction isolation by
1116     * putting uncommitted versions of objects in the shared cache.
1117     */

1118    protected Object JavaDoc buildWorkingCopyCloneFromRow(ObjectBuildingQuery query, JoinedAttributeManager joinManager, AbstractRecord databaseRow, UnitOfWorkImpl unitOfWork, Vector primaryKey) throws DatabaseException, QueryException {
1119        // If the clone already exists then it may only need to be refreshed or returned.
1120
// We call directly on the identity map to avoid going to the parent,
1121
// registering if found, and wrapping the result.
1122
Object JavaDoc workingClone = unitOfWork.getIdentityMapAccessorInstance().getIdentityMapManager().getFromIdentityMap(primaryKey, getDescriptor().getJavaClass(), getDescriptor());
1123
1124        // If there is a clone, and it is not a refresh then just return it.
1125
boolean wasAClone = workingClone != null;
1126        boolean isARefresh = query.shouldRefreshIdentityMapResult() || (query.isLockQuery() && (!wasAClone || !query.isClonePessimisticLocked(workingClone, unitOfWork)));
1127        if (wasAClone && (!isARefresh)) {
1128            return workingClone;
1129        }
1130
1131        boolean wasAnOriginal = false;
1132        Object JavaDoc original = null;
1133
1134        // If not refreshing can get the object from the cache.
1135
if (!isARefresh && !unitOfWork.shouldReadFromDB()) {
1136            CacheKey originalCacheKey = unitOfWork.getParentIdentityMapSession(query).getIdentityMapAccessorInstance().getCacheKeyForObject(primaryKey, getDescriptor().getJavaClass(), getDescriptor());
1137            if (originalCacheKey != null) {
1138                //bug 4772232- acquire readlock on cachekey then release to ensure object is fully built before being returned
1139
try {
1140                    originalCacheKey.acquireReadLock();
1141                    original = originalCacheKey.getObject();
1142                } finally {
1143                    originalCacheKey.releaseReadLock();
1144                }
1145                wasAnOriginal = original != null;
1146                // If the original is invalid or always refresh then need to refresh.
1147
isARefresh = wasAnOriginal && (getDescriptor().shouldAlwaysRefreshCache() || getDescriptor().getCacheInvalidationPolicy().isInvalidated(originalCacheKey, query.getExecutionTime()));
1148                // Otherwise can just register the cached original object and return it.
1149
if (wasAnOriginal && (!isARefresh)) {
1150                    return unitOfWork.cloneAndRegisterObject(original, originalCacheKey, joinManager);
1151                }
1152            }
1153        }
1154
1155        CacheKey unitOfWorkCacheKey = null;
1156        if (!wasAClone) {
1157            // This code is copied from UnitOfWork.cloneAndRegisterObject. Unlike
1158
// that method we don't need to lock the shared cache, because
1159
// are not building off of an original in the shared cache.
1160
// The copy policy is easier to invoke if we have an original.
1161
if (wasAnOriginal) {
1162                workingClone = instantiateWorkingCopyClone(original, unitOfWork);
1163                // intentionally put nothing in clones to originals, unless really was one.
1164
unitOfWork.getCloneToOriginals().put(workingClone, original);
1165            } else {
1166                // What happens if a copy policy is defined is not pleasant.
1167
workingClone = instantiateWorkingCopyCloneFromRow(databaseRow, query);
1168            }
1169
1170            // This must be registered before it is built to avoid cycles.
1171
// The version and read is set below in copyQueryInfoToCacheKey.
1172
unitOfWorkCacheKey = unitOfWork.getIdentityMapAccessorInstance().internalPutInIdentityMap(workingClone, primaryKey, null, 0, getDescriptor());
1173
1174            // This must be registered before it is built to avoid cycles.
1175
unitOfWork.getCloneMapping().put(workingClone, workingClone);
1176        }
1177
1178        // Since there is no original cache key, we may need all the
1179
// info for the shared cache key in the UnitOfWork cache key.
1180
// PERF: Only lookup cache-key if did not just put it there.
1181
if (unitOfWorkCacheKey == null) {
1182            unitOfWorkCacheKey = unitOfWork.getIdentityMapAccessorInstance().getCacheKeyForObject(primaryKey, getDescriptor().getJavaClass(), getDescriptor());
1183        }
1184
1185        // Must avoid infinite loops while refreshing.
1186
if (wasAClone && (unitOfWorkCacheKey.getLastUpdatedQueryId() >= query.getQueryId())) {
1187            return workingClone;
1188        }
1189        copyQueryInfoToCacheKey(unitOfWorkCacheKey, query, databaseRow, unitOfWork, getDescriptor());
1190
1191        // If it was a clone the change listener must be cleared after.
1192
if (!wasAClone) {
1193            // The change listener must be set before building the clone as aggregate/collections need the listener.
1194
descriptor.getObjectChangePolicy().setChangeListener(workingClone, unitOfWork, getDescriptor());
1195        }
1196
1197        // Turn it 'off' to prevent unwanted events.
1198
descriptor.getObjectChangePolicy().dissableEventProcessing(workingClone);
1199        // Build/refresh the clone from the row.
1200
buildAttributesIntoWorkingCopyClone(workingClone, query, joinManager, databaseRow, unitOfWork, wasAClone);
1201        Object JavaDoc backupClone = getDescriptor().getObjectChangePolicy().buildBackupClone(workingClone, this, unitOfWork);
1202
1203        // If it was a clone the change listener must be cleared.
1204
if (wasAClone) {
1205            descriptor.getObjectChangePolicy().clearChanges(workingClone, unitOfWork, getDescriptor());
1206        }
1207        descriptor.getObjectChangePolicy().enableEventProcessing(workingClone);
1208        unitOfWork.getCloneMapping().put(workingClone, backupClone);
1209        query.recordCloneForPessimisticLocking(workingClone, unitOfWork);
1210
1211        return workingClone;
1212    }
1213
1214    /**
1215     * Returns clone of itself
1216     */

1217    public Object JavaDoc clone() {
1218        Object JavaDoc object = null;
1219
1220        try {
1221            object = super.clone();
1222        } catch (Exception JavaDoc exception) {
1223            ;
1224        }
1225
1226        // Only the shallow copy is created. The entries never change in these data structures
1227
((ObjectBuilder)object).setMappingsByAttribute(new HashMap(getMappingsByAttribute()));
1228        ((ObjectBuilder)object).setMappingsByField(new HashMap(getMappingsByField()));
1229        ((ObjectBuilder)object).setReadOnlyMappingsByField(new HashMap(getReadOnlyMappingsByField()));
1230        ((ObjectBuilder)object).setPrimaryKeyMappings((Vector)getPrimaryKeyMappings().clone());
1231        ((ObjectBuilder)object).setNonPrimaryKeyMappings((Vector)getNonPrimaryKeyMappings().clone());
1232
1233        return object;
1234    }
1235
1236    /**
1237     * INTERNAL:
1238     * THis method is used by the UnitOfWork to cascade registration of new objects.
1239     * It may rais exceptions as described inthe EJB 3.x specification
1240     */

1241    public void cascadePerformRemove(Object JavaDoc object, UnitOfWorkImpl uow, IdentityHashtable visitedObjects) {
1242        Iterator mappings = getDescriptor().getMappings().iterator();
1243        while (mappings.hasNext()) {
1244            ((DatabaseMapping)mappings.next()).cascadePerformRemoveIfRequired(object, uow, visitedObjects);
1245        }
1246    }
1247
1248    /**
1249     * INTERNAL:
1250     * THis method is used by the UnitOfWork to cascade registration of new objects.
1251     * It may rais exceptions as described inthe EJB 3.x specification
1252     */

1253    public void cascadeRegisterNewForCreate(Object JavaDoc object, UnitOfWorkImpl uow, IdentityHashtable visitedObjects) {
1254        Iterator mappings = getDescriptor().getMappings().iterator();
1255        while (mappings.hasNext()) {
1256            ((DatabaseMapping)mappings.next()).cascadeRegisterNewIfRequired(object, uow, visitedObjects);
1257        }
1258    }
1259
1260    /**
1261     * INTERNAL:
1262     * This method creates an records changes for a particular object
1263     * @return ChangeRecord
1264     */

1265    public ObjectChangeSet compareForChange(Object JavaDoc clone, Object JavaDoc backUp, UnitOfWorkChangeSet changeSet, AbstractSession session) {
1266        // delegate the change comparision to this objects ObjectChangePolicy - TGW
1267
return descriptor.getObjectChangePolicy().calculateChanges(clone, backUp, changeSet, session, getDescriptor(), true);
1268    }
1269
1270    /**
1271     * Compares the two specified objects
1272     */

1273    public boolean compareObjects(Object JavaDoc firstObject, Object JavaDoc secondObject, AbstractSession session) {
1274        // PERF: Avoid synchronized enumerator as is concurrency bottleneck.
1275
Vector mappings = getDescriptor().getMappings();
1276        for (int index = 0; index < mappings.size(); index++) {
1277            DatabaseMapping mapping = (DatabaseMapping)mappings.get(index);
1278
1279            if (!mapping.compareObjects(firstObject, secondObject, session)) {
1280                Object JavaDoc firstValue = mapping.getAttributeValueFromObject(firstObject);
1281                Object JavaDoc secondValue = mapping.getAttributeValueFromObject(secondObject);
1282                session.log(SessionLog.FINEST, SessionLog.QUERY, "compare_failed", mapping, firstValue, secondValue);
1283                return false;
1284            }
1285        }
1286
1287        return true;
1288    }
1289
1290    /**
1291     * Copy each attribute from one object into the other.
1292     */

1293    public void copyInto(Object JavaDoc source, Object JavaDoc target) {
1294        // PERF: Avoid synchronized enumerator as is concurrency bottleneck.
1295
Vector mappings = getDescriptor().getMappings();
1296        for (int index = 0; index < mappings.size(); index++) {
1297            DatabaseMapping mapping = (DatabaseMapping)mappings.get(index);
1298            mapping.setAttributeValueInObject(target, mapping.getAttributeValueFromObject(source));
1299        }
1300    }
1301
1302    /**
1303     * Return a copy of the object.
1304     * This is NOT used for unit of work but for templatizing an object.
1305     * The depth and primary key reseting are passed in.
1306     */

1307    public Object JavaDoc copyObject(Object JavaDoc original, ObjectCopyingPolicy policy) {
1308        Object JavaDoc copy = policy.getCopies().get(original);
1309        if (copy != null) {
1310            return copy;
1311        }
1312
1313        copy = instantiateClone(original, policy.getSession());
1314        policy.getCopies().put(original, copy);
1315
1316        // PERF: Avoid synchronized enumerator as is concurrency bottleneck.
1317
List mappings = getCloningMappings();
1318        for (int index = 0; index < mappings.size(); index++) {
1319            ((DatabaseMapping)mappings.get(index)).buildCopy(copy, original, policy);
1320        }
1321
1322        if (policy.shouldResetPrimaryKey() && (!(getDescriptor().isAggregateDescriptor() || getDescriptor().isAggregateCollectionDescriptor()))) {
1323            // Do not reset if any of the keys is mapped through a 1-1, i.e. back reference id has already changed.
1324
boolean hasOneToOne = false;
1325            for (Enumeration keyMappingsEnum = getPrimaryKeyMappings().elements();
1326                     keyMappingsEnum.hasMoreElements();) {
1327                if (((DatabaseMapping)keyMappingsEnum.nextElement()).isOneToOneMapping()) {
1328                    hasOneToOne = true;
1329                }
1330            }
1331            if (!hasOneToOne) {
1332                for (Enumeration keyMappingsEnum = getPrimaryKeyMappings().elements();
1333                         keyMappingsEnum.hasMoreElements();) {
1334                    DatabaseMapping mapping = (DatabaseMapping)keyMappingsEnum.nextElement();
1335
1336                    // Only null out direct mappings, as others will be nulled in the respective objects.
1337
if (mapping.isDirectToFieldMapping()) {
1338                        Object JavaDoc nullValue = ((AbstractDirectMapping)mapping).getAttributeValue(null, policy.getSession());
1339                        mapping.setAttributeValueInObject(copy, nullValue);
1340                    } else if (mapping.isTransformationMapping()) {
1341                        mapping.setAttributeValueInObject(copy, null);
1342                    }
1343                }
1344            }
1345        }
1346
1347        // PERF: Avoid events if no listeners.
1348
if (getDescriptor().getEventManager().hasAnyEventListeners()) {
1349            oracle.toplink.essentials.descriptors.DescriptorEvent event = new oracle.toplink.essentials.descriptors.DescriptorEvent(copy);
1350            event.setSession(policy.getSession());
1351            event.setOriginalObject(original);
1352            event.setEventCode(DescriptorEventManager.PostCloneEvent);
1353            getDescriptor().getEventManager().executeEvent(event);
1354        }
1355
1356        return copy;
1357    }
1358
1359    /**
1360     * INTERNAL:
1361     * Used by the ObjectBuilder to create an ObjectChangeSet for the specified clone object
1362     * @return oracle.toplink.essentials.internal.sessions.ObjectChangeSet the newly created changeSet representing the clone object
1363     * @param clone java.lang.Object the object to convert to a changeSet
1364     * @param uowChangeSet oracle.toplink.essentials.internal.sessions.UnitOfWorkChangeSet the owner of this changeSet
1365     */

1366    public ObjectChangeSet createObjectChangeSet(Object JavaDoc clone, UnitOfWorkChangeSet uowChangeSet, AbstractSession session) {
1367        boolean isNew = ((UnitOfWorkImpl)session).isObjectNew(clone);
1368        return createObjectChangeSet(clone, uowChangeSet, isNew, session);
1369    }
1370
1371    /**
1372     * INTERNAL:
1373     * Used by the ObjectBuilder to create an ObjectChangeSet for the specified clone object
1374     * @return oracle.toplink.essentials.internal.sessions.ObjectChangeSet the newly created changeSet representing the clone object
1375     * @param clone java.lang.Object the object to convert to a changeSet
1376     * @param uowChangeSet oracle.toplink.essentials.internal.sessions.UnitOfWorkChangeSet the owner of this changeSet
1377     * @param isNew boolean signifies if the clone object is a new object.
1378     */

1379    public ObjectChangeSet createObjectChangeSet(Object JavaDoc clone, UnitOfWorkChangeSet uowChangeSet, boolean isNew, AbstractSession session) {
1380        ObjectChangeSet changes = (ObjectChangeSet)uowChangeSet.getObjectChangeSetForClone(clone);
1381        if (changes == null) {
1382            if (getDescriptor().isAggregateDescriptor()) {
1383                changes = new AggregateObjectChangeSet(new Vector(0), clone, uowChangeSet, isNew);
1384            } else {
1385                changes = new ObjectChangeSet(extractPrimaryKeyFromObject(clone, session), clone, uowChangeSet, isNew);
1386            }
1387            changes.setIsAggregate(getDescriptor().isAggregateDescriptor() || getDescriptor().isAggregateCollectionDescriptor());
1388            uowChangeSet.addObjectChangeSetForIdentity(changes, clone);
1389        }
1390        return changes;
1391    }
1392
1393    /**
1394     * Creates and stores primary key expression.
1395     */

1396    public void createPrimaryKeyExpression(AbstractSession session) {
1397        Expression expression = null;
1398        Expression builder = new ExpressionBuilder();
1399        Expression subExp1;
1400        Expression subExp2;
1401        Expression subExpression;
1402        List primaryKeyFields = getDescriptor().getPrimaryKeyFields();
1403
1404        for (int index = 0; index < primaryKeyFields.size(); index++) {
1405            DatabaseField primaryKeyField = (DatabaseField)primaryKeyFields.get(index);
1406            subExp1 = builder.getField(primaryKeyField);
1407            subExp2 = builder.getParameter(primaryKeyField);
1408            subExpression = subExp1.equal(subExp2);
1409
1410            if (expression == null) {
1411                expression = subExpression;
1412            } else {
1413                expression = expression.and(subExpression);
1414            }
1415        }
1416
1417        setPrimaryKeyExpression(expression);
1418    }
1419
1420    /**
1421     * Return the row with primary keys and their values from the given expression.
1422     */

1423    public Vector extractPrimaryKeyFromExpression(boolean requiresExactMatch, Expression expression, AbstractRecord translationRow, AbstractSession session) {
1424        AbstractRecord primaryKeyRow = createRecord(getPrimaryKeyMappings().size());
1425
1426        //put the curent session onthe expression builder for use later store current session incase
1427
//it is required at a later stage
1428
AbstractSession oldSession = expression.getBuilder().getSession();
1429        expression.getBuilder().setSession(session);
1430        // Get all the field & values from expression.
1431
boolean isValid = expression.extractPrimaryKeyValues(requiresExactMatch, getDescriptor(), primaryKeyRow, translationRow);
1432        expression.getBuilder().setSession(oldSession);
1433        if (requiresExactMatch && (!isValid)) {
1434            return null;
1435        }
1436
1437        // Check that the sizes match.
1438
if (primaryKeyRow.size() != getDescriptor().getPrimaryKeyFields().size()) {
1439            return null;
1440        }
1441
1442        return extractPrimaryKeyFromRow(primaryKeyRow, session);
1443    }
1444
1445    /**
1446     * Extract primary key attribute values from the domainObject.
1447     */

1448    public Vector extractPrimaryKeyFromObject(Object JavaDoc domainObject, AbstractSession session) {
1449        // Allow for inheritance, the concrete descriptor must always be used.
1450
if (getDescriptor().hasInheritance() && (domainObject.getClass() != getDescriptor().getJavaClass()) && (!domainObject.getClass().getSuperclass().equals(getDescriptor().getJavaClass()))) {
1451            return session.getDescriptor(domainObject).getObjectBuilder().extractPrimaryKeyFromObject(domainObject, session);
1452        } else {
1453            List primaryKeyFields = getDescriptor().getPrimaryKeyFields();
1454            Vector primaryKeyValues = new Vector(primaryKeyFields.size());
1455
1456            // PERF: optimize simple case of direct mapped singleton primary key.
1457
if (getDescriptor().hasSimplePrimaryKey()) {
1458                // PERF: use index not enumeration
1459
for (int index = 0; index < getPrimaryKeyMappings().size(); index++) {
1460                    AbstractDirectMapping mapping = (AbstractDirectMapping)getPrimaryKeyMappings().get(index);
1461                    Object JavaDoc keyValue = mapping.valueFromObject(domainObject, (DatabaseField)primaryKeyFields.get(index), session);
1462                    primaryKeyValues.add(keyValue);
1463                }
1464            } else {
1465                AbstractRecord databaseRow = createRecord(getPrimaryKeyMappings().size());
1466
1467                // PERF: use index not enumeration
1468
for (int index = 0; index < getPrimaryKeyMappings().size(); index++) {
1469                    DatabaseMapping mapping = (DatabaseMapping)getPrimaryKeyMappings().get(index);
1470
1471                    // Primary key mapping may be null for aggregate collection.
1472
if (mapping != null) {
1473                        mapping.writeFromObjectIntoRow(domainObject, databaseRow, session);
1474                    }
1475                }
1476
1477                // PERF: use index not enumeration
1478
for (int index = 0; index < primaryKeyFields.size(); index++) {
1479                    // Ensure that the type extracted from the object is the same type as in the descriptor,
1480
// the main reason for this is that 1-1 can optimize on vh by getting from the row as the row-type.
1481
Class JavaDoc classification = (Class JavaDoc)getPrimaryKeyClassifications().get(index);
1482                    Object JavaDoc value = databaseRow.get((DatabaseField)primaryKeyFields.get(index));
1483
1484                    // CR2114 following line modified; domainObject.getClass() passed as an argument
1485
primaryKeyValues.addElement(session.getPlatform(domainObject.getClass()).convertObject(value, classification));
1486                }
1487            }
1488
1489            return primaryKeyValues;
1490        }
1491    }
1492
1493    /**
1494     * Extract primary key values from the specified row.
1495     * null is returned if the row does not contain the key.
1496     */

1497    public Vector extractPrimaryKeyFromRow(AbstractRecord databaseRow, AbstractSession session) {
1498        List primaryKeyFields = getDescriptor().getPrimaryKeyFields();
1499        Vector primaryKeyValues = new Vector(primaryKeyFields.size());
1500
1501        // PERF: use index not enumeration
1502
for (int index = 0; index < primaryKeyFields.size(); index++) {
1503            DatabaseField field = (DatabaseField)primaryKeyFields.get(index);
1504
1505            // Ensure that the type extracted from the row is the same type as in the object.
1506
Class JavaDoc classification = (Class JavaDoc)getPrimaryKeyClassifications().get(index);
1507            Object JavaDoc value = databaseRow.get(field);
1508            if (value != null) {
1509                primaryKeyValues.addElement(session.getPlatform(getDescriptor().getJavaClass()).convertObject(value, classification));
1510            } else {
1511                return null;
1512            }
1513        }
1514
1515        return primaryKeyValues;
1516    }
1517
1518    /**
1519     * Return the row with primary keys and their values from the given expression.
1520     */

1521    public AbstractRecord extractPrimaryKeyRowFromExpression(Expression expression, AbstractRecord translationRow, AbstractSession session) {
1522        AbstractRecord primaryKeyRow = createRecord(getPrimaryKeyMappings().size());
1523
1524        //put the curent session onthe expression builder for use later store current session incase
1525
//it is required at a later stage
1526
AbstractSession oldSession = expression.getBuilder().getSession();
1527        expression.getBuilder().setSession(session);
1528        // Get all the field & values from expression
1529
boolean isValid = expression.extractPrimaryKeyValues(true, getDescriptor(), primaryKeyRow, translationRow);
1530        expression.getBuilder().setSession(session);
1531        if (!isValid) {
1532            return null;
1533        }
1534
1535        // Check that the sizes match up
1536
if (primaryKeyRow.size() != getDescriptor().getPrimaryKeyFields().size()) {
1537            return null;
1538        }
1539
1540        return primaryKeyRow;
1541    }
1542
1543    /**
1544     * Extract primary key attribute values from the domainObject.
1545     */

1546    public AbstractRecord extractPrimaryKeyRowFromObject(Object JavaDoc domainObject, AbstractSession session) {
1547        AbstractRecord databaseRow = createRecord(getPrimaryKeyMappings().size());
1548
1549        // PERF: use index not enumeration.
1550
for (int index = 0; index < getPrimaryKeyMappings().size(); index++) {
1551            ((DatabaseMapping)getPrimaryKeyMappings().get(index)).writeFromObjectIntoRow(domainObject, databaseRow, session);
1552        }
1553
1554        // PERF: optimize simple primary key case, no need to remap.
1555
if (getDescriptor().hasSimplePrimaryKey()) {
1556            return databaseRow;
1557        }
1558        AbstractRecord primaryKeyRow = createRecord(getPrimaryKeyMappings().size());
1559        List primaryKeyFields = getDescriptor().getPrimaryKeyFields();
1560        for (int index = 0; index < primaryKeyFields.size(); index++) {
1561            // Ensure that the type extracted from the object is the same type as in the descriptor,
1562
// the main reason for this is that 1-1 can optimize on vh by getting from the row as the row-type.
1563
Class JavaDoc classification = (Class JavaDoc)getPrimaryKeyClassifications().get(index);
1564            DatabaseField field = (DatabaseField)primaryKeyFields.get(index);
1565            Object JavaDoc value = databaseRow.get(field);
1566            primaryKeyRow.put(field, session.getPlatform(domainObject.getClass()).convertObject(value, classification));
1567        }
1568
1569        return primaryKeyRow;
1570    }
1571
1572    /**
1573     * Extract the value of the primary key attribute from the specified object.
1574     */

1575    public Object JavaDoc extractValueFromObjectForField(Object JavaDoc domainObject, DatabaseField field, AbstractSession session) throws DescriptorException {
1576        // Allow for inheritance, the concrete descriptor must always be used.
1577
ClassDescriptor descriptor = null;//this variable will be assigned in the final
1578

1579        if (getDescriptor().hasInheritance() && (domainObject.getClass() != getDescriptor().getJavaClass()) && ((descriptor = session.getDescriptor(domainObject)).getJavaClass() != getDescriptor().getJavaClass())) {
1580            return descriptor.getObjectBuilder().extractValueFromObjectForField(domainObject, field, session);
1581        } else {
1582            DatabaseMapping mapping = getMappingForField(field);
1583            if (mapping == null) {
1584                throw DescriptorException.missingMappingForField(field, getDescriptor());
1585            }
1586
1587            return mapping.valueFromObject(domainObject, field, session);
1588        }
1589    }
1590
1591    /**
1592     * Return the base mapping for the given DatabaseField.
1593     */

1594    public DatabaseMapping getBaseMappingForField(DatabaseField databaseField) {
1595        DatabaseMapping mapping = getMappingForField(databaseField);
1596
1597        // Drill down through the mappings until we get the direct mapping to the databaseField.
1598
while (mapping.isAggregateObjectMapping()) {
1599            mapping = ((AggregateObjectMapping)mapping).getReferenceDescriptor().getObjectBuilder().getMappingForField(databaseField);
1600        }
1601        return mapping;
1602    }
1603
1604    /**
1605     * Return the base value that is mapped to for given field.
1606     */

1607    public Object JavaDoc getBaseValueForField(DatabaseField databaseField, Object JavaDoc domainObject) {
1608        Object JavaDoc valueIntoObject = domainObject;
1609        DatabaseMapping mapping = getMappingForField(databaseField);
1610
1611        // Drill down through the aggregate mappings to get to the direct to field mapping.
1612
while (mapping.isAggregateObjectMapping()) {
1613            valueIntoObject = mapping.getAttributeValueFromObject(valueIntoObject);
1614            mapping = ((AggregateMapping)mapping).getReferenceDescriptor().getObjectBuilder().getMappingForField(databaseField);
1615        }
1616        return mapping.getAttributeValueFromObject(valueIntoObject);
1617    }
1618
1619    /**
1620     * Return the descriptor
1621     */

1622    public ClassDescriptor getDescriptor() {
1623        return descriptor;
1624    }
1625
1626    /**
1627     * INTERNAL:
1628     * Return the classifiction for the field contained in the mapping.
1629     * This is used to convert the row value to a consistent java value.
1630     */

1631    public Class JavaDoc getFieldClassification(DatabaseField fieldToClassify) throws DescriptorException {
1632        DatabaseMapping mapping = getMappingForField(fieldToClassify);
1633        if (mapping == null) {
1634            // Means that the mapping is read-only or the classification is unknown,
1635
// this is normally not an issue as the classification is only really used for primary keys
1636
// and only when the database type can be different and not polymorphic than the object type.
1637
return null;
1638        }
1639
1640        return mapping.getFieldClassification(fieldToClassify);
1641    }
1642
1643    /**
1644     * Return the field used for the query key name.
1645     */

1646    public DatabaseField getFieldForQueryKeyName(String JavaDoc name) {
1647        QueryKey key = getDescriptor().getQueryKeyNamed(name);
1648        if (key == null) {
1649            DatabaseMapping mapping = getMappingForAttributeName(name);
1650            if (mapping == null) {
1651                return null;
1652            }
1653            if (mapping.getFields().isEmpty()) {
1654                return null;
1655            }
1656            return (DatabaseField)mapping.getFields().firstElement();
1657        }
1658        if (key.isDirectQueryKey()) {
1659            return ((DirectQueryKey)key).getField();
1660        }
1661        return null;
1662    }
1663
1664    /**
1665     * PERF:
1666     * Return all mappings that require cloning.
1667     * This allows for simple directs to be avoided when using clone copying.
1668     */

1669    public List getCloningMappings() {
1670        return cloningMappings;
1671    }
1672
1673    /**
1674     * INTERNAL:
1675     * Answers the attributes which are always joined to the original query on reads.
1676     */

1677    public Vector getJoinedAttributes() {
1678        return joinedAttributes;
1679    }
1680
1681    /**
1682     * INTERNAL:
1683     * Answers if any attributes are to be joined / returned in the same select
1684     * statement.
1685     */

1686    public boolean hasJoinedAttributes() {
1687        return (joinedAttributes != null);
1688    }
1689
1690    /**
1691     * Return the mapping for the specified attribute name.
1692     */

1693    public DatabaseMapping getMappingForAttributeName(String JavaDoc name) {
1694        return (DatabaseMapping)getMappingsByAttribute().get(name);
1695    }
1696
1697    /**
1698     * Return al the mapping for the specified field.
1699     */

1700    public DatabaseMapping getMappingForField(DatabaseField field) {
1701        return (DatabaseMapping)getMappingsByField().get(field);
1702    }
1703
1704    /**
1705     * Return all the read-only mapping for the specified field.
1706     */

1707    public Vector getReadOnlyMappingsForField(DatabaseField field) {
1708        return (Vector)getReadOnlyMappingsByField().get(field);
1709    }
1710
1711    /**
1712     * Return all the mapping to attribute associations
1713     */

1714    protected Map getMappingsByAttribute() {
1715        return mappingsByAttribute;
1716    }
1717
1718    /**
1719     * INTERNAL:
1720     * Return all the mapping to field associations
1721     */

1722    public Map getMappingsByField() {
1723        return mappingsByField;
1724    }
1725
1726    /**
1727     * INTERNAL:
1728     * Return all the read-only mapping to field associations
1729     */

1730    public Map getReadOnlyMappingsByField() {
1731        return readOnlyMappingsByField;
1732    }
1733
1734    /**
1735     * Return the non primary key mappings.
1736     */

1737    protected Vector getNonPrimaryKeyMappings() {
1738        return nonPrimaryKeyMappings;
1739    }
1740
1741    /**
1742     * Return the base value that is mapped to for given field.
1743     */

1744    public Object JavaDoc getParentObjectForField(DatabaseField databaseField, Object JavaDoc domainObject) {
1745        Object JavaDoc valueIntoObject = domainObject;
1746        DatabaseMapping mapping = getMappingForField(databaseField);
1747
1748        // Drill down through the aggregate mappings to get to the direct to field mapping.
1749
while (mapping.isAggregateObjectMapping()) {
1750            valueIntoObject = mapping.getAttributeValueFromObject(valueIntoObject);
1751            mapping = ((AggregateMapping)mapping).getReferenceDescriptor().getObjectBuilder().getMappingForField(databaseField);
1752        }
1753        return valueIntoObject;
1754    }
1755
1756    /**
1757     * Return primary key classifications.
1758     * These are used to ensure a consistent type for the pk values.
1759     */

1760    public Vector getPrimaryKeyClassifications() {
1761        if (primaryKeyClassifications == null) {
1762            List primaryKeyFields = getDescriptor().getPrimaryKeyFields();
1763            Vector classifications = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(primaryKeyFields.size());
1764
1765            for (int index = 0; index < primaryKeyFields.size(); index++) {
1766                DatabaseMapping mapping = (DatabaseMapping)getPrimaryKeyMappings().get(index);
1767                DatabaseField field = (DatabaseField)primaryKeyFields.get(index);
1768                if (mapping != null) {
1769                    classifications.add(Helper.getObjectClass(mapping.getFieldClassification(field)));
1770                } else {
1771                    classifications.add(null);
1772                }
1773                primaryKeyClassifications = classifications;
1774            }
1775        }
1776        return primaryKeyClassifications;
1777    }
1778
1779    /**
1780     * Return the primary key expression
1781     */

1782    public Expression getPrimaryKeyExpression() {
1783        return primaryKeyExpression;
1784    }
1785
1786    /**
1787     * Return primary key mappings.
1788     */

1789    public Vector getPrimaryKeyMappings() {
1790        return primaryKeyMappings;
1791    }
1792
1793    /**
1794     * INTERNAL: return a database field based on a query key name
1795     */

1796    public DatabaseField getTargetFieldForQueryKeyName(String JavaDoc queryKeyName) {
1797        DatabaseMapping mapping = (DatabaseMapping)getMappingForAttributeName(queryKeyName);
1798        if ((mapping != null) && mapping.isDirectToFieldMapping()) {
1799            return ((AbstractDirectMapping)mapping).getField();
1800        }
1801
1802        //mapping is either null or not direct to field.
1803
//check query keys
1804
QueryKey queryKey = getDescriptor().getQueryKeyNamed(queryKeyName);
1805        if ((queryKey != null) && queryKey.isDirectQueryKey()) {
1806            return ((DirectQueryKey)queryKey).getField();
1807        }
1808
1809        //nothing found
1810
return null;
1811    }
1812
1813    /**
1814     * Cache all the mappings by their attribute and fields.
1815     */

1816    public void initialize(AbstractSession session) throws DescriptorException {
1817        this.getMappingsByField().clear();
1818        this.getReadOnlyMappingsByField().clear();
1819        this.getMappingsByAttribute().clear();
1820        this.getCloningMappings().clear();
1821
1822        for (Enumeration mappings = getDescriptor().getMappings().elements();
1823                 mappings.hasMoreElements();) {
1824            DatabaseMapping mapping = (DatabaseMapping)mappings.nextElement();
1825
1826            // Add attribute to mapping association
1827
if (!mapping.isWriteOnly()) {
1828                getMappingsByAttribute().put(mapping.getAttributeName(), mapping);
1829            }
1830            if (mapping.isCloningRequired()) {
1831                getCloningMappings().add(mapping);
1832            }
1833
1834            // Add field to mapping association
1835
for (Enumeration fields = mapping.getFields().elements(); fields.hasMoreElements();) {
1836                Object JavaDoc field = fields.nextElement();
1837                if (!mapping.isReadOnly()) {
1838                    if (getMappingsByField().containsKey(field)) {
1839                        // We cannot determine if aggregate mapping for the field are read-only, so ignore exception.
1840
if (!mapping.isAggregateObjectMapping()) {
1841                            session.getIntegrityChecker().handleError(DescriptorException.multipleWriteMappingsForField(field.toString(), mapping));
1842                        }
1843                    } else {
1844                        getMappingsByField().put(field, mapping);
1845                    }
1846                } else {
1847                    Vector mappingVector = (Vector)getReadOnlyMappingsByField().get(field);
1848                    if (mappingVector == null) {
1849                        mappingVector = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance();
1850                        getReadOnlyMappingsByField().put(field, mappingVector);
1851                    }
1852                    mappingVector.add(mapping);
1853                }
1854            }
1855        }
1856
1857        initializePrimaryKey(session);
1858
1859        initializeJoinedAttributes();
1860    }
1861
1862    /**
1863     * INTERNAL:
1864     * Iterates through all one to one mappings and checks if any of them use joining.
1865     * <p>
1866     * By caching the result query execution in the case where there are no joined
1867     * attributes can be improved.
1868     */

1869    public void initializeJoinedAttributes() {
1870        // For concurrency don't worry about doing this work twice, just make sure
1871
// if it happens don't add the same joined attributes twice.
1872
Vector joinedAttributes = null;
1873        Vector mappings = getDescriptor().getMappings();
1874        for (int i = 0; i < mappings.size(); i++) {
1875            DatabaseMapping mapping = (DatabaseMapping)mappings.get(i);
1876            if (mapping.isOneToOneMapping() && (mapping.isRelationalMapping()) && ((OneToOneMapping)mapping).shouldUseJoining()) {
1877                if (joinedAttributes == null) {
1878                    joinedAttributes = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance();
1879                }
1880                joinedAttributes.add(mapping.getAttributeName());
1881            }
1882        }
1883        this.joinedAttributes = joinedAttributes;
1884    }
1885
1886    /**
1887     * Initialize a cache key. Called by buildObject and now also by
1888     * buildWorkingCopyCloneFromRow.
1889     */

1890    protected void copyQueryInfoToCacheKey(CacheKey cacheKey, ObjectBuildingQuery query, AbstractRecord databaseRow, AbstractSession session, ClassDescriptor concreteDescriptor) {
1891        //CR #4365 - used to prevent infinit recursion on refresh object cascade all
1892
cacheKey.setLastUpdatedQueryId(query.getQueryId());
1893
1894        if (concreteDescriptor.usesOptimisticLocking()) {
1895            OptimisticLockingPolicy policy = concreteDescriptor.getOptimisticLockingPolicy();
1896            Object JavaDoc cacheValue = policy.getValueToPutInCache(databaseRow, session);
1897
1898            //register the object into the IM and set the write lock object
1899
cacheKey.setWriteLockValue(cacheValue);
1900        }
1901        cacheKey.setReadTime(query.getExecutionTime());
1902    }
1903
1904    /**
1905     * Cache primary key and non primary key mappings.
1906     */

1907    public void initializePrimaryKey(AbstractSession session) throws DescriptorException {
1908        createPrimaryKeyExpression(session);
1909
1910        List primaryKeyfields = getDescriptor().getPrimaryKeyFields();
1911
1912        this.getPrimaryKeyMappings().clear();
1913        this.getNonPrimaryKeyMappings().clear();
1914
1915        // This must be before because the scondary table primary key fields are registered after
1916
for (Iterator fields = getMappingsByField().keySet().iterator(); fields.hasNext();) {
1917            DatabaseField field = (DatabaseField)fields.next();
1918            if (!primaryKeyfields.contains(field)) {
1919                DatabaseMapping mapping = getMappingForField(field);
1920                if (!getNonPrimaryKeyMappings().contains(mapping)) {
1921                    getNonPrimaryKeyMappings().addElement(mapping);
1922                }
1923            }
1924        }
1925        List primaryKeyFields = getDescriptor().getPrimaryKeyFields();
1926        for (int index = 0; index < primaryKeyFields.size(); index++) {
1927            DatabaseField primaryKeyField = (DatabaseField)primaryKeyFields.get(index);
1928            DatabaseMapping mapping = getMappingForField(primaryKeyField);
1929
1930            if ((mapping == null) && (!getDescriptor().isAggregateDescriptor()) && (!getDescriptor().isAggregateCollectionDescriptor())) {
1931                throw DescriptorException.noMappingForPrimaryKey(primaryKeyField, getDescriptor());
1932            }
1933
1934            getPrimaryKeyMappings().addElement(mapping);
1935            if (mapping != null) {
1936                mapping.setIsPrimaryKeyMapping(true);
1937            }
1938
1939            // Use the same mapping to map the additional table primary key fields.
1940
// This is required if someone trys to map to one of these fields.
1941
if (getDescriptor().hasMultipleTables()) {
1942                for (Iterator secondaryKeysEnum = getDescriptor().getAdditionalTablePrimaryKeyFields().values().iterator();
1943                         secondaryKeysEnum.hasNext();) {
1944                    Map keyMaping = (Map)secondaryKeysEnum.next();
1945                    DatabaseField secondaryField = (DatabaseField)keyMaping.get(primaryKeyField);
1946
1947                    // This can be null in the custom multiple join case
1948
if ((secondaryField != null) && (mapping != null)) {
1949                        getMappingsByField().put(secondaryField, mapping);
1950                    }
1951                }
1952            }
1953        }
1954
1955        // PERF: compute if primary key is mapped through direct mappings,
1956
// to allow fast extraction.
1957
boolean hasSimplePrimaryKey = true;
1958        for (int index = 0; index < getPrimaryKeyMappings().size(); index++) {
1959            DatabaseMapping mapping = (DatabaseMapping)getPrimaryKeyMappings().get(index);
1960
1961            // Primary key mapping may be null for aggregate collection.
1962
if ((mapping == null) || (!mapping.isDirectToFieldMapping())) {
1963                hasSimplePrimaryKey = false;
1964                break;
1965            }
1966        }
1967        getDescriptor().setHasSimplePrimaryKey(hasSimplePrimaryKey);
1968    }
1969
1970    /**
1971     * Returns the clone of the specified object. This is called only from unit of work.
1972     * This only instatiates the clone instance, it does not clone the attributes,
1973     * this allows the stub of the clone to be registered before cloning its parts.
1974     */

1975    public Object JavaDoc instantiateClone(Object JavaDoc domainObject, AbstractSession session) {
1976        return getDescriptor().getCopyPolicy().buildClone(domainObject, session);
1977    }
1978
1979    /**
1980     * Returns the clone of the specified object. This is called only from unit of work.
1981     * The domainObject sent as parameter is always a copy from the parent of unit of work.
1982     * bug 2612602 make a call to build a working clone. This will in turn call the copy policy
1983     * to make a working clone. This allows for lighter and heavier clones to
1984     * be created based on their use.
1985     * this allows the stub of the clone to be registered before cloning its parts.
1986     */

1987    public Object JavaDoc instantiateWorkingCopyClone(Object JavaDoc domainObject, AbstractSession session) {
1988        return getDescriptor().getCopyPolicy().buildWorkingCopyClone(domainObject, session);
1989    }
1990
1991    /**
1992     * It is now possible to build working copy clones directly from rows.
1993     * <p>An intermediary original is no longer needed.
1994     * <p>This has ramifications to the copy policy and cmp, for clones are
1995     * no longer built via cloning.
1996     * <p>Instead the copy policy must in some cases not copy at all.
1997     * this allows the stub of the clone to be registered before cloning its parts.
1998     */

1999    public Object JavaDoc instantiateWorkingCopyCloneFromRow(AbstractRecord row, ObjectBuildingQuery query) {
2000        if (query.isObjectLevelReadQuery()){ //for backward compat reasons cast this
2001
return getDescriptor().getCopyPolicy().buildWorkingCopyCloneFromRow(row, ((ObjectLevelReadQuery)query));
2002        }else{
2003            return getDescriptor().getCopyPolicy().buildWorkingCopyCloneFromRow(row, query);
2004        }
2005    }
2006
2007    public boolean isPrimaryKeyMapping(DatabaseMapping mapping) {
2008        return getPrimaryKeyMappings().contains(mapping);
2009    }
2010
2011    /**
2012     * INTERNAL:
2013     * Perform the itteration opperation on the objects attributes through the mappings.
2014     */

2015    public void iterate(DescriptorIterator iterator) {
2016        // PERF: Avoid synchronized enumerator as is concurrency bottleneck.
2017
Vector mappings = getDescriptor().getMappings();
2018        int mappingsSize = mappings.size();
2019        for (int index = 0; index < mappingsSize; index++) {
2020            ((DatabaseMapping)mappings.get(index)).iterate(iterator);
2021        }
2022    }
2023
2024    /**
2025     * INTERNAL:
2026     * Merge changes between the objects, this merge algorthim is dependent on the merge manager.
2027     */

2028    public void mergeChangesIntoObject(Object JavaDoc target, ObjectChangeSet changeSet, Object JavaDoc source, MergeManager mergeManager) {
2029        for (Enumeration changes = changeSet.getChanges().elements(); changes.hasMoreElements();) {
2030            ChangeRecord record = (ChangeRecord)changes.nextElement();
2031
2032            //cr 4236, use ObjectBuilder getMappingForAttributeName not the Descriptor one because the
2033
// ObjectBuilder method is much more efficient.
2034
DatabaseMapping mapping = getMappingForAttributeName(record.getAttribute());
2035            mapping.mergeChangesIntoObject(target, record, source, mergeManager);
2036        }
2037
2038        // PERF: Avoid events if no listeners.
2039
if (getDescriptor().getEventManager().hasAnyEventListeners()) {
2040            oracle.toplink.essentials.descriptors.DescriptorEvent event = new oracle.toplink.essentials.descriptors.DescriptorEvent(target);
2041            event.setSession(mergeManager.getSession());
2042            event.setOriginalObject(source);
2043            event.setChangeSet(changeSet);
2044            event.setEventCode(DescriptorEventManager.PostMergeEvent);
2045            getDescriptor().getEventManager().executeEvent(event);
2046        }
2047    }
2048
2049    /**
2050     * INTERNAL:
2051     * Merge the contents of one object into another, this merge algorthim is dependent on the merge manager.
2052     * This merge also prevents the extra step of calculating the changes when it is not required.
2053     */

2054    public void mergeIntoObject(Object JavaDoc target, boolean isUnInitialized, Object JavaDoc source, MergeManager mergeManager) {
2055        // PERF: Avoid synchronized enumerator as is concurrency bottleneck.
2056
Vector mappings = getDescriptor().getMappings();
2057        for (int index = 0; index < mappings.size(); index++) {
2058            ((DatabaseMapping)mappings.get(index)).mergeIntoObject(target, isUnInitialized, source, mergeManager);
2059        }
2060
2061        // PERF: Avoid events if no listeners.
2062
if (getDescriptor().getEventManager().hasAnyEventListeners()) {
2063            oracle.toplink.essentials.descriptors.DescriptorEvent event = new oracle.toplink.essentials.descriptors.DescriptorEvent(target);
2064            event.setSession(mergeManager.getSession());
2065            event.setOriginalObject(source);
2066            event.setEventCode(DescriptorEventManager.PostMergeEvent);
2067            getDescriptor().getEventManager().executeEvent(event);
2068        }
2069    }
2070
2071    /**
2072     * Clones the attributes of the specified object. This is called only from unit of work.
2073     * The domainObject sent as parameter is always a copy from the parent of unit of work.
2074     */

2075    public void populateAttributesForClone(Object JavaDoc original, Object JavaDoc clone, UnitOfWorkImpl unitOfWork, JoinedAttributeManager joinedAttributeManager) {
2076        // PERF: Avoid synchronized enumerator as is concurrency bottleneck.
2077
List mappings = getCloningMappings();
2078        for (int index = 0; index < mappings.size(); index++) {
2079            ((DatabaseMapping)mappings.get(index)).buildClone(original, clone, unitOfWork, joinedAttributeManager);
2080        }
2081
2082        // PERF: Avoid events if no listeners.
2083
if (getDescriptor().getEventManager().hasAnyEventListeners()) {
2084            oracle.toplink.essentials.descriptors.DescriptorEvent event = new oracle.toplink.essentials.descriptors.DescriptorEvent(clone);
2085            event.setSession(unitOfWork);
2086            event.setOriginalObject(original);
2087            event.setEventCode(DescriptorEventManager.PostCloneEvent);
2088            getDescriptor().getEventManager().executeEvent(event);
2089        }
2090    }
2091
2092    /**
2093     * Rehash any hashtables based on fields.
2094     * This is used to clone descriptors for aggregates, which hammer field names,
2095     * it is probably better not to hammer the field name and this should be refactored.
2096     */

2097    public void rehashFieldDependancies(AbstractSession session) {
2098        setMappingsByField(Helper.rehashMap(getMappingsByField()));
2099        setReadOnlyMappingsByField(Helper.rehashMap(getReadOnlyMappingsByField()));
2100        setPrimaryKeyMappings(oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(2));
2101        setNonPrimaryKeyMappings(oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(2));
2102        initializePrimaryKey(session);
2103    }
2104
2105    /**
2106     * Set the descriptor.
2107     */

2108    public void setDescriptor(ClassDescriptor aDescriptor) {
2109        descriptor = aDescriptor;
2110    }
2111
2112    /**
2113     * All the mappings and their respective attribute associations are cached for performance improvement.
2114     */

2115    protected void setMappingsByAttribute(Map theAttributeMappings) {
2116        mappingsByAttribute = theAttributeMappings;
2117    }
2118
2119    /**
2120     * INTERNAL:
2121     * All the mappings and their respective field associations are cached for performance improvement.
2122     */

2123    public void setMappingsByField(Map theFieldMappings) {
2124        mappingsByField = theFieldMappings;
2125    }
2126
2127    /**
2128     * INTERNAL:
2129     * All the read-only mappings and their respective field associations are cached for performance improvement.
2130     */

2131    public void setReadOnlyMappingsByField(Map theReadOnlyFieldMappings) {
2132        readOnlyMappingsByField = theReadOnlyFieldMappings;
2133    }
2134
2135    /**
2136     * The non primary key mappings are cached to improve performance.
2137     */

2138    protected void setNonPrimaryKeyMappings(Vector theNonPrimaryKeyMappings) {
2139        nonPrimaryKeyMappings = theNonPrimaryKeyMappings;
2140    }
2141
2142    /**
2143     * Set primary key classifications.
2144     * These are used to ensure a consistent type for the pk values.
2145     */

2146    protected void setPrimaryKeyClassifications(Vector primaryKeyClassifications) {
2147        this.primaryKeyClassifications = primaryKeyClassifications;
2148    }
2149
2150    /**
2151     * The primary key expression is cached to improve performance.
2152     */

2153    public void setPrimaryKeyExpression(Expression criteria) {
2154        primaryKeyExpression = criteria;
2155    }
2156
2157    /**
2158     * The primary key mappings are cached to improve performance.
2159     */

2160    protected void setPrimaryKeyMappings(Vector thePrimaryKeyMappings) {
2161        primaryKeyMappings = thePrimaryKeyMappings;
2162    }
2163
2164    public String JavaDoc toString() {
2165        return Helper.getShortClassName(getClass()) + "(" + getDescriptor().toString() + ")";
2166    }
2167
2168    /**
2169     * Unwrap the object if required.
2170     * This is used for the wrapper policy support and EJB.
2171     */

2172    public Object JavaDoc unwrapObject(Object JavaDoc proxy, AbstractSession session) {
2173        if (proxy == null) {
2174            return null;
2175        }
2176
2177        // Check if already unwrapped.
2178
if ((getDescriptor().getJavaClass() == proxy.getClass()) || !getDescriptor().hasWrapperPolicy() || !getDescriptor().getWrapperPolicy().isWrapped(proxy)) {
2179            return proxy;
2180        }
2181
2182        // Allow for inheritance, the concrete wrapper must always be used.
2183
if (getDescriptor().hasInheritance() && (getDescriptor().getInheritancePolicy().hasChildren())) {
2184            ClassDescriptor descriptor = session.getDescriptor(proxy);
2185            if (descriptor != getDescriptor()) {
2186                return descriptor.getObjectBuilder().unwrapObject(proxy, session);
2187            }
2188        }
2189
2190        if (getDescriptor().hasWrapperPolicy()) {
2191            return getDescriptor().getWrapperPolicy().unwrapObject(proxy, session);
2192        } else {
2193            return proxy;
2194        }
2195    }
2196
2197    /**
2198     * Validates the object builder. This is done once the object builder initialized and descriptor
2199     * fires this validation.
2200     */

2201    public void validate(AbstractSession session) throws DescriptorException {
2202        if (getDescriptor().usesSequenceNumbers()) {
2203            if (getMappingForField(getDescriptor().getSequenceNumberField()) == null) {
2204                throw DescriptorException.mappingForSequenceNumberField(getDescriptor());
2205            }
2206        }
2207    }
2208
2209    /**
2210     * Verify that an object has been deleted from the database.
2211     * An object can span multiple tables. A query is performed on each of
2212     * these tables using the primary key values of the object as the selection
2213     * criteria. If the query returns a result then the object has not been
2214     * deleted from the database. If no result is returned then each of the
2215     * mappings is asked to verify that the object has been deleted. If all mappings
2216     * answer true then the result is true.
2217     */

2218    public boolean verifyDelete(Object JavaDoc object, AbstractSession session) {
2219        AbstractRecord translationRow = buildRowForTranslation(object, session);
2220
2221        // If a call is used generated SQL cannot be executed, the call must be used.
2222
if ((getDescriptor().getQueryManager().getReadObjectQuery() != null) && getDescriptor().getQueryManager().getReadObjectQuery().isCallQuery()) {
2223            Object JavaDoc result = session.readObject(object);
2224            if (result != null) {
2225                return false;
2226            }
2227        } else {
2228            for (Enumeration tables = getDescriptor().getTables().elements();
2229                     tables.hasMoreElements();) {
2230                DatabaseTable table = (DatabaseTable)tables.nextElement();
2231
2232                SQLSelectStatement sqlStatement = new SQLSelectStatement();
2233                sqlStatement.addTable(table);
2234                if (table == getDescriptor().getTables().firstElement()) {
2235                    sqlStatement.setWhereClause((Expression)getPrimaryKeyExpression().clone());
2236                } else {
2237                    sqlStatement.setWhereClause(buildPrimaryKeyExpression(table));
2238                }
2239                DatabaseField all = new DatabaseField("*");
2240                all.setTable(table);
2241                sqlStatement.addField(all);
2242                sqlStatement.normalize(session, null);
2243
2244                DataReadQuery dataReadQuery = new DataReadQuery();
2245                dataReadQuery.setSQLStatement(sqlStatement);
2246                dataReadQuery.setSessionName(getDescriptor().getSessionName());
2247
2248                // execute the query and check if there is a valid result
2249
Vector queryResults = (Vector)session.executeQuery(dataReadQuery, translationRow);
2250                if (!queryResults.isEmpty()) {
2251                    return false;
2252                }
2253            }
2254        }
2255
2256        // now ask each of the mappings to verify that the object has been deleted.
2257
for (Enumeration mappings = getDescriptor().getMappings().elements();
2258                 mappings.hasMoreElements();) {
2259            DatabaseMapping mapping = (DatabaseMapping)mappings.nextElement();
2260
2261            if (!mapping.verifyDelete(object, session)) {
2262                return false;
2263            }
2264        }
2265
2266        return true;
2267    }
2268
2269    /**
2270     * Wrap the object if required.
2271     * This is used for the wrapper policy support and EJB.
2272     */

2273    public Object JavaDoc wrapObject(Object JavaDoc implementation, AbstractSession session) {
2274        if (implementation == null) {
2275            return null;
2276        }
2277
2278        // Check if already wrapped.
2279
if (!getDescriptor().hasWrapperPolicy() || getDescriptor().getWrapperPolicy().isWrapped(implementation)) {
2280            return implementation;
2281        }
2282
2283        // Allow for inheritance, the concrete wrapper must always be used.
2284
if (getDescriptor().hasInheritance() && getDescriptor().getInheritancePolicy().hasChildren() && (implementation.getClass() != getDescriptor().getJavaClass())) {
2285            ClassDescriptor descriptor = session.getDescriptor(implementation);
2286            if(descriptor != getDescriptor()) {
2287                return descriptor.getObjectBuilder().wrapObject(implementation, session);
2288            }
2289        }
2290        if (getDescriptor().hasWrapperPolicy()) {
2291            return getDescriptor().getWrapperPolicy().wrapObject(implementation, session);
2292        } else {
2293            return implementation;
2294        }
2295    }
2296}
2297
2298
Popular Tags