KickJava   Java API By Example, From Geeks To Geeks.

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


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.mappings;
23
24 import java.util.*;
25 import oracle.toplink.essentials.exceptions.*;
26 import oracle.toplink.essentials.expressions.*;
27 import oracle.toplink.essentials.internal.databaseaccess.Platform;
28 import oracle.toplink.essentials.internal.descriptors.*;
29 import oracle.toplink.essentials.internal.expressions.*;
30 import oracle.toplink.essentials.internal.helper.*;
31 import oracle.toplink.essentials.internal.queryframework.*;
32 import oracle.toplink.essentials.internal.sessions.*;
33 import oracle.toplink.essentials.mappings.converters.*;
34 import oracle.toplink.essentials.queryframework.*;
35 import oracle.toplink.essentials.sessions.ObjectCopyingPolicy;
36 import oracle.toplink.essentials.sessions.DatabaseRecord;
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 import oracle.toplink.essentials.descriptors.ClassDescriptor;
41
42 /**
43  * <p><b>Purpose</b>: This mapping is used to store a collection of simple types (String, Number, Date, etc.)
44  * into a single table. The table must store the value and a foreign key to the source object.
45  * A converter can be used if the desired object type and the data type do not match.
46  *
47  * @see Converter
48  * @see ObjectTypeConverter
49  * @see TypeConversionConverter
50  * @see SerializedObjectConverter
51  *
52  * @author Sati
53  * @since TOPLink/Java 1.0
54  */

55 public class DirectCollectionMapping extends CollectionMapping implements RelationalMapping {
56
57     /** Used for data modification events. */
58     protected static final String JavaDoc Delete = "delete";
59     protected static final String JavaDoc Insert = "insert";
60     protected static final String JavaDoc DeleteAll = "deleteAll";
61
62     /** Allows user defined conversion between the object value and the database value. */
63     protected Converter valueConverter;
64
65     /** Stores the reference table*/
66     protected transient DatabaseTable referenceTable;
67
68     /** The direct field name is converted and stored */
69     protected transient DatabaseField directField;
70     protected transient Vector sourceKeyFields;
71     protected transient Vector referenceKeyFields;
72
73     /** Used for insertion for m-m and dc, not used in 1-m. */
74     protected transient DataModifyQuery insertQuery;
75     /** Used for deletion when ChangeSets are used */
76     protected transient ModifyQuery changeSetDeleteQuery;
77     
78     protected transient boolean hasCustomDeleteQuery;
79     protected transient boolean hasCustomInsertQuery;
80
81     /**
82      * PUBLIC:
83      * Default constructor.
84      */

85     public DirectCollectionMapping() {
86         this.insertQuery = new DataModifyQuery();
87         this.sourceKeyFields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(1);
88         this.referenceKeyFields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(1);
89         this.selectionQuery = new DirectReadQuery();
90         this.hasCustomInsertQuery = false;
91         this.isPrivateOwned = true;
92     }
93
94     public boolean isRelationalMapping() {
95         return true;
96     }
97
98     /**
99      * PUBLIC:
100      * Return the converter on the mapping.
101      * A converter can be used to convert between the direct collection's object value and database value.
102      */

103     public Converter getValueConverter() {
104         return valueConverter;
105     }
106
107     /**
108      * PUBLIC:
109      * Set the converter on the mapping.
110      * A converter can be used to convert between the direct collection's object value and database value.
111      */

112     public void setValueConverter(Converter valueConverter) {
113         this.valueConverter = valueConverter;
114     }
115
116     /**
117      * PUBLIC:
118      * Add the name of the reference key field.
119      * This is used for composite reference keys.
120      * This is the foreign key field in the direct table referencing the primary key of the source object.
121      * Both the reference field name and the name of the source field that it references must be provided.
122      */

123     public void addReferenceKeyFieldName(String JavaDoc referenceForeignKeyFieldName, String JavaDoc sourcePrimaryKeyFieldName) {
124         getSourceKeyFields().addElement(new DatabaseField(sourcePrimaryKeyFieldName));
125         getReferenceKeyFields().addElement(new DatabaseField(referenceForeignKeyFieldName));
126     }
127
128     /**
129      * INTERNAL:
130      * Copy of the attribute of the object.
131      * This is NOT used for unit of work but for templatizing an object.
132      */

133     public void buildCopy(Object JavaDoc copy, Object JavaDoc original, ObjectCopyingPolicy policy) {
134         Object JavaDoc attributeValue = getRealCollectionAttributeValueFromObject(original, policy.getSession());
135         attributeValue = getContainerPolicy().cloneFor(attributeValue);
136         setRealAttributeValueInObject(copy, attributeValue);
137     }
138
139     /**
140      * INTERNAL:
141      * Clone the element, 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 buildElementClone(Object JavaDoc element, UnitOfWorkImpl unitOfWork, boolean isExisting) {
147         Object JavaDoc cloneValue = element;
148         if ((getValueConverter() != null) && getValueConverter().isMutable()) {
149             cloneValue = getValueConverter().convertDataValueToObjectValue(getValueConverter().convertObjectValueToDataValue(cloneValue, unitOfWork), unitOfWork);
150         }
151         return cloneValue;
152     }
153
154     /**
155      * INTERNAL:
156      * Cascade perform delete through mappings that require the cascade
157      */

158     public void cascadePerformRemoveIfRequired(Object JavaDoc object, UnitOfWorkImpl uow, IdentityHashtable visitedObjects) {
159         //as this mapping type references primitive objects this method does not apply
160
}
161
162     /**
163      * INTERNAL:
164      * Cascade registerNew for Create through mappings that require the cascade
165      */

166     public void cascadeRegisterNewIfRequired(Object JavaDoc object, UnitOfWorkImpl uow, IdentityHashtable visitedObjects) {
167         //as this mapping type references primitive objects this method does not apply
168
}
169
170     /**
171      * INTERNAL:
172      * The mapping clones itself to create deep copy.
173      */

174     public Object JavaDoc clone() {
175         DirectCollectionMapping clone = (DirectCollectionMapping)super.clone();
176
177         clone.setSourceKeyFields(cloneFields(getSourceKeyFields()));
178         clone.setReferenceKeyFields(cloneFields(getReferenceKeyFields()));
179
180         return clone;
181     }
182
183     /**
184      * INTERNAL:
185      * This method is used to calculate the differences between two collections.
186      */

187     public void compareCollectionsForChange(Object JavaDoc oldCollection, Object JavaDoc newCollection, ChangeRecord changeRecord, AbstractSession session){
188         ContainerPolicy cp = getContainerPolicy();
189         int numberOfNewNulls = 0;
190
191         HashMap originalKeyValues = new HashMap(10);
192         HashMap cloneKeyValues = new HashMap(10);
193         
194         if (oldCollection != null){
195             Object JavaDoc backUpIter = cp.iteratorFor(oldCollection);
196
197             while (cp.hasNext(backUpIter)) {// Make a lookup of the objects
198
Object JavaDoc secondObject = cp.next(backUpIter, session);
199
200                 // For CR#2258/CR#2378 handle null values inserted in a collection.
201
if (secondObject == null) {
202                     numberOfNewNulls--;
203                 } else {
204                     Integer JavaDoc count = (Integer JavaDoc)originalKeyValues.get(secondObject);
205                     if (count == null) {
206                         originalKeyValues.put(secondObject, new Integer JavaDoc(1));
207                     } else {
208                         originalKeyValues.put(secondObject, new Integer JavaDoc(count.intValue() + 1));
209                     }
210                 }
211             }
212         }
213         // should a removal occur this is the original count of objects on the database.
214
// this value is used to determine how many objects to re-insert after the delete as a
215
// delete will delete all of the objects not just one.
216
HashMap databaseCount = (HashMap)originalKeyValues.clone();
217         int databaseNullCount = Math.abs(numberOfNewNulls);
218
219         if (newCollection != null){
220             Object JavaDoc cloneIter = cp.iteratorFor(newCollection);
221
222             /* The following code is used to compare objects in a direct collection.
223                Because objects in a direct collection are primitives and may be the same object
224                the following code must count the number of instances in the collection not just the
225                existence of an object.
226             */

227             while (cp.hasNext(cloneIter)) {//Compare them with the objects from the clone
228
Object JavaDoc firstObject = cp.next(cloneIter, session);
229     
230                 // For CR#2258/CR#2378 handle null values inserted in a collection.
231
if (firstObject == null) {
232                     numberOfNewNulls++;
233                 } else {
234                     Integer JavaDoc count = (Integer JavaDoc)originalKeyValues.get(firstObject);
235                     if (count == null) {//the object was not in the backup
236
Integer JavaDoc cloneCount = (Integer JavaDoc)cloneKeyValues.get(firstObject);
237     
238                         //Add it to the additions hashtable
239
if (cloneCount == null) {
240                             cloneKeyValues.put(firstObject, new Integer JavaDoc(1));
241                         } else {
242                             cloneKeyValues.put(firstObject, new Integer JavaDoc(cloneCount.intValue() + 1));
243                         }
244                     } else if (count.intValue() == 1) {
245                         //There is only one object so remove the whole reference
246
originalKeyValues.remove(firstObject);
247                     } else {
248                         originalKeyValues.put(firstObject, new Integer JavaDoc(count.intValue() - 1));
249                     }
250                 }
251             }
252         }
253         if (cloneKeyValues.isEmpty() && originalKeyValues.isEmpty() && (numberOfNewNulls == 0) && (!changeRecord.getOwner().isNew())) {
254             return;
255         }
256         ((DirectCollectionChangeRecord)changeRecord).addAdditionChange(cloneKeyValues, databaseCount);
257         ((DirectCollectionChangeRecord)changeRecord).addRemoveChange(originalKeyValues, databaseCount);
258         //For CR#2258, produce a changeRecord which reflects the addition and removal of null values.
259
if (numberOfNewNulls != 0) {
260             Vector changeList = null;
261             ((DirectCollectionChangeRecord)changeRecord).getCommitAddMap().put(DirectCollectionChangeRecord.Null, new Integer JavaDoc(databaseNullCount));
262             if (numberOfNewNulls > 0) {
263                 ((DirectCollectionChangeRecord)changeRecord).addAdditionChange(DirectCollectionChangeRecord.Null, new Integer JavaDoc(numberOfNewNulls));
264             } else {
265                 numberOfNewNulls *= -1;
266                 ((DirectCollectionChangeRecord)changeRecord).addRemoveChange(DirectCollectionChangeRecord.Null, new Integer JavaDoc(numberOfNewNulls));
267             }
268         }
269     }
270     
271     /**
272      * INTERNAL:
273      * This method compares the changes between two direct collections. Comparisons are made on equality
274      * not identity.
275      * @return prototype.changeset.ChangeRecord
276      */

277     public ChangeRecord compareForChange(Object JavaDoc clone, Object JavaDoc backUp, ObjectChangeSet owner, AbstractSession session) {
278         Object JavaDoc cloneAttribute = null;
279         Object JavaDoc backUpAttribute = null;
280         int numberOfNewNulls = 0;
281
282         ContainerPolicy cp = getContainerPolicy();
283
284         cloneAttribute = getAttributeValueFromObject(clone);
285
286         if ((cloneAttribute != null) && (!getIndirectionPolicy().objectIsInstantiated(cloneAttribute))) {
287             return null;
288         }
289
290         Object JavaDoc cloneObjectCollection = getRealCollectionAttributeValueFromObject(clone, session);
291
292         Object JavaDoc backUpCollection = null;
293
294         if (!owner.isNew()) {
295             backUpAttribute = getAttributeValueFromObject(backUp);
296
297             if ((backUpAttribute == null) && (cloneAttribute == null)) {
298                 return null;
299             }
300
301             backUpCollection = getRealCollectionAttributeValueFromObject(backUp, session);
302         }
303         DirectCollectionChangeRecord changeRecord = new DirectCollectionChangeRecord(owner);
304         changeRecord.setAttribute(getAttributeName());
305         changeRecord.setMapping(this);
306         compareCollectionsForChange(backUpCollection, cloneObjectCollection, changeRecord, session);
307         if (changeRecord.hasChanges()){
308             return changeRecord;
309         }
310         return null;
311     }
312
313     /**
314      * INTERNAL:
315      * Compare the attributes belonging to this mapping for the objects.
316      */

317     public boolean compareObjects(Object JavaDoc firstObject, Object JavaDoc secondObject, AbstractSession session) {
318         Object JavaDoc firstCollection = getRealCollectionAttributeValueFromObject(firstObject, session);
319         Object JavaDoc secondCollection = getRealCollectionAttributeValueFromObject(secondObject, session);
320         ContainerPolicy containerPolicy = getContainerPolicy();
321
322         if (containerPolicy.sizeFor(firstCollection) != containerPolicy.sizeFor(secondCollection)) {
323             return false;
324         }
325
326         HashMap firstCounter = new HashMap();
327         HashMap secondCounter = new HashMap();
328         for (Object JavaDoc iter = containerPolicy.iteratorFor(firstCollection);containerPolicy.hasNext(iter);) {
329             Object JavaDoc object = containerPolicy.next(iter, session);
330             if (firstCounter.containsKey(object)){
331                 int count = ((Integer JavaDoc)firstCounter.get(object)).intValue();
332                 firstCounter.put(object, new Integer JavaDoc(++count));
333             }else{
334                 firstCounter.put(object, new Integer JavaDoc(1));
335             }
336         }
337         for (Object JavaDoc iter = containerPolicy.iteratorFor(secondCollection);containerPolicy.hasNext(iter);) {
338             Object JavaDoc object = containerPolicy.next(iter, session);
339             if (secondCounter.containsKey(object)){
340                 int count = ((Integer JavaDoc)secondCounter.get(object)).intValue();
341                 secondCounter.put(object, new Integer JavaDoc(++count));
342             }else{
343                 secondCounter.put(object, new Integer JavaDoc(1));
344             }
345         }
346         for (Iterator iterator = firstCounter.keySet().iterator(); iterator.hasNext();){
347             Object JavaDoc object = iterator.next();
348             
349             if (!secondCounter.containsKey(object) || ( ((Integer JavaDoc)secondCounter.get(object)).intValue() != ((Integer JavaDoc)firstCounter.get(object)).intValue()) ) {
350                 return false;
351             }else{
352                 iterator.remove();
353                 secondCounter.remove(object);
354             }
355         }
356         if ( !firstCounter.isEmpty() || !secondCounter.isEmpty() ) {
357             return false;
358         }
359         return true;
360     }
361
362     /**
363      * INTERNAL:
364      * Convert all the class-name-based settings in this mapping to actual class-based
365      * settings
366      * This method is implemented by subclasses as necessary.
367      * @param classLoader
368      */

369     public void convertClassNamesToClasses(ClassLoader JavaDoc classLoader){
370         super.convertClassNamesToClasses(classLoader);
371         if (valueConverter != null && valueConverter instanceof TypeConversionConverter){
372             ((TypeConversionConverter)valueConverter).convertClassNamesToClasses(classLoader);
373         }
374     };
375
376
377     /**
378      * INTERNAL:
379      * Extract the source primary key value from the reference direct row.
380      * Used for batch reading, most following same order and fields as in the mapping.
381      */

382     protected Vector extractKeyFromReferenceRow(AbstractRecord row, AbstractSession session) {
383         Vector key = new Vector(getReferenceKeyFields().size());
384
385         for (int index = 0; index < getReferenceKeyFields().size(); index++) {
386             DatabaseField relationField = (DatabaseField)getReferenceKeyFields().elementAt(index);
387             DatabaseField sourceField = (DatabaseField)getSourceKeyFields().elementAt(index);
388             Object JavaDoc value = row.get(relationField);
389
390             // Must ensure the classificatin to get a cache hit.
391
try {
392                 value = session.getDatasourcePlatform().getConversionManager().convertObject(value, getDescriptor().getObjectBuilder().getFieldClassification(sourceField));
393             } catch (ConversionException e) {
394                 throw ConversionException.couldNotBeConverted(this, getDescriptor(), e);
395             }
396
397             key.addElement(value);
398         }
399
400         return key;
401     }
402
403     /**
404      * INTERNAL:
405      * Extract the primary key value from the source row.
406      * Used for batch reading, most following same order and fields as in the mapping.
407      */

408     protected Vector extractPrimaryKeyFromRow(AbstractRecord row, AbstractSession session) {
409         Vector key = new Vector(getSourceKeyFields().size());
410
411         for (Enumeration fieldEnum = getSourceKeyFields().elements(); fieldEnum.hasMoreElements();) {
412             DatabaseField field = (DatabaseField)fieldEnum.nextElement();
413             Object JavaDoc value = row.get(field);
414
415             // Must ensure the classificatin to get a cache hit.
416
try {
417                 value = session.getDatasourcePlatform().getConversionManager().convertObject(value, getDescriptor().getObjectBuilder().getFieldClassification(field));
418             } catch (ConversionException e) {
419                 throw ConversionException.couldNotBeConverted(this, getDescriptor(), e);
420             }
421
422             key.addElement(value);
423         }
424
425         return key;
426     }
427
428     protected ModifyQuery getDeleteQuery() {
429         if (changeSetDeleteQuery == null) {
430             changeSetDeleteQuery = new DataModifyQuery();
431         }
432         return changeSetDeleteQuery;
433     }
434
435     /**
436      * INTERNAL:
437      * Return the direct field.
438      * This is the field in the direct table to store the values.
439      */

440     public DatabaseField getDirectField() {
441         return directField;
442     }
443
444     /**
445      * PUBLIC:
446      * Returns the name of the field name in the reference table.
447      */

448     public String JavaDoc getDirectFieldName() {
449         if (getDirectField() == null) {
450             return null;
451         }
452         return getDirectField().getQualifiedName();
453     }
454
455     protected DataModifyQuery getInsertQuery() {
456         return insertQuery;
457     }
458
459     /**
460      * INTERNAL:
461      * This cannot be used with direct collection mappings.
462      */

463     public Class JavaDoc getReferenceClass() {
464         return null;
465     }
466
467     public String JavaDoc getReferenceClassName() {
468         return null;
469     }
470
471     /**
472      * INTERNAL:
473      * There is none on direct collection.
474      */

475     public ClassDescriptor getReferenceDescriptor() {
476         return null;
477     }
478
479     /**
480      * INTERNAL:
481      * Return the reference key field names associated with the mapping.
482      * These are in-order with the sourceKeyFieldNames.
483      */

484     public Vector getReferenceKeyFieldNames() {
485         Vector fieldNames = new Vector(getReferenceKeyFields().size());
486         for (Enumeration fieldsEnum = getReferenceKeyFields().elements();
487                  fieldsEnum.hasMoreElements();) {
488             fieldNames.addElement(((DatabaseField)fieldsEnum.nextElement()).getQualifiedName());
489         }
490
491         return fieldNames;
492     }
493
494     /**
495      * INTERNAL:
496      * Return the reference key fields.
497      */

498     public Vector getReferenceKeyFields() {
499         return referenceKeyFields;
500     }
501
502     /**
503      * INTERNAL:
504      * Return the direct table.
505      * This is the table to store the values.
506      */

507     public DatabaseTable getReferenceTable() {
508         return referenceTable;
509     }
510
511     /**
512      * PUBLIC:
513      * Returns the name of the reference table
514      */

515     public String JavaDoc getReferenceTableName() {
516         if (getReferenceTable() == null) {
517             return null;
518         }
519         return getReferenceTable().getName();
520     }
521
522     //This method is added to include table qualifier.
523

524     /**
525      * PUBLIC:
526      * Returns the qualified name of the reference table
527      */

528     public String JavaDoc getReferenceTableQualifiedName() {//CR#2407
529
if (getReferenceTable() == null) {
530             return null;
531         }
532         return getReferenceTable().getQualifiedName();
533     }
534
535     /**
536      * INTERNAL:
537      * Return the relationshipPartner mapping for this bi-directional mapping. If the relationshipPartner is null then
538      * this is a uni-directional mapping.
539      * DirectCollectionMapping can not be part of a bi-directional mapping
540      */

541     public DatabaseMapping getRelationshipPartner() {
542         return null;
543     }
544
545     /**
546      * PUBLIC:
547      * Return the source key field names associated with the mapping.
548      * These are in-order with the referenceKeyFieldNames.
549      */

550     public Vector getSourceKeyFieldNames() {
551         Vector fieldNames = new Vector(getSourceKeyFields().size());
552         for (Enumeration fieldsEnum = getSourceKeyFields().elements();
553                  fieldsEnum.hasMoreElements();) {
554             fieldNames.addElement(((DatabaseField)fieldsEnum.nextElement()).getQualifiedName());
555         }
556
557         return fieldNames;
558     }
559
560     /**
561      * INTERNAL:
562      * Return the source key fields.
563      */

564     public Vector getSourceKeyFields() {
565         return sourceKeyFields;
566     }
567
568     protected boolean hasCustomDeleteQuery() {
569         return hasCustomDeleteQuery;
570     }
571
572     protected boolean hasCustomInsertQuery() {
573         return hasCustomInsertQuery;
574     }
575
576     /**
577      * INTERNAL:
578      * Initialize and validate the mapping properties.
579      */

580     public void initialize(AbstractSession session) throws DescriptorException {
581         if (isKeyForSourceSpecified()) {
582             initializeSourceKeys(session);
583         } else {
584             initializeSourceKeysWithDefaults(session);
585         }
586
587         initializeReferenceTable(session);
588         initializeReferenceKeys(session);
589         initializeDirectField(session);
590         if (shouldInitializeSelectionCriteria()) {
591             initializeSelectionCriteria(session);
592             initializeSelectionStatement(session);
593         }
594         if (!getSelectionQuery().hasSessionName()) {
595             getSelectionQuery().setSessionName(session.getName());
596         }
597         if ((getValueConverter() != null) && (getSelectionQuery() instanceof DirectReadQuery)) {
598             ((DirectReadQuery)getSelectionQuery()).setValueConverter(getValueConverter());
599         }
600         initializeDeleteAllQuery(session);
601         initializeDeleteQuery(session);
602         initializeInsertQuery(session);
603         if (getValueConverter() != null) {
604             getValueConverter().initialize(this, session);
605         }
606         super.initialize(session);
607     }
608
609     /**
610      * Initialize delete all query. This query is used to delete the collection of objects from the
611      * reference table.
612      */

613     protected void initializeDeleteAllQuery(AbstractSession session) {
614         if (!getDeleteAllQuery().hasSessionName()) {
615             getDeleteAllQuery().setSessionName(session.getName());
616         }
617
618         if (hasCustomDeleteAllQuery()) {
619             return;
620         }
621
622         Expression expression = null;
623         Expression subExp1;
624         Expression subExp2;
625         Expression subExpression;
626         Expression builder = new ExpressionBuilder();
627         SQLDeleteStatement statement = new SQLDeleteStatement();
628
629         // Construct an expression to delete from the relation table.
630
for (int index = 0; index < getReferenceKeyFields().size(); index++) {
631             DatabaseField referenceKey = (DatabaseField)getReferenceKeyFields().elementAt(index);
632             DatabaseField sourceKey = (DatabaseField)getSourceKeyFields().elementAt(index);
633
634             subExp1 = builder.getField(referenceKey);
635             subExp2 = builder.getParameter(sourceKey);
636             subExpression = subExp1.equal(subExp2);
637
638             if (expression == null) {
639                 expression = subExpression;
640             } else {
641                 expression = expression.and(subExpression);
642             }
643         }
644
645         statement.setWhereClause(expression);
646         statement.setTable(getReferenceTable());
647         getDeleteAllQuery().setSQLStatement(statement);
648     }
649
650     protected void initializeDeleteQuery(AbstractSession session) {
651         if (!getDeleteQuery().hasSessionName()) {
652             getDeleteQuery().setSessionName(session.getName());
653         }
654
655         if (hasCustomDeleteQuery()) {
656             return;
657         }
658
659         Expression builder = new ExpressionBuilder();
660         Expression directExp = builder.getField(getDirectField()).equal(builder.getParameter(getDirectField()));
661         Expression expression = null;
662         SQLDeleteStatement statement = new SQLDeleteStatement();
663
664         // Construct an expression to delete from the relation table.
665
for (int index = 0; index < getReferenceKeyFields().size(); index++) {
666             DatabaseField referenceKey = (DatabaseField)getReferenceKeyFields().elementAt(index);
667             DatabaseField sourceKey = (DatabaseField)getSourceKeyFields().elementAt(index);
668
669             Expression subExp1 = builder.getField(referenceKey);
670             Expression subExp2 = builder.getParameter(sourceKey);
671             Expression subExpression = subExp1.equal(subExp2);
672
673             expression = subExpression.and(expression);
674         }
675         expression = expression.and(directExp);
676         statement.setWhereClause(expression);
677         statement.setTable(getReferenceTable());
678         getDeleteQuery().setSQLStatement(statement);
679     }
680
681     /**
682      * The field name on the reference table is initialized and cached.
683      */

684     protected void initializeDirectField(AbstractSession session) throws DescriptorException {
685         if (getDirectField() == null) {
686             throw DescriptorException.directFieldNameNotSet(this);
687         }
688
689         getDirectField().setTable(getReferenceTable());
690         getDirectField().setIndex(0);
691     }
692
693     /**
694      * Initialize insert query. This query is used to insert the collection of objects into the
695      * reference table.
696      */

697     protected void initializeInsertQuery(AbstractSession session) {
698         if (!getInsertQuery().hasSessionName()) {
699             getInsertQuery().setSessionName(session.getName());
700         }
701
702         if (hasCustomInsertQuery()) {
703             return;
704         }
705
706         SQLInsertStatement statement = new SQLInsertStatement();
707         statement.setTable(getReferenceTable());
708         AbstractRecord directRow = new DatabaseRecord();
709         for (Enumeration referenceEnum = getReferenceKeyFields().elements();
710                  referenceEnum.hasMoreElements();) {
711             directRow.put((DatabaseField)referenceEnum.nextElement(), null);
712         }
713         directRow.put(getDirectField(), null);
714         statement.setModifyRow(directRow);
715         getInsertQuery().setSQLStatement(statement);
716         getInsertQuery().setModifyRow(directRow);
717     }
718
719     /**
720      * There is no reference descriptor
721      */

722     protected void initializeReferenceDescriptor(AbstractSession session) {
723         ;
724     }
725
726     /**
727      * The reference keys on the reference table are initalized
728      */

729     protected void initializeReferenceKeys(AbstractSession session) throws DescriptorException {
730         if (getReferenceKeyFields().size() == 0) {
731             throw DescriptorException.noReferenceKeyIsSpecified(this);
732         }
733
734         for (Enumeration referenceEnum = getReferenceKeyFields().elements();
735                  referenceEnum.hasMoreElements();) {
736             DatabaseField field = (DatabaseField)referenceEnum.nextElement();
737             if (field.hasTableName() && (!(field.getTableName().equals(getReferenceTable().getName())))) {
738                 throw DescriptorException.referenceKeyFieldNotProperlySpecified(field, this);
739             }
740             field.setTable(getReferenceTable());
741         }
742     }
743
744     /*
745      * Set the table qualifier on the reference table if required
746      */

747     protected void initializeReferenceTable(AbstractSession session) throws DescriptorException {
748         Platform platform = session.getDatasourcePlatform();
749
750         if (getReferenceTable() == null) {
751             throw DescriptorException.referenceTableNotSpecified(this);
752         }
753
754         if (platform.getTableQualifier().length() == 0) {
755             return;
756         }
757
758         if (getReferenceTable().getTableQualifier().length() == 0) {
759             getReferenceTable().setTableQualifier(platform.getTableQualifier());
760         }
761     }
762
763     protected void initializeSelectionCriteria(AbstractSession session) {
764         Expression exp1;
765         Expression exp2;
766         Expression expression;
767         Expression criteria = null;
768         Enumeration referenceKeysEnum;
769         Enumeration sourceKeysEnum;
770         ExpressionBuilder base = new ExpressionBuilder();
771         TableExpression table = (TableExpression)base.getTable(getReferenceTable());
772
773         referenceKeysEnum = getReferenceKeyFields().elements();
774         sourceKeysEnum = getSourceKeyFields().elements();
775
776         for (; referenceKeysEnum.hasMoreElements();) {
777             DatabaseField referenceKey = (DatabaseField)referenceKeysEnum.nextElement();
778             DatabaseField sourceKey = (DatabaseField)sourceKeysEnum.nextElement();
779
780             exp1 = table.getField(referenceKey);
781             exp2 = base.getParameter(sourceKey);
782             expression = exp1.equal(exp2);
783
784             if (criteria == null) {
785                 criteria = expression;
786             } else {
787                 criteria = expression.and(criteria);
788             }
789         }
790
791         setSelectionCriteria(criteria);
792     }
793
794     /**
795      * The selection query is initialized
796      */

797     protected void initializeSelectionQuery(AbstractSession session) {
798         // Nothing required.
799
}
800
801     protected void initializeSelectionStatement(AbstractSession session) {
802         SQLSelectStatement statement = new SQLSelectStatement();
803         statement.addTable(getReferenceTable());
804         statement.addField((DatabaseField)getDirectField().clone());
805         statement.setWhereClause(getSelectionCriteria());
806         statement.normalize(session, null);
807         getSelectionQuery().setSQLStatement(statement);
808     }
809
810     /**
811      * The source keys are initalized
812      */

813     protected void initializeSourceKeys(AbstractSession session) {
814         for (Enumeration sourceKeyEnum = getSourceKeyFields().elements();
815                  sourceKeyEnum.hasMoreElements();) {
816             getDescriptor().buildField((DatabaseField)sourceKeyEnum.nextElement());
817         }
818     }
819
820     /**
821      * INTERNAL:
822      * If a user does not specify the source key then the primary keys of the source table are used.
823      */

824     protected void initializeSourceKeysWithDefaults(AbstractSession session) {
825         List primaryKeyFields = getDescriptor().getPrimaryKeyFields();
826         for (int index = 0; index < primaryKeyFields.size(); index++) {
827             getSourceKeyFields().addElement(primaryKeyFields.get(index));
828         }
829     }
830
831     /**
832      * INTERNAL:
833      */

834     public boolean isDirectCollectionMapping() {
835         return true;
836     }
837
838     /**
839      * INTERNAL:
840      * Checks if source and target keys are mentioned by the user or not.
841      */

842     protected boolean isKeyForSourceSpecified() {
843         return !getSourceKeyFields().isEmpty();
844     }
845
846     /**
847      * INTERNAL:
848      * Return true if referenced objects are provately owned else false.
849      */

850     public boolean isPrivateOwned() {
851         return true;
852     }
853
854     /**
855      * INTERNAL:
856      * Iterate on the attribute value.
857      * The value holder has already been processed.
858      * PERF: Avoid iteration if not required.
859      */

860     public void iterateOnRealAttributeValue(DescriptorIterator iterator, Object JavaDoc realAttributeValue) {
861         if (iterator.shouldIterateOnPrimitives()) {
862             super.iterateOnRealAttributeValue(iterator, realAttributeValue);
863         }
864     }
865
866     /**
867      * INTERNAL:
868      * Iterate on the specified element.
869      */

870     public void iterateOnElement(DescriptorIterator iterator, Object JavaDoc element) {
871         iterator.iteratePrimitiveForMapping(element, this);
872     }
873
874     /**
875      * INTERNAL:
876      * Merge changes from the source to the target object.
877      * Because this is a collection mapping, values are added to or removed from the
878      * collection based on the changeset
879      */

880     public void mergeChangesIntoObject(Object JavaDoc target, ChangeRecord changeRecord, Object JavaDoc source, MergeManager mergeManager) {
881         ContainerPolicy containerPolicy = getContainerPolicy();
882         Object JavaDoc valueOfTarget = null;
883         AbstractSession session = mergeManager.getSession();
884
885         //collect the changes into a vector
886
HashMap addObjects = ((DirectCollectionChangeRecord)changeRecord).getAddObjectMap();
887         HashMap removeObjects = ((DirectCollectionChangeRecord)changeRecord).getRemoveObjectMap();
888
889         //Check to see if the target has an instantiated collection
890
if ((isAttributeValueInstantiated(target)) && (!changeRecord.getOwner().isNew())) {
891             valueOfTarget = getRealCollectionAttributeValueFromObject(target, session);
892         } else {
893             //if not create an instance of the collection
894
valueOfTarget = containerPolicy.containerInstance(addObjects.size());
895         }
896         if (!isAttributeValueInstantiated(target)) {
897             if (mergeManager.shouldMergeChangesIntoDistributedCache()) {
898                 return;
899             }
900             for (Object JavaDoc iterator = containerPolicy.iteratorFor(getRealCollectionAttributeValueFromObject(source, session));
901                      containerPolicy.hasNext(iterator);) {
902                 containerPolicy.addInto(containerPolicy.next(iterator, session), valueOfTarget, session);
903             }
904         } else {
905             synchronized (valueOfTarget) {
906                 // Next iterate over the changes and add them to the container
907
for (Iterator iterator = addObjects.keySet().iterator(); iterator.hasNext(); ){
908                     Object JavaDoc object = iterator.next();
909                     int objectCount = ((Integer JavaDoc)addObjects.get(object)).intValue();
910                     for (int i = 0; i < objectCount; ++i) {
911                         if (mergeManager.shouldMergeChangesIntoDistributedCache()) {
912                             //bug#4458089 and 4454532- check if collection contains new item before adding during merge into distributed cache
913
if (!containerPolicy.contains(object, valueOfTarget, session)) {
914                                 containerPolicy.addInto(object, valueOfTarget, session);
915                             }
916                         } else {
917                             containerPolicy.addInto(object, valueOfTarget, session);
918                         }
919                     }
920                 }
921                 for (Iterator iterator = removeObjects.keySet().iterator(); iterator.hasNext(); ){
922                     Object JavaDoc object = iterator.next();
923                     int objectCount = ((Integer JavaDoc)removeObjects.get(object)).intValue();
924                     for (int i = 0; i < objectCount; ++i) {
925                         containerPolicy.removeFrom(object, valueOfTarget, session);
926                     }
927                 }
928             }
929         }
930         setRealAttributeValueInObject(target, valueOfTarget);
931     }
932
933     /**
934      * INTERNAL:
935      * Merge changes from the source to the target object.
936      */

937     public void mergeIntoObject(Object JavaDoc target, boolean isTargetUnInitialized, Object JavaDoc source, MergeManager mergeManager) {
938         if (isTargetUnInitialized) {
939             // This will happen if the target object was removed from the cache before the commit was attempted
940
if (mergeManager.shouldMergeWorkingCopyIntoOriginal() && (!isAttributeValueInstantiated(source))) {
941                 setAttributeValueInObject(target, getIndirectionPolicy().getOriginalIndirectionObject(getAttributeValueFromObject(source), mergeManager.getSession()));
942                 return;
943             }
944         }
945         if (!shouldMergeCascadeReference(mergeManager)) {
946             // This is only going to happen on mergeClone, and we should not attempt to merge the reference
947
return;
948         }
949         if (mergeManager.shouldMergeOriginalIntoWorkingCopy()) {
950             if (!isAttributeValueInstantiated(target)) {
951                 // This will occur when the clone's value has not been instantiated yet and we do not need
952
// the refresh that attribute
953
return;
954             }
955         } else if (!isAttributeValueInstantiated(source)) {
956             // I am merging from a clone into an original. No need to do merge if the attribute was never
957
// modified
958
return;
959         }
960
961         ContainerPolicy containerPolicy = getContainerPolicy();
962         Object JavaDoc valueOfSource = getRealCollectionAttributeValueFromObject(source, mergeManager.getSession());
963
964         // trigger instantiation of target attribute
965
Object JavaDoc valueOfTarget = getRealCollectionAttributeValueFromObject(target, mergeManager.getSession());
966         Object JavaDoc newContainer = containerPolicy.containerInstance(containerPolicy.sizeFor(valueOfSource));
967         boolean fireChangeEvents = false;
968         valueOfTarget = newContainer;
969         for (Object JavaDoc sourceValuesIterator = containerPolicy.iteratorFor(valueOfSource);
970                  containerPolicy.hasNext(sourceValuesIterator);) {
971             Object JavaDoc sourceValue = containerPolicy.next(sourceValuesIterator, mergeManager.getSession());
972             containerPolicy.addInto(sourceValue, valueOfTarget, mergeManager.getSession());
973         }
974
975         // Must re-set variable to allow for set method to re-morph changes if the collection is not being stored directly.
976
setRealAttributeValueInObject(target, valueOfTarget);
977     }
978
979     /**
980      * INTERNAL:
981      * Perform the commit event.
982      * This is used in the uow to delay data modifications.
983      */

984     public void performDataModificationEvent(Object JavaDoc[] event, AbstractSession session) throws DatabaseException, DescriptorException {
985         // Hey I might actually want to use an inner class here... ok array for now.
986
if (event[0] == Delete){
987             session.executeQuery((DataModifyQuery)event[1], (AbstractRecord)event[(2)]);
988         } else if (event[0] == Insert) {
989             session.executeQuery((DataModifyQuery)event[1], (AbstractRecord)event[(2)]);
990         } else if (event[0] == DeleteAll) {
991             preDelete((DeleteObjectQuery)event[1]);
992         } else {
993             throw DescriptorException.invalidDataModificationEventCode(event[0], this);
994         }
995     }
996
997     /**
998      * INTERNAL:
999      * Insert the private owned object.
1000     */

1001    public void postInsert(WriteObjectQuery query) throws DatabaseException {
1002        Object JavaDoc objects;
1003        AbstractRecord databaseRow = new DatabaseRecord();
1004
1005        if (isReadOnly()) {
1006            return;
1007        }
1008
1009        objects = getRealCollectionAttributeValueFromObject(query.getObject(), query.getSession());
1010        ContainerPolicy containerPolicy = getContainerPolicy();
1011        if (containerPolicy.isEmpty(objects)) {
1012            return;
1013        }
1014
1015        prepareTranslationRow(query.getTranslationRow(), query.getObject(), query.getSession());
1016        // Extract primary key and value from the source.
1017
for (int index = 0; index < getReferenceKeyFields().size(); index++) {
1018            DatabaseField referenceKey = (DatabaseField)getReferenceKeyFields().elementAt(index);
1019            DatabaseField sourceKey = (DatabaseField)getSourceKeyFields().elementAt(index);
1020            Object JavaDoc sourceKeyValue = query.getTranslationRow().get(sourceKey);
1021            databaseRow.put(referenceKey, sourceKeyValue);
1022        }
1023
1024        // Extract target field and its value. Construct insert statement and execute it
1025
for (Object JavaDoc iter = containerPolicy.iteratorFor(objects); containerPolicy.hasNext(iter);) {
1026            Object JavaDoc object = containerPolicy.next(iter, query.getSession());
1027            if (getValueConverter() != null) {
1028                object = getValueConverter().convertObjectValueToDataValue(object, query.getSession());
1029            }
1030            databaseRow.put(getDirectField(), object);
1031
1032            // In the uow data queries are cached until the end of the commit.
1033
if (query.shouldCascadeOnlyDependentParts()) {
1034                // Hey I might actually want to use an inner class here... ok array for now.
1035
Object JavaDoc[] event = new Object JavaDoc[3];
1036                event[0] = Insert;
1037                event[1] = getInsertQuery();
1038                event[2] = databaseRow.clone();
1039                query.getSession().getCommitManager().addDataModificationEvent(this, event);
1040            } else {
1041                query.getSession().executeQuery(getInsertQuery(), databaseRow);
1042            }
1043        }
1044    }
1045
1046    /**
1047     * INTERNAL:
1048     * Update private owned part.
1049     */

1050    public void postUpdate(WriteObjectQuery writeQuery) throws DatabaseException {
1051        if (isReadOnly()) {
1052            return;
1053        }
1054
1055        if (writeQuery.getObjectChangeSet() != null){
1056            postUpdateWithChangeSet(writeQuery);
1057            return;
1058        }
1059        // If objects are not instantiated that means they are not changed.
1060
if (!isAttributeValueInstantiated(writeQuery.getObject())) {
1061            return;
1062        }
1063
1064        if (writeQuery.getSession().isUnitOfWork()) {
1065            if (compareObjects(writeQuery.getObject(), writeQuery.getBackupClone(), writeQuery.getSession())) {
1066                return;// Nothing has changed, no work required
1067
}
1068        }
1069
1070        DeleteObjectQuery deleteQuery = new DeleteObjectQuery();
1071        deleteQuery.setObject(writeQuery.getObject());
1072        deleteQuery.setSession(writeQuery.getSession());
1073        deleteQuery.setTranslationRow(writeQuery.getTranslationRow());
1074
1075        if (writeQuery.shouldCascadeOnlyDependentParts()) {
1076            // Hey I might actually want to use an inner class here... ok array for now.
1077
Object JavaDoc[] event = new Object JavaDoc[3];
1078            event[0] = DeleteAll;
1079            event[1] = deleteQuery;
1080            writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
1081        } else {
1082            preDelete(deleteQuery);
1083        }
1084        postInsert(writeQuery);
1085    }
1086
1087    /**
1088     * INTERNAL:
1089     * Update private owned part.
1090     */

1091    protected void postUpdateWithChangeSet(WriteObjectQuery writeQuery) throws DatabaseException {
1092
1093        ObjectChangeSet changeSet = writeQuery.getObjectChangeSet();
1094        DirectCollectionChangeRecord changeRecord = (DirectCollectionChangeRecord)changeSet.getChangesForAttributeNamed(this.getAttributeName());
1095        if (changeRecord == null){
1096            return;
1097        }
1098        for (int index = 0; index < getReferenceKeyFields().size(); index++) {
1099            DatabaseField referenceKey = (DatabaseField)getReferenceKeyFields().elementAt(index);
1100            DatabaseField sourceKey = (DatabaseField)getSourceKeyFields().elementAt(index);
1101            Object JavaDoc sourceKeyValue = writeQuery.getTranslationRow().get(sourceKey);
1102            writeQuery.getTranslationRow().put(referenceKey, sourceKeyValue);
1103        }
1104        for (Iterator iterator = changeRecord.getRemoveObjectMap().keySet().iterator(); iterator.hasNext();){
1105            Object JavaDoc object = iterator.next();
1106            AbstractRecord thisRow = (AbstractRecord)writeQuery.getTranslationRow().clone();
1107            Object JavaDoc value = object;
1108            if (getValueConverter() != null){
1109                value = getValueConverter().convertObjectValueToDataValue(value, writeQuery.getSession());
1110            }
1111            if (value == DirectCollectionChangeRecord.Null){
1112                thisRow.add(getDirectField(), null);
1113            }else{
1114                thisRow.add(getDirectField(), value);
1115            }
1116            // Hey I might actually want to use an inner class here... ok array for now.
1117
Object JavaDoc[] event = new Object JavaDoc[3];
1118            event[0] = Delete;
1119            event[1] = getDeleteQuery();
1120            event[2] = thisRow;
1121            writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
1122            Integer JavaDoc count = (Integer JavaDoc)changeRecord.getCommitAddMap().get(object);
1123            if (count != null){
1124                for (int counter = count.intValue(); counter > 0; --counter){
1125                    thisRow = (AbstractRecord)writeQuery.getTranslationRow().clone();
1126                    thisRow.add(getDirectField(), value);
1127                    // Hey I might actually want to use an inner class here... ok array for now.
1128
event = new Object JavaDoc[3];
1129                    event[0] = Insert;
1130                    event[1] = getInsertQuery();
1131                    event[2] = thisRow;
1132                    writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
1133                }
1134            }
1135        }
1136        for (Iterator iterator = changeRecord.getAddObjectMap().keySet().iterator(); iterator.hasNext();){
1137            Object JavaDoc object = iterator.next();
1138            Integer JavaDoc count = (Integer JavaDoc)changeRecord.getAddObjectMap().get(object);
1139            for (int counter = count.intValue(); counter > 0; --counter){
1140                AbstractRecord thisRow = (AbstractRecord)writeQuery.getTranslationRow().clone();
1141                Object JavaDoc value = object;
1142                if (getValueConverter() != null){
1143                    value = getValueConverter().convertObjectValueToDataValue(value, writeQuery.getSession());
1144                }
1145                if (value == DirectCollectionChangeRecord.Null){ //special placeholder for nulls
1146
thisRow.add(getDirectField(), null);
1147                }else{
1148                    thisRow.add(getDirectField(), value);
1149                }
1150                // Hey I might actually want to use an inner class here... ok array for now.
1151
Object JavaDoc[] event = new Object JavaDoc[3];
1152                event[0] = Insert;
1153                event[1] = getInsertQuery();
1154                event[2] = thisRow;
1155                writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
1156            }
1157        }
1158    }
1159
1160    /**
1161     * INTERNAL:
1162     * Delete private owned part. Which is a collection of objects from the reference table.
1163     */

1164    public void preDelete(DeleteObjectQuery query) throws DatabaseException {
1165        if (isReadOnly()) {
1166            return;
1167        }
1168
1169        prepareTranslationRow(query.getTranslationRow(), query.getObject(), query.getSession());
1170        query.getSession().executeQuery(getDeleteAllQuery(), query.getTranslationRow());
1171    }
1172    
1173    /**
1174     * INTERNAL:
1175     * The translation row may require additional fields than the primary key if the mapping in not on the primary key.
1176     */

1177    protected void prepareTranslationRow(AbstractRecord translationRow, Object JavaDoc object, AbstractSession session) {
1178        // Make sure that each source key field is in the translation row.
1179
for (Enumeration sourceFieldsEnum = getSourceKeyFields().elements();
1180                 sourceFieldsEnum.hasMoreElements();) {
1181            DatabaseField sourceKey = (DatabaseField)sourceFieldsEnum.nextElement();
1182            if (!translationRow.containsKey(sourceKey)) {
1183                Object JavaDoc value = getDescriptor().getObjectBuilder().extractValueFromObjectForField(object, sourceKey, session);
1184                translationRow.put(sourceKey, value);
1185            }
1186        }
1187    }
1188
1189    protected void setDeleteQuery(ModifyQuery query) {
1190        this.changeSetDeleteQuery = query;
1191    }
1192
1193    /**
1194     * PUBLIC:
1195     * Set the receiver's delete SQL string. This allows the user to override the SQL
1196     * generated by TopLink, with there own SQL or procedure call. The arguments are
1197     * translated from the fields of the source row, through replacing the field names
1198     * marked by '#' with the values for those fields.
1199     * This SQL is responsible for doing the deletion required by the mapping,
1200     * such as deletion from join table for M-M.
1201     * Example, 'delete from RESPONS where EMP_ID = #EMP_ID and DESCRIP = #DESCRIP'.
1202     */

1203    public void setDeleteSQLString(String JavaDoc sqlString) {
1204        DataModifyQuery query = new DataModifyQuery();
1205        query.setSQLString(sqlString);
1206        setCustomDeleteQuery(query);
1207    }
1208
1209    /**
1210     * ADVANCED:
1211     * Configure the mapping to use a container policy.
1212     * The policy manages the access to the collection.
1213     */

1214    public void setContainerPolicy(ContainerPolicy containerPolicy) {
1215        this.containerPolicy = containerPolicy;
1216        ((DataReadQuery)getSelectionQuery()).setContainerPolicy(containerPolicy);
1217    }
1218
1219    /**
1220     * PUBLIC:
1221     * The default delete query for this mapping can be overridden by specifying the new query.
1222     * This query is responsible for doing the deletion required by the mapping,
1223     * such as deletion from join table for M-M. The query should delete a specific row from the
1224     * DirectCollectionTable bases on the DirectField.
1225     */

1226    public void setCustomDeleteQuery(ModifyQuery query) {
1227        setDeleteQuery(query);
1228        setHasCustomDeleteQuery(true);
1229    }
1230
1231    /**
1232     * PUBLIC:
1233     * The default insert query for mapping can be overridden by specifying the new query.
1234     * This query inserts the row into the direct table.
1235     */

1236    public void setCustomInsertQuery(DataModifyQuery query) {
1237        setInsertQuery(query);
1238        setHasCustomInsertQuery(true);
1239    }
1240
1241    protected void setDirectField(DatabaseField field) {
1242        directField = field;
1243    }
1244
1245    /**
1246     * PUBLIC:
1247     * Set the direct field name in the reference table.
1248     * This is the field that the primitive data value is stored in.
1249     */

1250    public void setDirectFieldName(String JavaDoc fieldName) {
1251        setDirectField(new DatabaseField(fieldName));
1252    }
1253
1254    protected void setHasCustomDeleteQuery(boolean bool) {
1255        hasCustomDeleteQuery = bool;
1256    }
1257
1258    protected void setHasCustomInsertQuery(boolean bool) {
1259        hasCustomInsertQuery = bool;
1260    }
1261
1262    protected void setInsertQuery(DataModifyQuery insertQuery) {
1263        this.insertQuery = insertQuery;
1264    }
1265
1266    /**
1267     * PUBLIC:
1268     * Set the receiver's insert SQL string. This allows the user to override the SQL
1269     * generated by TopLink, with there own SQL or procedure call. The arguments are
1270     * translated from the fields of the source row, through replacing the field names
1271     * marked by '#' with the values for those fields.
1272     * This is used to insert an entry into the direct table.
1273     * Example, 'insert into RESPONS (EMP_ID, RES_DESC) values (#EMP_ID, #RES_DESC)'.
1274     */

1275    public void setInsertSQLString(String JavaDoc sqlString) {
1276        DataModifyQuery query = new DataModifyQuery();
1277        query.setSQLString(sqlString);
1278        setCustomInsertQuery(query);
1279    }
1280
1281    /**
1282     * INTERNAL:
1283     * This cannot be used with direct collection mappings.
1284     */

1285    public void setReferenceClass(Class JavaDoc referenceClass) {
1286        return;
1287    }
1288
1289    public void setReferenceClassName(String JavaDoc referenceClassName) {
1290        return;
1291    }
1292
1293    /**
1294     * PUBLIC:
1295     * Set the name of the reference key field.
1296     * This is the foreign key field in the direct table referencing the primary key of the source object.
1297     * This method is used if the reference key consists of only a single field.
1298     */

1299    public void setReferenceKeyFieldName(String JavaDoc fieldName) {
1300        getReferenceKeyFields().addElement(new DatabaseField(fieldName));
1301    }
1302
1303    /**
1304     * INTERNAL:
1305     * Set the reference key field names associated with the mapping.
1306     * These must be in-order with the sourceKeyFieldNames.
1307     */

1308    public void setReferenceKeyFieldNames(Vector fieldNames) {
1309        Vector fields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(fieldNames.size());
1310        for (Enumeration fieldNamesEnum = fieldNames.elements(); fieldNamesEnum.hasMoreElements();) {
1311            fields.addElement(new DatabaseField((String JavaDoc)fieldNamesEnum.nextElement()));
1312        }
1313
1314        setReferenceKeyFields(fields);
1315    }
1316
1317    /**
1318     * INTERNAL:
1319     * Set the reference fields.
1320     */

1321    public void setReferenceKeyFields(Vector aVector) {
1322        this.referenceKeyFields = aVector;
1323    }
1324
1325    protected void setReferenceTable(DatabaseTable table) {
1326        referenceTable = table;
1327    }
1328
1329    /**
1330     * PUBLIC:
1331     * Set the reference table name.
1332     */

1333    public void setReferenceTableName(String JavaDoc tableName) {
1334        if (tableName == null) {
1335            setReferenceTable(null);
1336        } else {
1337            setReferenceTable(new DatabaseTable(tableName));
1338        }
1339    }
1340
1341    /**
1342     * PUBLIC:
1343     * Set the name of the session to execute the mapping's queries under.
1344     * This can be used by the session broker to override the default session
1345     * to be used for the target class.
1346     */

1347    public void setSessionName(String JavaDoc name) {
1348        super.setSessionName(name);
1349        getInsertQuery().setSessionName(name);
1350    }
1351
1352    /**
1353     * INTERNAL:
1354     * Set the source key field names associated with the mapping.
1355     * These must be in-order with the referenceKeyFieldNames.
1356     */

1357    public void setSourceKeyFieldNames(Vector fieldNames) {
1358        Vector fields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(fieldNames.size());
1359        for (Enumeration fieldNamesEnum = fieldNames.elements(); fieldNamesEnum.hasMoreElements();) {
1360            fields.addElement(new DatabaseField((String JavaDoc)fieldNamesEnum.nextElement()));
1361        }
1362
1363        setSourceKeyFields(fields);
1364    }
1365
1366    /**
1367     * INTERNAL:
1368     * Set the source fields.
1369     */

1370    public void setSourceKeyFields(Vector sourceKeyFields) {
1371        this.sourceKeyFields = sourceKeyFields;
1372    }
1373
1374    /**
1375     * INTERNAL:
1376     * Used by AttributeLevelChangeTracking to update a changeRecord with calculated changes
1377     * as apposed to detected changes. If an attribute can not be change tracked it's
1378     * changes can be detected through this process.
1379     */

1380    public void calculateDeferredChanges(ChangeRecord changeRecord, AbstractSession session){
1381        DirectCollectionChangeRecord collectionRecord = (DirectCollectionChangeRecord) changeRecord;
1382        compareCollectionsForChange(collectionRecord.getOriginalCollection(), collectionRecord.getLatestCollection(), collectionRecord, session);
1383    }
1384
1385    /**
1386     * ADVANCED:
1387     * This method is used to have an object add to a collection once the changeSet is applied
1388     * The referenceKey parameter should only be used for direct Maps.
1389
1390     */

1391    public void simpleAddToCollectionChangeRecord(Object JavaDoc referenceKey, Object JavaDoc objectToAdd, ObjectChangeSet changeSet, AbstractSession session) {
1392        DirectCollectionChangeRecord collectionChangeRecord = (DirectCollectionChangeRecord)changeSet.getChangesForAttributeNamed(getAttributeName());
1393        if (collectionChangeRecord == null) {
1394            collectionChangeRecord = new DirectCollectionChangeRecord(changeSet);
1395            collectionChangeRecord.setAttribute(getAttributeName());
1396            collectionChangeRecord.setMapping(this);
1397            changeSet.addChange(collectionChangeRecord);
1398            Object JavaDoc collection = getRealAttributeValueFromObject(changeSet.getUnitOfWorkClone(), session);
1399            collectionChangeRecord.storeDatabaseCounts(collection, getContainerPolicy(), session);
1400        }
1401        collectionChangeRecord.addAdditionChange(objectToAdd, new Integer JavaDoc(1));
1402    }
1403
1404   /**
1405     * ADVANCED:
1406     * This method is used to have an object removed from a collection once the changeSet is applied
1407     * The referenceKey parameter should only be used for direct Maps.
1408     */

1409    public void simpleRemoveFromCollectionChangeRecord(Object JavaDoc referenceKey, Object JavaDoc objectToRemove, ObjectChangeSet changeSet, AbstractSession session) {
1410        DirectCollectionChangeRecord collectionChangeRecord = (DirectCollectionChangeRecord)changeSet.getChangesForAttributeNamed(getAttributeName());
1411        if (collectionChangeRecord == null) {
1412            collectionChangeRecord = new DirectCollectionChangeRecord(changeSet);
1413            collectionChangeRecord.setAttribute(getAttributeName());
1414            collectionChangeRecord.setMapping(this);
1415            changeSet.addChange(collectionChangeRecord);
1416            Object JavaDoc collection = getRealAttributeValueFromObject(changeSet.getUnitOfWorkClone(), session);
1417            collectionChangeRecord.storeDatabaseCounts(collection, getContainerPolicy(), session);
1418        }
1419        collectionChangeRecord.addRemoveChange(objectToRemove, new Integer JavaDoc(1));
1420    }
1421
1422    /**
1423     * INTERNAL:
1424     * Either create a new change record or update with the new value. This is used
1425     * by attribute change tracking.
1426     * Specifically in a collection mapping this will be called when the customer
1427     * Set a new collection. In this case we will need to mark the change record
1428     * with the new and the old versions of the collection.
1429     * And mark the ObjectChangeSet with the attribute name then when the changes are calculated
1430     * force a compare on the collections to determine changes.
1431     */

1432    public void updateChangeRecord(Object JavaDoc clone, Object JavaDoc newValue, Object JavaDoc oldValue, ObjectChangeSet objectChangeSet, UnitOfWorkImpl uow) {
1433        DirectCollectionChangeRecord collectionChangeRecord = (DirectCollectionChangeRecord)objectChangeSet.getChangesForAttributeNamed(this.getAttributeName());
1434        if (collectionChangeRecord == null) {
1435            collectionChangeRecord = new DirectCollectionChangeRecord(objectChangeSet);
1436            collectionChangeRecord.setAttribute(getAttributeName());
1437            collectionChangeRecord.setMapping(this);
1438            objectChangeSet.addChange(collectionChangeRecord);
1439        }
1440        if (collectionChangeRecord.getOriginalCollection() == null){
1441            collectionChangeRecord.setOriginalCollection(oldValue);
1442        }
1443        collectionChangeRecord.setLatestCollection(newValue);
1444        
1445        objectChangeSet.deferredDetectionRequiredOn(getAttributeName());
1446    }
1447
1448    /**
1449     * PUBLIC:
1450     * Configure the mapping to use an instance of the specified container class
1451     * to hold the target objects.
1452     * <p>jdk1.2.x: The container class must implement (directly or indirectly) the Collection interface.
1453     * <p>jdk1.1.x: The container class must be a subclass of Vector.
1454     */

1455    public void useCollectionClass(Class JavaDoc concreteClass) {
1456        ContainerPolicy policy = ContainerPolicy.buildPolicyFor(concreteClass);
1457        setContainerPolicy(policy);
1458    }
1459
1460    /**
1461     * PUBLIC:
1462     * It is illegal to use a Map as the container of a DirectCollectionMapping. Only
1463     * Collection containers are supported for DirectCollectionMappings.
1464     * @see oracle.toplink.essentials.mappings.DirectMapMapping
1465     */

1466    public void useMapClass(Class JavaDoc concreteClass, String JavaDoc methodName) {
1467        throw ValidationException.illegalUseOfMapInDirectCollection(this, concreteClass, methodName);
1468    }
1469
1470    /**
1471     * INTERNAL:
1472     * Return the value of the reference attribute or a value holder.
1473     * Check whether the mapping's attribute should be optimized through batch and joining.
1474     * Overridden to support flasback/historical queries.
1475     */

1476    public Object JavaDoc valueFromRow(AbstractRecord row, JoinedAttributeManager joinManager, ObjectBuildingQuery query, AbstractSession session) throws DatabaseException {
1477        ReadQuery targetQuery = getSelectionQuery();
1478        return getIndirectionPolicy().valueFromQuery(targetQuery, row, query.getSession());
1479    }
1480
1481    /**
1482     * INTERNAL:
1483     * Checks if object is deleted from the database or not.
1484     */

1485    public boolean verifyDelete(Object JavaDoc object, AbstractSession session) throws DatabaseException {
1486        // Row is built for translation
1487
if (isReadOnly()) {
1488            return true;
1489        }
1490
1491        AbstractRecord row = getDescriptor().getObjectBuilder().buildRowForTranslation(object, session);
1492        Object JavaDoc value = session.executeQuery(getSelectionQuery(), row);
1493
1494        return getContainerPolicy().isEmpty(value);
1495    }
1496
1497    /**
1498     * INTERNAL:
1499     * Add a new value and its change set to the collection change record. This is used by
1500     * attribute change tracking.
1501     */

1502    public void addToCollectionChangeRecord(Object JavaDoc newKey, Object JavaDoc newValue, ObjectChangeSet objectChangeSet, UnitOfWorkImpl uow) {
1503        if (newValue == null) {
1504            newValue = DirectCollectionChangeRecord.Null;
1505        }
1506        ClassDescriptor descriptor;
1507        DirectCollectionChangeRecord collectionChangeRecord = (DirectCollectionChangeRecord)objectChangeSet.getChangesForAttributeNamed(this.getAttributeName());
1508        if (collectionChangeRecord == null) {
1509            collectionChangeRecord = new DirectCollectionChangeRecord(objectChangeSet);
1510            collectionChangeRecord.setAttribute(getAttributeName());
1511            collectionChangeRecord.setMapping(this);
1512            objectChangeSet.addChange(collectionChangeRecord);
1513            Object JavaDoc collection = getRealAttributeValueFromObject(objectChangeSet.getUnitOfWorkClone(), uow);
1514            collectionChangeRecord.storeDatabaseCounts(collection, getContainerPolicy(), uow);
1515        }
1516        collectionChangeRecord.addAdditionChange(newValue, new Integer JavaDoc(1));
1517    }
1518    
1519    /**
1520     * INTERNAL
1521     * Return true if this mapping supports cascaded version optimistic locking.
1522     */

1523    public boolean isCascadedLockingSupported() {
1524        return true;
1525    }
1526    
1527    /**
1528     * INTERNAL:
1529     * Return if this mapping supports change tracking.
1530     */

1531    public boolean isChangeTrackingSupported() {
1532        return true;
1533    }
1534
1535    /**
1536     * INTERNAL:
1537     * Remove a value and its change set from the collection change record. This is used by
1538     * attribute change tracking.
1539     */

1540    public void removeFromCollectionChangeRecord(Object JavaDoc newKey, Object JavaDoc newValue, ObjectChangeSet objectChangeSet, UnitOfWorkImpl uow) {
1541        if (newValue == null) {
1542            newValue = DirectCollectionChangeRecord.Null;
1543        }
1544        ClassDescriptor descriptor;
1545        DirectCollectionChangeRecord collectionChangeRecord = (DirectCollectionChangeRecord)objectChangeSet.getChangesForAttributeNamed(this.getAttributeName());
1546        if (collectionChangeRecord == null) {
1547            collectionChangeRecord = new DirectCollectionChangeRecord(objectChangeSet);
1548            collectionChangeRecord.setAttribute(getAttributeName());
1549            collectionChangeRecord.setMapping(this);
1550            objectChangeSet.addChange(collectionChangeRecord);
1551            Object JavaDoc collection = getRealAttributeValueFromObject(objectChangeSet.getUnitOfWorkClone(), uow);
1552            collectionChangeRecord.storeDatabaseCounts(collection, getContainerPolicy(), uow);
1553        }
1554        collectionChangeRecord.addRemoveChange(newValue, new Integer JavaDoc(1));
1555    }
1556
1557}
1558
Popular Tags