KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > oracle > toplink > essentials > mappings > DirectMapMapping


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, 2005, Oracle. All rights reserved.
22
package oracle.toplink.essentials.mappings;
23
24 import java.util.*;
25 import oracle.toplink.essentials.exceptions.*;
26 import oracle.toplink.essentials.expressions.Expression;
27 import oracle.toplink.essentials.expressions.ExpressionBuilder;
28 import oracle.toplink.essentials.internal.identitymaps.CacheKey;
29 import oracle.toplink.essentials.internal.expressions.*;
30 import oracle.toplink.essentials.internal.helper.*;
31 import oracle.toplink.essentials.internal.indirection.*;
32 import oracle.toplink.essentials.internal.queryframework.*;
33 import oracle.toplink.essentials.internal.sessions.*;
34 import oracle.toplink.essentials.sessions.DatabaseRecord;
35 import oracle.toplink.essentials.mappings.converters.*;
36 import oracle.toplink.essentials.queryframework.*;
37 import oracle.toplink.essentials.internal.sessions.AbstractRecord;
38 import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
39 import oracle.toplink.essentials.internal.sessions.AbstractSession;
40
41 /**
42  * Mapping for a collection of key-value pairs.
43  * The key and value must be simple types (String, Number, Date, etc.)
44  * and stored in a single table along with a foreign key to the source object.
45  * A converter can be used on the key and value if the desired object types
46  * do not match the data types.
47  *
48  * @see Converter
49  * @see ObjectTypeConverter
50  * @see TypeConversionConverter
51  * @see SerializedObjectConverter
52  *
53  * @author: Steven Vo
54  * @since TopLink 3.5
55  */

56 public class DirectMapMapping extends DirectCollectionMapping {
57
58     /** The direct key field name is converted and stored */
59     protected DatabaseField directKeyField;
60
61     /** Allows user defined conversion between the object attribute value and the database value. */
62     protected Converter keyConverter;
63
64     /**
65      * DirectMapCollectionMapping constructor
66      */

67     public DirectMapMapping() {
68         super();
69         this.selectionQuery = new DataReadQuery();
70         this.containerPolicy = new DirectMapContainerPolicy(ClassConstants.Hashtable_Class);
71     }
72
73     /**
74      * PUBLIC:
75      * Return the converter on the mapping.
76      * A converter can be used to convert between the key's object value and database value.
77      */

78     public Converter getKeyConverter() {
79         return keyConverter;
80     }
81
82     /**
83      * PUBLIC:
84      * Set the converter on the mapping.
85      * A converter can be used to convert between the key's object value and database value.
86      */

87     public void setKeyConverter(Converter keyConverter) {
88         this.keyConverter = keyConverter;
89     }
90
91     /**
92      * INTERNAL:
93      * Add a new value and its change set to the collection change record. This is used by
94      * attribute change tracking. If a value has changed then issue a remove first with the key
95      * then an add.
96      */

97     public void addToCollectionChangeRecord(Object JavaDoc newKey, Object JavaDoc newValue, ObjectChangeSet objectChangeSet, UnitOfWorkImpl uow) throws DescriptorException {
98         DirectMapChangeRecord collectionChangeRecord = (DirectMapChangeRecord)objectChangeSet.getChangesForAttributeNamed(this.getAttributeName());
99         if (collectionChangeRecord == null) {
100             collectionChangeRecord = new DirectMapChangeRecord(objectChangeSet);
101             collectionChangeRecord.setAttribute(getAttributeName());
102             collectionChangeRecord.setMapping(this);
103             objectChangeSet.addChange(collectionChangeRecord);
104         }
105         collectionChangeRecord.addAdditionChange(newKey, newValue);
106     }
107
108     /**
109      * INTERNAL:
110      * Require for cloning, the part must be cloned.
111      * Ignore the objects, use the attribute value.
112      */

113     public Object JavaDoc buildCloneForPartObject(Object JavaDoc attributeValue, Object JavaDoc original, Object JavaDoc clone, UnitOfWorkImpl unitOfWork, boolean isExisting) {
114         DirectMapContainerPolicy containerPolicy = (DirectMapContainerPolicy)getContainerPolicy();
115         if (attributeValue == null) {
116             return containerPolicy.containerInstance(1);
117         }
118         Object JavaDoc clonedAttributeValue = containerPolicy.containerInstance(containerPolicy.sizeFor(attributeValue));
119
120         // I need to synchronize here to prevent the collection from changing while I am cloning it.
121
// This will occur when I am merging into the cache and I am instantiating a UOW valueHolder at the same time
122
// I can not synchronize around the clone, as this will cause deadlocks, so I will need to copy the collection then create the clones
123
// I will use a temporary collection to help speed up the process
124
Object JavaDoc temporaryCollection = null;
125         synchronized (attributeValue) {
126             temporaryCollection = containerPolicy.cloneFor(attributeValue);
127         }
128
129         for (Object JavaDoc keysIterator = containerPolicy.iteratorFor(temporaryCollection);
130                  containerPolicy.hasNext(keysIterator);) {
131             Object JavaDoc key = containerPolicy.next(keysIterator, unitOfWork);
132             Object JavaDoc cloneKey = buildKeyClone(key, unitOfWork, isExisting);
133             Object JavaDoc cloneValue = buildElementClone(containerPolicy.valueFromKey(key, temporaryCollection), unitOfWork, isExisting);
134             containerPolicy.addInto(cloneKey, cloneValue, clonedAttributeValue, unitOfWork);
135         }
136         return clonedAttributeValue;
137     }
138
139     /**
140      * INTERNAL:
141      * Clone the key, if necessary.
142      * DirectCollections hold on to objects that do not have Descriptors
143      * (e.g. int, String). These objects do not need to be cloned, unless they use a converter - they
144      * are immutable.
145      */

146     protected Object JavaDoc buildKeyClone(Object JavaDoc element, UnitOfWorkImpl unitOfWork, boolean isExisting) {
147         Object JavaDoc cloneValue = element;
148         if ((getKeyConverter() != null) && getKeyConverter().isMutable()) {
149             cloneValue = getKeyConverter().convertDataValueToObjectValue(getKeyConverter().convertObjectValueToDataValue(cloneValue, unitOfWork), unitOfWork);
150         }
151         return cloneValue;
152     }
153
154     /**
155      * INTERNAL:
156      * This method compares the changes between two direct collections. Comparisons are made on equality
157      * not identity.
158      * @return prototype.changeset.ChangeRecord
159      */

160     public ChangeRecord compareForChange(Object JavaDoc clone, Object JavaDoc backUp, ObjectChangeSet owner, AbstractSession session) {
161         Object JavaDoc cloneAttribute = null;
162         Object JavaDoc backUpAttribute = null;
163
164         DirectMapContainerPolicy cp = (DirectMapContainerPolicy)getContainerPolicy();
165
166         cloneAttribute = getAttributeValueFromObject(clone);
167         if ((cloneAttribute != null) && (!getIndirectionPolicy().objectIsInstantiated(cloneAttribute))) {
168             return null;
169         }
170
171         Map cloneObjectCollection = (Map)getRealCollectionAttributeValueFromObject(clone, session);
172         HashMap originalKeyValues = new HashMap(10);
173         HashMap cloneKeyValues = new HashMap(10);
174
175         if (!owner.isNew()) {
176             backUpAttribute = getAttributeValueFromObject(backUp);
177             if ((backUpAttribute == null) && (cloneAttribute == null)) {
178                 return null;
179             }
180             Map backUpCollection = (Map)getRealCollectionAttributeValueFromObject(backUp, session);
181             Object JavaDoc backUpIter = cp.iteratorFor(backUpCollection);
182             while (cp.hasNext(backUpIter)) {// Make a lookup of the objects
183
Object JavaDoc key = cp.next(backUpIter, session);
184                 originalKeyValues.put(key, backUpCollection.get(key));
185             }
186         }
187         Object JavaDoc cloneIter = cp.iteratorFor(cloneObjectCollection);
188         while (cp.hasNext(cloneIter)) {//Compare them with the objects from the clone
189
Object JavaDoc firstObject = cp.next(cloneIter, session);
190             Object JavaDoc firstValue = cloneObjectCollection.get(firstObject);
191             Object JavaDoc backupValue = originalKeyValues.get(firstObject);
192             if ( ! originalKeyValues.containsKey(firstObject) ){
193                 cloneKeyValues.put(firstObject, cloneObjectCollection.get(firstObject));
194             }else if ( (backupValue == null && firstValue != null) || (!backupValue.equals(firstValue)) ) {//the object was not in the backup
195
cloneKeyValues.put(firstObject, cloneObjectCollection.get(firstObject));
196             }else{
197                 originalKeyValues.remove(firstObject);
198             }
199         }
200         if (cloneKeyValues.isEmpty() && originalKeyValues.isEmpty() && (!owner.isNew())) {
201             return null;
202         }
203         DirectMapChangeRecord changeRecord = new DirectMapChangeRecord(owner);
204         changeRecord.setAttribute(getAttributeName());
205         changeRecord.setMapping(this);
206         changeRecord.addAdditionChange(cloneKeyValues);
207         changeRecord.addRemoveChange(originalKeyValues);
208         return changeRecord;
209     }
210
211     /**
212      * INTERNAL:
213      * Compare the attributes belonging to this mapping for the objects.
214      */

215     public boolean compareObjects(Object JavaDoc firstObject, Object JavaDoc secondObject, AbstractSession session) {
216         Object JavaDoc firstObjectMap = getRealCollectionAttributeValueFromObject(firstObject, session);
217         Object JavaDoc secondObjectMap = getRealCollectionAttributeValueFromObject(secondObject, session);
218         DirectMapContainerPolicy mapContainerPolicy = (DirectMapContainerPolicy)getContainerPolicy();
219
220         return mapContainerPolicy.compareContainers(firstObjectMap, secondObjectMap);
221     }
222
223     /**
224      * INTERNAL:
225      * Convert all the class-name-based settings in this mapping to actual class-based
226      * settings
227      * This method is implemented by subclasses as necessary.
228      * @param classLoader
229      */

230     public void convertClassNamesToClasses(ClassLoader JavaDoc classLoader){
231         super.convertClassNamesToClasses(classLoader);
232         if (keyConverter != null && keyConverter instanceof TypeConversionConverter){
233             ((TypeConversionConverter)keyConverter).convertClassNamesToClasses(classLoader);
234         }
235     };
236
237     /**
238      * INTERNAL:
239      */

240     public DatabaseField getDirectKeyField() {
241         return directKeyField;
242     }
243
244     /**
245      * INTERNAL:
246      * Initialize and validate the mapping properties.
247      */

248     public void initialize(AbstractSession session) throws DescriptorException {
249         super.initialize(session);
250         initializeDirectKeyField(session);
251         initializeContainerPolicy(session);
252         if (getKeyConverter() != null) {
253             getKeyConverter().initialize(this, session);
254         }
255         if (getValueConverter() != null) {
256             getValueConverter().initialize(this, session);
257         }
258     }
259
260     /**
261      * set the key and value fields that are used to build the container from database rows
262      */

263     protected void initializeContainerPolicy(AbstractSession session) {
264         ((DirectMapContainerPolicy)getContainerPolicy()).setKeyField(getDirectKeyField());
265         ((DirectMapContainerPolicy)getContainerPolicy()).setValueField(getDirectField());
266         ((DirectMapContainerPolicy)getContainerPolicy()).setKeyConverter(getKeyConverter());
267         ((DirectMapContainerPolicy)getContainerPolicy()).setValueConverter(getValueConverter());
268     }
269
270     protected void initializeDeleteQuery(AbstractSession session) {
271         if (!getDeleteQuery().hasSessionName()) {
272             getDeleteQuery().setSessionName(session.getName());
273         }
274
275         if (hasCustomDeleteQuery()) {
276             return;
277         }
278
279         Expression builder = new ExpressionBuilder();
280         Expression directKeyExp = builder.getField(getDirectKeyField()).equal(builder.getParameter(getDirectKeyField()));
281         Expression expression = null;
282         SQLDeleteStatement statement = new SQLDeleteStatement();
283
284         // Construct an expression to delete from the relation table.
285
for (int index = 0; index < getReferenceKeyFields().size(); index++) {
286             DatabaseField referenceKey = (DatabaseField)getReferenceKeyFields().elementAt(index);
287             DatabaseField sourceKey = (DatabaseField)getSourceKeyFields().elementAt(index);
288
289             Expression subExp1 = builder.getField(referenceKey);
290             Expression subExp2 = builder.getParameter(sourceKey);
291             Expression subExpression = subExp1.equal(subExp2);
292
293             expression = subExpression.and(expression);
294         }
295         expression = expression.and(directKeyExp);
296         statement.setWhereClause(expression);
297         statement.setTable(getReferenceTable());
298         getDeleteQuery().setSQLStatement(statement);
299     }
300
301     /**
302      * The field name on the reference table is initialized and cached.
303      */

304     protected void initializeDirectKeyField(AbstractSession session) throws DescriptorException {
305         if (getDirectKeyField() == null) {
306             throw DescriptorException.directFieldNameNotSet(this);
307         }
308
309         getDirectKeyField().setTable(getReferenceTable());
310         getDirectKeyField().setIndex(1);
311     }
312
313     /**
314      * Initialize insert query. This query is used to insert the collection of objects into the
315      * reference table.
316      */

317     protected void initializeInsertQuery(AbstractSession session) {
318         super.initializeInsertQuery(session);
319         getInsertQuery().getModifyRow().put(getDirectKeyField(), null);
320     }
321
322     protected void initializeSelectionStatement(AbstractSession session) {
323         SQLSelectStatement statement = new SQLSelectStatement();
324         statement.addTable(getReferenceTable());
325         statement.addField((DatabaseField)getDirectField().clone());
326         statement.addField((DatabaseField)getDirectKeyField().clone());
327         statement.setWhereClause(getSelectionCriteria());
328         statement.normalize(session, null);
329         getSelectionQuery().setSQLStatement(statement);
330     }
331
332     /**
333      * INTERNAL:
334      * Related mapping should implement this method to return true.
335      */

336     public boolean isDirectMapMapping() {
337         return true;
338     }
339
340     /**
341      * INTERNAL:
342      * Merge changes from the source to the target object.
343      * Because this is a collection mapping, values are added to or removed from the
344      * collection based on the changeset
345      */

346     public void mergeChangesIntoObject(Object JavaDoc target, ChangeRecord changeRecord, Object JavaDoc source, MergeManager mergeManager) {
347         DirectMapContainerPolicy containerPolicy = (DirectMapContainerPolicy)getContainerPolicy();
348         Map valueOfTarget = null;
349         AbstractSession session = mergeManager.getSession();
350
351         //collect the changes into a vector
352
HashMap addObjects = ((DirectMapChangeRecord)changeRecord).getAddObjects();
353         HashMap removeObjects = ((DirectMapChangeRecord)changeRecord).getRemoveObjects();
354
355         //Check to see if the target has an instantiated collection
356
if ((isAttributeValueInstantiated(target)) && (!changeRecord.getOwner().isNew())) {
357             valueOfTarget = (Map)getRealCollectionAttributeValueFromObject(target, session);
358         } else {
359             //if not create an instance of the map
360
valueOfTarget = (Map)containerPolicy.containerInstance(addObjects.size());
361         }
362
363         if (!isAttributeValueInstantiated(target)) {
364             if (mergeManager.shouldMergeChangesIntoDistributedCache()) {
365                 return;
366             }
367
368             Object JavaDoc valueOfSource = getRealCollectionAttributeValueFromObject(source, session);
369             for (Object JavaDoc iterator = containerPolicy.iteratorFor(valueOfSource);
370                      containerPolicy.hasNext(iterator);) {
371                 Object JavaDoc key = containerPolicy.next(iterator, session);
372                 containerPolicy.addInto(key, ((Map)valueOfSource).get(key), valueOfTarget, session);
373             }
374
375         } else {
376             synchronized (valueOfTarget) {
377                 // Next iterate over the changes and add them to the container
378
for (Iterator i = removeObjects.keySet().iterator(); i.hasNext(); ) {
379                     Object JavaDoc keyToRemove = i.next();
380                     containerPolicy.removeFrom(keyToRemove, (Object JavaDoc)null, valueOfTarget, session);
381                 }
382
383                 for (Iterator i = addObjects.keySet().iterator(); i.hasNext(); ) {
384                     Object JavaDoc keyToAdd = i.next();
385                     Object JavaDoc nextItem = addObjects.get(keyToAdd);
386                     if (mergeManager.shouldMergeChangesIntoDistributedCache()) {
387                         //bug#4458089 and 4454532- check if collection contains new item before adding during merge into distributed cache
388
if (!containerPolicy.contains(nextItem, valueOfTarget, session)) {
389                             containerPolicy.addInto(keyToAdd, nextItem, valueOfTarget, session);
390                         }
391                     } else {
392                         containerPolicy.addInto(keyToAdd, nextItem, valueOfTarget, session);
393                     }
394                 }
395             }
396         }
397         setRealAttributeValueInObject(target, valueOfTarget);
398     }
399
400     /**
401      * INTERNAL:
402      * Merge changes from the source to the target object.
403      */

404     public void mergeIntoObject(Object JavaDoc target, boolean isTargetUnInitialized, Object JavaDoc source, MergeManager mergeManager) {
405         if (isTargetUnInitialized) {
406             // This will happen if the target object was removed from the cache before the commit was attempted
407
if (mergeManager.shouldMergeWorkingCopyIntoOriginal() && (!isAttributeValueInstantiated(source))) {
408                 setAttributeValueInObject(target, getIndirectionPolicy().getOriginalIndirectionObject(getAttributeValueFromObject(source), mergeManager.getSession()));
409                 return;
410             }
411         }
412         if (!shouldMergeCascadeReference(mergeManager)) {
413             // This is only going to happen on mergeClone, and we should not attempt to merge the reference
414
return;
415         }
416         if (mergeManager.shouldMergeOriginalIntoWorkingCopy()) {
417             if (!isAttributeValueInstantiated(target)) {
418                 // This will occur when the clone's value has not been instantiated yet and we do not need
419
// the refresh that attribute
420
return;
421             }
422         } else if (!isAttributeValueInstantiated(source)) {
423             // I am merging from a clone into an original. No need to do merge if the attribute was never
424
// modified
425
return;
426         }
427
428         Map valueOfSource = (Map)getRealCollectionAttributeValueFromObject(source, mergeManager.getSession());
429
430         DirectMapContainerPolicy containerPolicy = (DirectMapContainerPolicy)getContainerPolicy();
431
432         // trigger instantiation of target attribute
433
Object JavaDoc valueOfTarget = getRealCollectionAttributeValueFromObject(target, mergeManager.getSession());
434         Object JavaDoc newContainer = containerPolicy.containerInstance(containerPolicy.sizeFor(valueOfSource));
435
436         boolean fireChangeEvents = false;
437         valueOfTarget = newContainer;
438         
439         for (Object JavaDoc sourceValuesIterator = containerPolicy.iteratorFor(valueOfSource);
440                  containerPolicy.hasNext(sourceValuesIterator);) {
441             Object JavaDoc sourceKey = containerPolicy.next(sourceValuesIterator, mergeManager.getSession());
442             containerPolicy.addInto(sourceKey, valueOfSource.get(sourceKey), valueOfTarget, mergeManager.getSession());
443         }
444
445         // Must re-set variable to allow for set method to re-morph changes if the collection is not being stored directly.
446
setRealAttributeValueInObject(target, valueOfTarget);
447     }
448
449     /**
450      * INTERNAL:
451      * Insert the private owned object.
452      */

453     public void postInsert(WriteObjectQuery query) throws DatabaseException {
454         Object JavaDoc objects;
455         AbstractRecord databaseRow = new DatabaseRecord();
456
457         if (isReadOnly()) {
458             return;
459         }
460
461         objects = getRealCollectionAttributeValueFromObject(query.getObject(), query.getSession());
462         DirectMapContainerPolicy containerPolicy = (DirectMapContainerPolicy)getContainerPolicy();
463         if (containerPolicy.isEmpty(objects)) {
464             return;
465         }
466
467         prepareTranslationRow(query.getTranslationRow(), query.getObject(), query.getSession());
468         // Extract primary key and value from the source.
469
for (int index = 0; index < getReferenceKeyFields().size(); index++) {
470             DatabaseField referenceKey = (DatabaseField)getReferenceKeyFields().elementAt(index);
471             DatabaseField sourceKey = (DatabaseField)getSourceKeyFields().elementAt(index);
472             Object JavaDoc sourceKeyValue = query.getTranslationRow().get(sourceKey);
473             databaseRow.put(referenceKey, sourceKeyValue);
474         }
475
476         // Extract target field and its value. Construct insert statement and execute it
477
Object JavaDoc keyIter = containerPolicy.iteratorFor(objects);
478         while (containerPolicy.hasNext(keyIter)) {
479             Object JavaDoc key = containerPolicy.next(keyIter, query.getSession());
480             Object JavaDoc value = containerPolicy.valueFromKey(key, objects);
481             if (getKeyConverter() != null) {
482                 key = getKeyConverter().convertObjectValueToDataValue(key, query.getSession());
483             }
484             if (getValueConverter() != null) {
485                 value = getValueConverter().convertObjectValueToDataValue(value, query.getSession());
486             }
487             databaseRow.put(getDirectKeyField(), key);
488             databaseRow.put(getDirectField(), value);
489             // In the uow data queries are cached until the end of the commit.
490
if (query.shouldCascadeOnlyDependentParts()) {
491                 // Hey I might actually want to use an inner class here... ok array for now.
492
Object JavaDoc[] event = new Object JavaDoc[3];
493                 event[0] = Insert;
494                 event[1] = getInsertQuery();
495                 event[2] = databaseRow.clone();
496                 query.getSession().getCommitManager().addDataModificationEvent(this, event);
497             } else {
498                 query.getSession().executeQuery(getInsertQuery(), databaseRow);
499             }
500         }
501     }
502
503     /**
504      * INTERNAL:
505      * Update private owned part.
506      */

507     protected void postUpdateWithChangeSet(WriteObjectQuery writeQuery) throws DatabaseException {
508
509         ObjectChangeSet changeSet = writeQuery.getObjectChangeSet();
510         DirectMapChangeRecord changeRecord = (DirectMapChangeRecord)changeSet.getChangesForAttributeNamed(this.getAttributeName());
511         if (changeRecord == null){
512             return;
513         }
514         for (int index = 0; index < getReferenceKeyFields().size(); index++) {
515             DatabaseField referenceKey = (DatabaseField)getReferenceKeyFields().elementAt(index);
516             DatabaseField sourceKey = (DatabaseField)getSourceKeyFields().elementAt(index);
517             Object JavaDoc sourceKeyValue = writeQuery.getTranslationRow().get(sourceKey);
518             writeQuery.getTranslationRow().put(referenceKey, sourceKeyValue);
519         }
520         for (Iterator iterator = changeRecord.getRemoveObjects().keySet().iterator(); iterator.hasNext();){
521             Object JavaDoc key = iterator.next();
522             AbstractRecord thisRow = (AbstractRecord)writeQuery.getTranslationRow().clone();
523             if (getKeyConverter() != null){
524                 key = getKeyConverter().convertObjectValueToDataValue(key, writeQuery.getSession());
525             }
526             thisRow.add(getDirectKeyField(), key);
527             // Hey I might actually want to use an inner class here... ok array for now.
528
Object JavaDoc[] event = new Object JavaDoc[3];
529             event[0] = Delete;
530             event[1] = getDeleteQuery();
531             event[2] = thisRow;
532             writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
533         }
534         for (Iterator iterator = changeRecord.getAddObjects().keySet().iterator(); iterator.hasNext();){
535             Object JavaDoc key = iterator.next();
536             AbstractRecord thisRow = (AbstractRecord)writeQuery.getTranslationRow().clone();
537             Object JavaDoc value = changeRecord.getAddObjects().get(key);
538             if (getKeyConverter() != null){
539                 key = getKeyConverter().convertObjectValueToDataValue(key, writeQuery.getSession());
540             }
541             if (getValueConverter() != null){
542                 value = getValueConverter().convertObjectValueToDataValue(value, writeQuery.getSession());
543             }
544             thisRow.add(getDirectKeyField(), key);
545             thisRow.add(getDirectField(), value);
546             // Hey I might actually want to use an inner class here... ok array for now.
547
Object JavaDoc[] event = new Object JavaDoc[3];
548             event[0] = Insert;
549             event[1] = getInsertQuery();
550             event[2] = thisRow;
551             writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
552         }
553     }
554
555     /**
556      * INTERNAL:
557      * Remove a value and its change set from the collection change record. This is used by
558      * attribute change tracking.
559      */

560     public void removeFromCollectionChangeRecord(Object JavaDoc newKey, Object JavaDoc newValue, ObjectChangeSet objectChangeSet, UnitOfWorkImpl uow) throws DescriptorException {
561         DirectMapChangeRecord collectionChangeRecord = (DirectMapChangeRecord)objectChangeSet.getChangesForAttributeNamed(this.getAttributeName());
562         if (collectionChangeRecord == null) {
563             collectionChangeRecord = new DirectMapChangeRecord(objectChangeSet);
564             collectionChangeRecord.setAttribute(getAttributeName());
565             collectionChangeRecord.setMapping(this);
566             objectChangeSet.addChange(collectionChangeRecord);
567         }
568         collectionChangeRecord.addRemoveChange(newKey, newValue);
569     }
570
571     /**
572      * INTERNAL:
573      */

574     public void setDirectKeyField(DatabaseField keyField) {
575         directKeyField = keyField;
576     }
577
578     /**
579      * PUBLIC:
580      * Set the direct key field name in the reference table.
581      * This is the field that the primitive data value of the Map key is stored in.
582      */

583     public void setDirectKeyFieldName(String JavaDoc fieldName) {
584         setDirectKeyField(new DatabaseField(fieldName));
585     }
586
587     /**
588      * INTERNAL:
589      * Either create a new change record or update the change record with the new value.
590      * This is used by attribute change tracking.
591      */

592     public void updateChangeRecord(Object JavaDoc clone, Object JavaDoc newValue, Object JavaDoc oldValue, ObjectChangeSet objectChangeSet, UnitOfWorkImpl uow) throws DescriptorException {
593         DirectMapChangeRecord collectionChangeRecord = (DirectMapChangeRecord)objectChangeSet.getChangesForAttributeNamed(this.getAttributeName());
594         if (collectionChangeRecord == null) {
595             collectionChangeRecord = new DirectMapChangeRecord(objectChangeSet);
596             collectionChangeRecord.setAttribute(getAttributeName());
597             collectionChangeRecord.setMapping(this);
598             objectChangeSet.addChange(collectionChangeRecord);
599         }
600         if (collectionChangeRecord.getOriginalCollection() == null){
601             collectionChangeRecord.setOriginalCollection(oldValue);
602         }
603         collectionChangeRecord.setLatestCollection(newValue);
604         
605         objectChangeSet.deferredDetectionRequiredOn(getAttributeName());
606     }
607
608     /**
609      * PUBLIC:
610      * Configure the mapping to use an instance of the specified container class
611      * to hold the target objects.
612      * <p>The default container class is java.util.Hashtable.
613      * <p>The container class must implements (directly or indirectly) the Map interface.
614      * <p>Note: Do not use both useMapClass(Class concreteClass), useTransparentMap(). The last use of one of the two methods will overide the previous one.
615      */

616     public void useMapClass(Class JavaDoc concreteClass) {
617         if (!Helper.classImplementsInterface(concreteClass, ClassConstants.Map_Class)) {
618             throw DescriptorException.illegalContainerClass(concreteClass);
619         }
620         DirectMapContainerPolicy policy = new DirectMapContainerPolicy(concreteClass);
621         setContainerPolicy(policy);
622     }
623
624     /**
625      * PUBLIC:
626      * Configure the mapping to use an instance of the specified container class
627      * to hold the target objects.
628      * <p>jdk1.2.x: The container class must implement (directly or indirectly) the Map interface.
629      * <p>jdk1.1.x: The container class must be a subclass of Hashtable.
630      * <p>Note: Do not use both useMapClass(Class concreteClass), useTransparentMap(). The last use of one of the two methods will overide the previous one.
631      */

632     public void useTransparentMap() {
633         setIndirectionPolicy(new TransparentIndirectionPolicy());
634         useMapClass(ClassConstants.IndirectMap_Class);
635     }
636
637     /**
638      * PUBLIC:
639      * This is a helper method to set the key converter to a TypeConversionConverter.
640      * This ensures that the key value from the database is converted to the correct
641      * Java type. The converter can also be set directly.
642      * Note that setting the converter to another converter will overwrite this setting.
643      */

644     public void setKeyClass(Class JavaDoc keyClass) {
645         TypeConversionConverter converter = new TypeConversionConverter(this);
646         converter.setObjectClass(keyClass);
647         setKeyConverter(converter);
648     }
649
650     /**
651      * PUBLIC:
652      * This is a helper method to get the object class from the key converter
653      * if it is a TypeConversionConverter.
654      * This returns null if not using a TypeConversionConverter key converter.
655      */

656     public Class JavaDoc getKeyClass() {
657         if (!(getKeyConverter() instanceof TypeConversionConverter)) {
658             return null;
659         }
660         return ((TypeConversionConverter)getKeyConverter()).getObjectClass();
661     }
662
663     /**
664      * PUBLIC:
665      * This is a helper method to set the value converter to a TypeConversionConverter.
666      * This ensures that the value from the database is converted to the correct
667      * Java type. The converter can also be set directly.
668      * Note that setting the converter to another converter will overwrite this setting.
669      */

670     public void setValueClass(Class JavaDoc valueClass) {
671         TypeConversionConverter converter = new TypeConversionConverter(this);
672         converter.setObjectClass(valueClass);
673         setValueConverter(converter);
674     }
675
676     /**
677      * ADVANCED:
678      * This method is used to have an object add to a collection once the changeSet is applied
679      * The referenceKey parameter should only be used for direct Maps.
680      */

681     public void simpleAddToCollectionChangeRecord(Object JavaDoc referenceKey, Object JavaDoc objectToAdd, ObjectChangeSet changeSet, AbstractSession session) {
682         DirectMapChangeRecord collectionChangeRecord = (DirectMapChangeRecord)changeSet.getChangesForAttributeNamed(getAttributeName());
683         if (collectionChangeRecord == null) {
684             collectionChangeRecord = new DirectMapChangeRecord(changeSet);
685             collectionChangeRecord.setAttribute(getAttributeName());
686             collectionChangeRecord.setMapping(this);
687             collectionChangeRecord.getAddObjects().put(referenceKey, objectToAdd);
688             changeSet.addChange(collectionChangeRecord);
689         } else {
690             if (collectionChangeRecord.getRemoveObjects().containsKey(referenceKey)) {
691                 collectionChangeRecord.getRemoveObjects().remove(referenceKey);
692             } else {
693                 collectionChangeRecord.getAddObjects().put(referenceKey, objectToAdd);
694             }
695         }
696     }
697
698     /**
699      * ADVANCED:
700      * This method is used to have an object removed from a collection once the changeSet is applied
701      * The referenceKey parameter should only be used for direct Maps.
702      */

703     public void simpleRemoveFromCollectionChangeRecord(Object JavaDoc referenceKey, Object JavaDoc objectToRemove, ObjectChangeSet changeSet, AbstractSession session) {
704         DirectMapChangeRecord collectionChangeRecord = (DirectMapChangeRecord)changeSet.getChangesForAttributeNamed(getAttributeName());
705         if (collectionChangeRecord == null) {
706             collectionChangeRecord = new DirectMapChangeRecord(changeSet);
707             collectionChangeRecord.setAttribute(getAttributeName());
708             collectionChangeRecord.setMapping(this);
709             collectionChangeRecord.getRemoveObjects().put(referenceKey, objectToRemove);
710             changeSet.addChange(collectionChangeRecord);
711         } else {
712             if (collectionChangeRecord.getAddObjects().containsKey(referenceKey)) {
713                 collectionChangeRecord.getAddObjects().remove(referenceKey);
714             } else {
715                 collectionChangeRecord.getRemoveObjects().put(referenceKey, objectToRemove);
716             }
717         }
718     }
719
720     /**
721      * PUBLIC:
722      * This is a helper method to get the object class from the value converter
723      * if it is a TypeConversionConverter.
724      * This returns null if not using a TypeConversionConverter value converter.
725      */

726     public Class JavaDoc getValueClass() {
727         if (!(getValueConverter() instanceof TypeConversionConverter)) {
728             return null;
729         }
730         return ((TypeConversionConverter)getValueConverter()).getObjectClass();
731     }
732
733     /**
734      * INTERNAL:
735      * Ovewrite super method
736      */

737     public Object JavaDoc extractResultFromBatchQuery(DatabaseQuery query, AbstractRecord databaseRow, AbstractSession session, AbstractRecord argumentRow) {
738         //this can be null, because either one exists in the query or it will be created
739
Hashtable referenceDataByKey = null;
740         ContainerPolicy mappingContainerPolicy = getContainerPolicy();
741         synchronized (query) {
742             referenceDataByKey = (Hashtable)query.getProperty("batched objects");
743             mappingContainerPolicy = getContainerPolicy();
744             if (referenceDataByKey == null) {
745                 Vector rows = (Vector)session.executeQuery(query, argumentRow);
746
747                 referenceDataByKey = new Hashtable();
748
749                 for (Enumeration rowsEnum = rows.elements(); rowsEnum.hasMoreElements();) {
750                     AbstractRecord referenceRow = (AbstractRecord)rowsEnum.nextElement();
751                     Object JavaDoc referenceKey = referenceRow.get(getDirectKeyField());
752                     Object JavaDoc referenceValue = referenceRow.get(getDirectField());
753                     CacheKey eachCacheKey = new CacheKey(extractKeyFromReferenceRow(referenceRow, session));
754
755                     Object JavaDoc container = referenceDataByKey.get(eachCacheKey);
756                     if (container == null) {
757                         container = mappingContainerPolicy.containerInstance();
758                         referenceDataByKey.put(eachCacheKey, container);
759                     }
760
761                     // Allow for key conversion.
762
if (getKeyConverter() != null) {
763                         referenceKey = getKeyConverter().convertDataValueToObjectValue(referenceKey, query.getSession());
764                     }
765
766                     // Allow for value conversion.
767
if (getValueConverter() != null) {
768                         referenceValue = getValueConverter().convertDataValueToObjectValue(referenceValue, query.getSession());
769                     }
770
771                     mappingContainerPolicy.addInto(referenceKey, referenceValue, container, query.getSession());
772                 }
773
774                 query.setProperty("batched objects", referenceDataByKey);
775             }
776         }
777         Object JavaDoc result = referenceDataByKey.get(new CacheKey(extractPrimaryKeyFromRow(databaseRow, session)));
778
779         // The source object might not have any target objects
780
if (result == null) {
781             return mappingContainerPolicy.containerInstance();
782         } else {
783             return result;
784         }
785     }
786 }
787
Popular Tags