KickJava   Java API By Example, From Geeks To Geeks.

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


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.expressions.ObjectExpression;
28 import oracle.toplink.essentials.internal.helper.*;
29 import oracle.toplink.essentials.internal.sessions.*;
30 import oracle.toplink.essentials.sessions.DatabaseRecord;
31 import oracle.toplink.essentials.queryframework.*;
32 import oracle.toplink.essentials.internal.sessions.AbstractRecord;
33 import oracle.toplink.essentials.internal.sessions.AbstractSession;
34 import oracle.toplink.essentials.descriptors.ClassDescriptor;
35 import oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager;
36
37 /**
38  * <p><b>Purpose</b>: One to one mappings are used to represent a pointer references
39  * between two java objects. This mappings is usually represented by a single pointer
40  * (stored in an instance variable) between the source and target objects. In the relational
41  * database tables, these mappings are normally implemented using foreign keys.
42  *
43  * @author Sati
44  * @since TOPLink/Java 1.0
45  */

46 public class OneToOneMapping extends ObjectReferenceMapping implements RelationalMapping {
47
48     /** Maps the source foreign/primary key fields to the target primary/foreign key fields. */
49     protected Map sourceToTargetKeyFields;
50
51     /** Maps the target primary/foreign key fields to the source foreign/primary key fields. */
52     protected Map targetToSourceKeyFields;
53
54     /** Keeps track of which fields are foreign keys on a per field basis (can have mixed foreign key relationships). */
55     /** These are used for non-unit of work modification to check if the value of the 1-1 was changed and a deletion is required. */
56     protected boolean shouldVerifyDelete;
57     protected transient Expression privateOwnedCriteria;
58
59     /** Indicates whether the referenced object should always be joined on read queries. */
60     protected boolean usesJoining;
61     
62     /**
63      * PUBLIC:
64      * Default constructor.
65      */

66     public OneToOneMapping() {
67         this.selectionQuery = new ReadObjectQuery();
68         this.sourceToTargetKeyFields = new HashMap(2);
69         this.targetToSourceKeyFields = new HashMap(2);
70         this.foreignKeyFields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(1);
71         this.isForeignKeyRelationship = false;
72         this.shouldVerifyDelete = true;
73         this.usesJoining = false;
74     }
75
76     /**
77      * INTERNAL:
78      */

79     public boolean isRelationalMapping() {
80         return true;
81     }
82
83     /**
84      * PUBLIC:
85      * Define the foreign key relationship in the 1-1 mapping.
86      * This method is used for composite foreign key relationships,
87      * that is the source object's table has multiple foreign key fields to
88      * the target object's primary key fields.
89      * Both the source foreign key field and the target foreign key field must
90      * be specified.
91      * When a foreign key is specified TopLink will automatically populate the
92      * value for that field from the target object when the object is written to
93      * the database. If the foreign key is also mapped through a direct-to-field
94      * then the direct-to-field must be set read-only.
95      */

96     public void addForeignKeyField(DatabaseField sourceForeignKeyField, DatabaseField targetPrimaryKeyField) {
97         setIsForeignKeyRelationship(true);
98         getForeignKeyFields().addElement(sourceForeignKeyField);
99
100         getSourceToTargetKeyFields().put(sourceForeignKeyField, targetPrimaryKeyField);
101         getTargetToSourceKeyFields().put(targetPrimaryKeyField, sourceForeignKeyField);
102     }
103     
104     /**
105      * PUBLIC:
106      * Define the foreign key relationship in the 1-1 mapping.
107      * This method is used for composite foreign key relationships,
108      * that is the source object's table has multiple foreign key fields to
109      * the target object's primary key fields.
110      * Both the source foreign key field name and the target foreign key field
111      * name must be specified.
112      * When a foreign key is specified TopLink will automatically populate the
113      * value for that field from the target object when the object is written to
114      * the database. If the foreign key is also mapped through a direct-to-field
115      * then the direct-to-field must be set read-only.
116      */

117     public void addForeignKeyFieldName(String JavaDoc sourceForeignKeyFieldName, String JavaDoc targetPrimaryKeyFieldName) {
118         addForeignKeyField(new DatabaseField(sourceForeignKeyFieldName), new DatabaseField(targetPrimaryKeyFieldName));
119     }
120
121     /**
122      * PUBLIC:
123      * Define the target foreign key relationship in the 1-1 mapping.
124      * This method is used for composite target foreign key relationships,
125      * that is the target object's table has multiple foreign key fields to
126      * the source object's primary key fields.
127      * Both the target foreign key field and the source primary key field must
128      * be specified.
129      * The distinction between a foreign key and target foreign key is that the
130      * 1-1 mapping will not populate the target foreign key value when written
131      * (because it is in the target table). Normally 1-1's are through foreign
132      * keys but in bi-directional 1-1's the back reference will be a target
133      * foreign key. In obscure composite legacy data models a 1-1 may consist of
134      * a foreign key part and a target foreign key part, in this case both
135      * method will be called with the correct parts.
136      */

137     public void addTargetForeignKeyField(DatabaseField targetForeignKeyField, DatabaseField sourcePrimaryKeyField) {
138         getSourceToTargetKeyFields().put(sourcePrimaryKeyField, targetForeignKeyField);
139         getTargetToSourceKeyFields().put(targetForeignKeyField, sourcePrimaryKeyField);
140     }
141     
142     /**
143      * PUBLIC:
144      * Define the target foreign key relationship in the 1-1 mapping.
145      * This method is used for composite target foreign key relationships,
146      * that is the target object's table has multiple foreign key fields to
147      * the source object's primary key fields.
148      * Both the target foreign key field name and the source primary key field
149      * name must be specified.
150      * The distinction between a foreign key and target foreign key is that the
151      * 1-1 mapping will not populate the target foreign key value when written
152      * (because it is in the target table). Normally 1-1's are through foreign
153      * keys but in bi-directional 1-1's the back reference will be a target
154      * foreign key. In obscure composite legacy data models a 1-1 may consist of
155      * a foreign key part and a target foreign key part, in this case both
156      * method will be called with the correct parts.
157      */

158     public void addTargetForeignKeyFieldName(String JavaDoc targetForeignKeyFieldName, String JavaDoc sourcePrimaryKeyFieldName) {
159         addTargetForeignKeyField(new DatabaseField(targetForeignKeyFieldName), new DatabaseField(sourcePrimaryKeyFieldName));
160     }
161
162     /**
163      * INTERNAL:
164      * Used to allow object level comparisons.
165      */

166     public Expression buildObjectJoinExpression(Expression expression, Object JavaDoc value, AbstractSession session) {
167         Expression base = ((oracle.toplink.essentials.internal.expressions.ObjectExpression)expression).getBaseExpression();
168         Expression foreignKeyJoin = null;
169
170         // Allow for equal null.
171
if (value == null) {
172             // Can only perform null comparison on foreign key relationships.
173
// It does not really make sense for target any way as it is the source key.
174
if (!isForeignKeyRelationship()) {
175                 throw QueryException.cannotCompareTargetForeignKeysToNull(base, value, this);
176             }
177             for (Iterator sourceFieldsEnum = getSourceToTargetKeyFields().keySet().iterator();
178                      sourceFieldsEnum.hasNext();) {
179                 DatabaseField field = (DatabaseField)sourceFieldsEnum.next();
180                 Expression join = null;
181                 if (expression.isObjectExpression() && ((ObjectExpression)expression).shouldUseOuterJoin()){
182                     join = base.getField(field).equalOuterJoin(null);
183                 } else {
184                     join = base.getField(field).equal(null);
185                 }
186                 if (foreignKeyJoin == null) {
187                     foreignKeyJoin = join;
188                 } else {
189                     foreignKeyJoin = foreignKeyJoin.and(join);
190                 }
191             }
192         } else {
193             if (!getReferenceDescriptor().getJavaClass().isInstance(value)) {
194                 throw QueryException.incorrectClassForObjectComparison(base, value, this);
195             }
196
197             Enumeration keyEnum = extractKeyFromReferenceObject(value, session).elements();
198             for (Iterator sourceFieldsEnum = getSourceToTargetKeyFields().keySet().iterator();
199                      sourceFieldsEnum.hasNext();) {
200                 DatabaseField field = (DatabaseField)sourceFieldsEnum.next();
201                 Expression join = null;
202                 if (expression.isObjectExpression() && ((ObjectExpression)expression).shouldUseOuterJoin()){
203                     join = base.getField(field).equalOuterJoin(keyEnum.nextElement());
204                 } else {
205                     join = base.getField(field).equal(keyEnum.nextElement());
206                 }
207                 if (foreignKeyJoin == null) {
208                     foreignKeyJoin = join;
209                 } else {
210                     foreignKeyJoin = foreignKeyJoin.and(join);
211                 }
212             }
213         }
214         return foreignKeyJoin;
215     }
216
217     /**
218      * INTERNAL:
219      * Used to allow object level comparisons.
220      */

221     public Expression buildObjectJoinExpression(Expression expression, Expression argument, AbstractSession session) {
222         Expression base = ((oracle.toplink.essentials.internal.expressions.ObjectExpression)expression).getBaseExpression();
223         Expression foreignKeyJoin = null;
224         Iterator targetFieldsEnum = getSourceToTargetKeyFields().values().iterator();
225         for (Iterator sourceFieldsEnum = getSourceToTargetKeyFields().keySet().iterator();
226                  sourceFieldsEnum.hasNext();) {
227             DatabaseField sourceField = (DatabaseField)sourceFieldsEnum.next();
228             DatabaseField targetField = (DatabaseField)targetFieldsEnum.next();
229             Expression join = null;
230             if (expression.isObjectExpression() && ((ObjectExpression)expression).shouldUseOuterJoin()){
231                 join = base.getField(sourceField).equalOuterJoin(argument.getField(targetField));
232             } else {
233                 join = base.getField(sourceField).equal(argument.getField(targetField));
234             }
235             if (foreignKeyJoin == null) {
236                 foreignKeyJoin = join;
237             } else {
238                 foreignKeyJoin = foreignKeyJoin.and(join);
239             }
240         }
241         return foreignKeyJoin;
242     }
243
244     /**
245      * INTERNAL:
246      * This methods clones all the fields and ensures that each collection refers to
247      * the same clones.
248      */

249     public Object JavaDoc clone() {
250         OneToOneMapping clone = (OneToOneMapping)super.clone();
251         clone.setForeignKeyFields(oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(getForeignKeyFields().size()));
252         clone.setSourceToTargetKeyFields(new HashMap(getSourceToTargetKeyFields().size()));
253         clone.setTargetToSourceKeyFields(new HashMap(getTargetToSourceKeyFields().size()));
254         Hashtable setOfFields = new Hashtable(getTargetToSourceKeyFields().size());
255
256         //clone foreign keys and save the clones in a set
257
for (Enumeration enumtr = getForeignKeyFields().elements(); enumtr.hasMoreElements();) {
258             DatabaseField field = (DatabaseField)enumtr.nextElement();
259             DatabaseField fieldClone = (DatabaseField)field.clone();
260             setOfFields.put(field, fieldClone);
261             clone.getForeignKeyFields().addElement(fieldClone);
262         }
263
264         //get clones from set for source hashtable. If they do not exist, create a new one.
265
for (Iterator sourceEnum = getSourceToTargetKeyFields().keySet().iterator();
266                  sourceEnum.hasNext();) {
267             DatabaseField sourceField = (DatabaseField)sourceEnum.next();
268             DatabaseField targetField = (DatabaseField)getSourceToTargetKeyFields().get(sourceField);
269
270             DatabaseField targetClone;
271             DatabaseField sourceClone;
272
273             targetClone = (DatabaseField)setOfFields.get(targetField);
274             if (targetClone == null) {
275                 targetClone = (DatabaseField)targetField.clone();
276                 setOfFields.put(targetField, targetClone);
277             }
278             sourceClone = (DatabaseField)setOfFields.get(sourceField);
279             if (sourceClone == null) {
280                 sourceClone = (DatabaseField)sourceField.clone();
281                 setOfFields.put(sourceField, sourceClone);
282             }
283             clone.getSourceToTargetKeyFields().put(sourceClone, targetClone);
284         }
285
286         //get clones from set for target hashtable. If they do not exist, create a new one.
287
for (Iterator targetEnum = getTargetToSourceKeyFields().keySet().iterator();
288                  targetEnum.hasNext();) {
289             DatabaseField targetField = (DatabaseField)targetEnum.next();
290             DatabaseField sourceField = (DatabaseField)getTargetToSourceKeyFields().get(targetField);
291
292             DatabaseField targetClone;
293             DatabaseField sourceClone;
294
295             targetClone = (DatabaseField)setOfFields.get(targetField);
296             if (targetClone == null) {
297                 targetClone = (DatabaseField)targetField.clone();
298                 setOfFields.put(targetField, targetClone);
299             }
300             sourceClone = (DatabaseField)setOfFields.get(sourceField);
301             if (sourceClone == null) {
302                 sourceClone = (DatabaseField)sourceField.clone();
303                 setOfFields.put(sourceField, sourceClone);
304             }
305             clone.getTargetToSourceKeyFields().put(targetClone, sourceClone);
306         }
307         return clone;
308     }
309
310     /**
311      * PUBLIC:
312      * Indicates whether the referenced object should always be joined on read queries.
313      * Joining will join the two classes tables to read all of the data in a single query.
314      * This should only be used if it is know that the related objects are always required with the source object, or indirection is not used.
315      */

316     public void dontUseJoining() {
317         setUsesJoining(false);
318     }
319
320     /**
321      * INTERNAL:
322      * Extract the foreign key value from the source row.
323      */

324     protected Vector extractForeignKeyFromRow(AbstractRecord row, AbstractSession session) {
325         Vector key = new Vector();
326
327         for (Iterator fieldEnum = getSourceToTargetKeyFields().keySet().iterator();
328                  fieldEnum.hasNext();) {
329             DatabaseField field = (DatabaseField)fieldEnum.next();
330             Object JavaDoc value = row.get(field);
331
332             // Must ensure the classificatin to get a cache hit.
333
try {
334                 value = session.getDatasourcePlatform().getConversionManager().convertObject(value, getDescriptor().getObjectBuilder().getFieldClassification(field));
335             } catch (ConversionException e) {
336                 throw ConversionException.couldNotBeConverted(this, getDescriptor(), e);
337             }
338
339             key.addElement(value);
340         }
341
342         return key;
343     }
344
345     /**
346      * INTERNAL:
347      * Extract the key value from the reference object.
348      */

349     protected Vector extractKeyFromReferenceObject(Object JavaDoc object, AbstractSession session) {
350         Vector key = new Vector();
351
352         for (Iterator fieldEnum = getSourceToTargetKeyFields().values().iterator();
353                  fieldEnum.hasNext();) {
354             DatabaseField field = (DatabaseField)fieldEnum.next();
355
356             if (object == null) {
357                 key.addElement(null);
358             } else {
359                 key.addElement(getReferenceDescriptor().getObjectBuilder().extractValueFromObjectForField(object, field, session));
360             }
361         }
362
363         return key;
364     }
365
366     /**
367      * INTERNAL:
368      * Return the primary key for the reference object (i.e. the object
369      * object referenced by domainObject and specified by mapping).
370      * This key will be used by a RemoteValueHolder.
371      */

372     public Vector extractPrimaryKeysForReferenceObjectFromRow(AbstractRecord row) {
373         List primaryKeyFields = getReferenceDescriptor().getPrimaryKeyFields();
374         Vector result = new Vector(primaryKeyFields.size());
375         for (int index = 0; index < primaryKeyFields.size(); index++) {
376             DatabaseField targetKeyField = (DatabaseField)primaryKeyFields.get(index);
377             DatabaseField sourceKeyField = (DatabaseField)getTargetToSourceKeyFields().get(targetKeyField);
378             if (sourceKeyField == null) {
379                 return new Vector(1);
380             }
381             result.addElement(row.get(sourceKeyField));
382         }
383         return result;
384     }
385
386     /**
387      * INTERNAL:
388      * Return the classifiction for the field contained in the mapping.
389      * This is used to convert the row value to a consistent java value.
390      */

391     public Class JavaDoc getFieldClassification(DatabaseField fieldToClassify) throws DescriptorException {
392         DatabaseField fieldInTarget = (DatabaseField)getSourceToTargetKeyFields().get(fieldToClassify);
393         if (fieldInTarget == null) {
394             return null;// Can be registered as multiple table secondary field mapping
395
}
396         DatabaseMapping mapping = getReferenceDescriptor().getObjectBuilder().getMappingForField(fieldInTarget);
397         if (mapping == null) {
398             return null;// Means that the mapping is read-only
399
}
400         return mapping.getFieldClassification(fieldInTarget);
401     }
402
403     /**
404      * PUBLIC:
405      * Return the foreign key field names associated with the mapping.
406      * These are only the source fields that are writable.
407      */

408     public Vector getForeignKeyFieldNames() {
409         Vector fieldNames = new Vector(getForeignKeyFields().size());
410         for (Enumeration fieldsEnum = getForeignKeyFields().elements();
411                  fieldsEnum.hasMoreElements();) {
412             fieldNames.addElement(((DatabaseField)fieldsEnum.nextElement()).getQualifiedName());
413         }
414
415         return fieldNames;
416     }
417
418     /**
419      * Return the appropriate hashtable that maps the "foreign keys"
420      * to the "primary keys".
421      */

422     protected Map getForeignKeysToPrimaryKeys() {
423         if (this.isForeignKeyRelationship()) {
424             return this.getSourceToTargetKeyFields();
425         } else {
426             return this.getTargetToSourceKeyFields();
427         }
428     }
429
430     /**
431      * INTERNAL:
432      * Return a vector of the foreign key fields in the same order
433      * as the corresponding primary key fields are in their descriptor.
434      */

435     public Vector getOrderedForeignKeyFields() {
436         List primaryKeyFields = getPrimaryKeyDescriptor().getPrimaryKeyFields();
437         Vector result = new Vector(primaryKeyFields.size());
438
439         for (int index = 0; index < primaryKeyFields.size(); index++) {
440             DatabaseField pkField = (DatabaseField)primaryKeyFields.get(index);
441             boolean found = false;
442             for (Iterator fkStream = this.getForeignKeysToPrimaryKeys().keySet().iterator();
443                      fkStream.hasNext();) {
444                 DatabaseField fkField = (DatabaseField)fkStream.next();
445
446                 if (this.getForeignKeysToPrimaryKeys().get(fkField).equals(pkField)) {
447                     found = true;
448                     result.addElement(fkField);
449                     break;
450                 }
451             }
452             if (!found) {
453                 throw DescriptorException.missingForeignKeyTranslation(this, pkField);
454             }
455         }
456         return result;
457     }
458
459     /**
460      * Return the descriptor for whichever side of the
461      * relation has the "primary key".
462     */

463     protected ClassDescriptor getPrimaryKeyDescriptor() {
464         if (this.isForeignKeyRelationship()) {
465             return this.getReferenceDescriptor();
466         } else {
467             return this.getDescriptor();
468         }
469     }
470
471     /**
472      * INTERNAL:
473      * The private owned criteria is only used outside of the unit of work to compare the previous value of the reference.
474      */

475     public Expression getPrivateOwnedCriteria() {
476         if (privateOwnedCriteria == null) {
477             initializePrivateOwnedCriteria();
478         }
479         return privateOwnedCriteria;
480     }
481
482     /**
483      * INTERNAL:
484      * Return a collection of the source to target field value associations.
485      */

486     public Vector getSourceToTargetKeyFieldAssociations() {
487         Vector associations = new Vector(getSourceToTargetKeyFields().size());
488         Iterator sourceFieldEnum = getSourceToTargetKeyFields().keySet().iterator();
489         Iterator targetFieldEnum = getSourceToTargetKeyFields().values().iterator();
490         while (sourceFieldEnum.hasNext()) {
491             Object JavaDoc fieldValue = ((DatabaseField)sourceFieldEnum.next()).getQualifiedName();
492             Object JavaDoc attributeValue = ((DatabaseField)targetFieldEnum.next()).getQualifiedName();
493             associations.addElement(new Association(fieldValue, attributeValue));
494         }
495
496         return associations;
497     }
498
499     /**
500      * INTERNAL:
501      * Returns the source keys to target keys fields association.
502      */

503     public Map getSourceToTargetKeyFields() {
504         return sourceToTargetKeyFields;
505     }
506
507     /**
508      * INTERNAL:
509      * Returns the target keys to source keys fields association.
510      */

511     public Map getTargetToSourceKeyFields() {
512         return targetToSourceKeyFields;
513     }
514
515     /**
516      * INTERNAL:
517      * Initialize the mapping.
518      */

519     public void initialize(AbstractSession session) throws DescriptorException {
520         super.initialize(session);
521         
522         // Must set table of foreign keys.
523
for (Enumeration foreignKeysEnum = getForeignKeyFields().elements();
524                  foreignKeysEnum.hasMoreElements();) {
525             DatabaseField foreignKeyField = (DatabaseField)foreignKeysEnum.nextElement();
526             getDescriptor().buildField(foreignKeyField);
527         }
528
529         // If only a selection criteria is specified then the foreign keys do not have to be initialized.
530
if (!(getTargetToSourceKeyFields().isEmpty() && getSourceToTargetKeyFields().isEmpty())) {
531             if (getTargetToSourceKeyFields().isEmpty() || getSourceToTargetKeyFields().isEmpty()) {
532                 initializeForeignKeysWithDefaults(session);
533             } else {
534                 initializeForeignKeys(session);
535             }
536         }
537
538         if (shouldInitializeSelectionCriteria()) {
539             initializeSelectionCriteria(session);
540         } else {
541             setShouldVerifyDelete(false);
542         }
543
544         setFields(collectFields());
545     }
546
547     /**
548      * INTERNAL:
549      * The foreign keys primary keys are stored as database fields in the hashtable.
550      */

551     protected void initializeForeignKeys(AbstractSession session) {
552         Iterator sourceEnum = getSourceToTargetKeyFields().keySet().iterator();
553         Iterator targetEnum = getTargetToSourceKeyFields().keySet().iterator();
554         while (sourceEnum.hasNext()) {
555             DatabaseField sourceField = (DatabaseField)sourceEnum.next();
556             DatabaseField targetField = (DatabaseField)targetEnum.next();
557
558             getDescriptor().buildField(sourceField);
559             getReferenceDescriptor().buildField(targetField);
560         }
561     }
562
563     /**
564      * INTERNAL:
565      * The foreign keys primary keys are stored as database fields in the hashtable.
566      */

567     protected void initializeForeignKeysWithDefaults(AbstractSession session) {
568         if (isForeignKeyRelationship()) {
569             if (getSourceToTargetKeyFields().size() != 1) {
570                 throw DescriptorException.foreignKeysDefinedIncorrectly(this);
571             }
572             List targetKeys = getReferenceDescriptor().getPrimaryKeyFields();
573             if (targetKeys.size() != 1) {
574                 //target and source keys are not the same size.
575
throw DescriptorException.sizeMismatchOfForeignKeys(this);
576             }
577
578             //grab the only element out of the Hashtable
579
DatabaseField sourceField = (DatabaseField)getSourceToTargetKeyFields().keySet().iterator().next();
580             getDescriptor().buildField(sourceField);
581             getSourceToTargetKeyFields().put(sourceField, targetKeys.get(0));
582             getTargetToSourceKeyFields().put(targetKeys.get(0), sourceField);
583         } else {
584             if (getTargetToSourceKeyFields().size() != 1) {
585                 throw DescriptorException.foreignKeysDefinedIncorrectly(this);
586             }
587             List sourceKeys = getDescriptor().getPrimaryKeyFields();
588             if (sourceKeys.size() != 1) {
589                 //target and source keys are not the same size.
590
throw DescriptorException.sizeMismatchOfForeignKeys(this);
591             }
592
593             //grab the only element out of the Hashtable
594
DatabaseField targetField = (DatabaseField)getTargetToSourceKeyFields().keySet().iterator().next();
595             getReferenceDescriptor().buildField(targetField);
596             getTargetToSourceKeyFields().put(targetField, sourceKeys.get(0));
597             getSourceToTargetKeyFields().put(sourceKeys.get(0), targetField);
598         }
599     }
600
601     /**
602      * INTERNAL:
603      * Selection criteria is created with source foreign keys and target keys.
604      */

605     protected void initializePrivateOwnedCriteria() {
606         if (!isForeignKeyRelationship()) {
607             setPrivateOwnedCriteria(getSelectionCriteria());
608         } else {
609             Expression pkCriteria = getDescriptor().getObjectBuilder().getPrimaryKeyExpression();
610             ExpressionBuilder builder = new ExpressionBuilder();
611             Expression backRef = builder.getManualQueryKey(getAttributeName() + "-back-ref", getDescriptor());
612             Expression newPKCriteria = pkCriteria.rebuildOn(backRef);
613             Expression twistedSelection = backRef.twist(getSelectionCriteria(), builder);
614             if (getDescriptor().getQueryManager().getAdditionalJoinExpression() != null) {
615                 // We don't have to twist the additional join because it's all against the same node, which is our base
616
// but we do have to rebuild it onto the manual query key
617
Expression rebuiltAdditional = getDescriptor().getQueryManager().getAdditionalJoinExpression().rebuildOn(backRef);
618                 if (twistedSelection == null) {
619                     twistedSelection = rebuiltAdditional;
620                 } else {
621                     twistedSelection = twistedSelection.and(rebuiltAdditional);
622                 }
623             }
624             setPrivateOwnedCriteria(newPKCriteria.and(twistedSelection));
625         }
626     }
627
628     /**
629      * INTERNAL:
630      * Selection criteria is created with source foreign keys and target keys.
631      * This criteria is then used to read target records from the table.
632      *
633      * CR#3922 - This method is almost the same as buildSelectionCriteria() the difference
634      * is that getSelectionCriteria() is called
635      */

636     protected void initializeSelectionCriteria(AbstractSession session) {
637         if (getSourceToTargetKeyFields().isEmpty()) {
638             throw DescriptorException.noForeignKeysAreSpecified(this);
639         }
640
641         Expression criteria;
642         Expression builder = new ExpressionBuilder();
643         for (Iterator entries = getSourceToTargetKeyFields().entrySet().iterator(); entries.hasNext();) {
644             Map.Entry entry = (Map.Entry) entries.next();
645             DatabaseField foreignKey = (DatabaseField)entry.getKey();
646             DatabaseField targetKey = (DatabaseField)entry.getValue();
647             Expression expression = builder.getField(targetKey).equal(builder.getParameter(foreignKey));
648             criteria = expression.and(getSelectionCriteria());
649             setSelectionCriteria(criteria);
650         }
651     }
652     
653     /**
654      * This method would allow customers to get the potential selection criteria for a mapping
655      * prior to initialization. This would allow them to more easily create an ammendment method
656      * that would ammend the SQL for the join.
657      *
658      * CR#3922 - This method is almost the same as initializeSelectionCriteria() the difference
659      * is that getSelectionCriteria() is not called
660      */

661     public Expression buildSelectionCriteria() {
662         // CR3922
663
if (getSourceToTargetKeyFields().isEmpty()) {
664             throw DescriptorException.noForeignKeysAreSpecified(this);
665         }
666
667         Expression criteria = null;
668         Expression builder = new ExpressionBuilder();
669
670         for (Iterator keys = getSourceToTargetKeyFields().keySet().iterator(); keys.hasNext();) {
671             DatabaseField foreignKey = (DatabaseField)keys.next();
672             DatabaseField targetKey = (DatabaseField)getSourceToTargetKeyFields().get(foreignKey);
673
674             Expression expression = builder.getField(targetKey).equal(builder.getParameter(foreignKey));
675             if (criteria == null) {
676                 criteria = expression;
677             } else {
678                 criteria = expression.and(criteria);
679             }
680         }
681         return criteria;
682     }
683
684     /**
685      * INTERNAL:
686      * Builds a shallow original object. Only direct attributes and primary
687      * keys are populated. In this way the minimum original required for
688      * instantiating a working copy clone can be built without placing it in
689      * the shared cache (no concern over cycles).
690      */

691     public void buildShallowOriginalFromRow(AbstractRecord databaseRow, Object JavaDoc original, ObjectBuildingQuery query, AbstractSession executionSession) {
692         // Now we are only building this original so we can extract the primary
693
// key out of it. If the primary key is stored accross a 1-1 a value
694
// holder needs to be built/triggered to get at it.
695
// In this case recursively build the shallow original accross the 1-1.
696
// We only need the primary key for that object, and we know
697
// what that primary key is: it is the foreign key in our row.
698
ClassDescriptor descriptor = getReferenceDescriptor();
699         AbstractRecord targetRow = new DatabaseRecord();
700
701         for (Iterator keys = getSourceToTargetKeyFields().keySet().iterator(); keys.hasNext();) {
702             DatabaseField foreignKey = (DatabaseField)keys.next();
703             DatabaseField targetKey = (DatabaseField)getSourceToTargetKeyFields().get(foreignKey);
704
705             targetRow.put(targetKey, databaseRow.get(foreignKey));
706         }
707
708         Object JavaDoc targetObject = descriptor.getObjectBuilder().buildNewInstance();
709         descriptor.getObjectBuilder().buildAttributesIntoShallowObject(targetObject, databaseRow, query);
710         targetObject = getIndirectionPolicy().valueFromRow(targetObject);
711
712         setAttributeValueInObject(original, targetObject);
713     }
714
715     /**
716      * INTERNAL:
717      */

718     public boolean isOneToOneMapping() {
719         return true;
720     }
721
722     /**
723      * INTERNAL:
724      * Reads the private owned object.
725      */

726     protected Object JavaDoc readPrivateOwnedForObject(ObjectLevelModifyQuery modifyQuery) throws DatabaseException {
727         if (modifyQuery.getSession().isUnitOfWork()) {
728             return super.readPrivateOwnedForObject(modifyQuery);
729         } else {
730             if (!shouldVerifyDelete()) {
731                 return null;
732             }
733             ReadObjectQuery readQuery = (ReadObjectQuery)getSelectionQuery().clone();
734
735             readQuery.setSelectionCriteria(getPrivateOwnedCriteria());
736             return modifyQuery.getSession().executeQuery(readQuery, modifyQuery.getTranslationRow());
737         }
738     }
739
740     /**
741      * INTERNAL:
742      * Rehash any hashtables based on fields.
743      * This is used to clone descriptors for aggregates, which hammer field names,
744      * it is probably better not to hammer the field name and this should be refactored.
745      */

746     public void rehashFieldDependancies(AbstractSession session) {
747         setSourceToTargetKeyFields(Helper.rehashMap(getSourceToTargetKeyFields()));
748     }
749
750     /**
751      * PUBLIC:
752      * Define the foreign key relationship in the 1-1 mapping.
753      * This method is used for singleton foreign key relationships only,
754      * that is the source object's table has a foreign key field to
755      * the target object's primary key field.
756      * Only the source foreign key field name is specified.
757      * When a foreign key is specified TopLink will automatically populate the value
758      * for that field from the target object when the object is written to the database.
759      * If the foreign key is also mapped through a direct-to-field then the direct-to-field must
760      * be set read-only.
761      */

762     public void setForeignKeyFieldName(String JavaDoc sourceForeignKeyFieldName) {
763         DatabaseField sourceField = new DatabaseField(sourceForeignKeyFieldName);
764
765         setIsForeignKeyRelationship(true);
766         getForeignKeyFields().addElement(sourceField);
767         getSourceToTargetKeyFields().put(sourceField, new DatabaseField());
768     }
769
770     /**
771      * PUBLIC:
772      * Return the foreign key field names associated with the mapping.
773      * These are only the source fields that are writable.
774      */

775     public void setForeignKeyFieldNames(Vector fieldNames) {
776         Vector fields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(fieldNames.size());
777         for (Enumeration fieldNamesEnum = fieldNames.elements(); fieldNamesEnum.hasMoreElements();) {
778             fields.addElement(new DatabaseField((String JavaDoc)fieldNamesEnum.nextElement()));
779         }
780
781         setForeignKeyFields(fields);
782     }
783
784     /**
785      * INTERNAL:
786      * Private owned criteria is used to verify the deletion of the target.
787      * It joins from the source table on the foreign key to the target table,
788      * with a parameterization of the primary key of the source object.
789      */

790     protected void setPrivateOwnedCriteria(Expression expression) {
791         privateOwnedCriteria = expression;
792     }
793
794     /**
795      * PUBLIC:
796      * Verify delete is used during delete and update on private 1:1's outside of a unit of work only.
797      * It checks for the previous value of the target object through joining the source and target tables.
798      * By default it is always done, but may be disabled for performance on distributed database reasons.
799      * In the unit of work the previous value is obtained from the backup-clone so it is never used.
800      */

801     public void setShouldVerifyDelete(boolean shouldVerifyDelete) {
802         this.shouldVerifyDelete = shouldVerifyDelete;
803     }
804
805     /**
806      * INTERNAL:
807      * Set a collection of the source to target field associations.
808      */

809     public void setSourceToTargetKeyFieldAssociations(Vector sourceToTargetKeyFieldAssociations) {
810         setSourceToTargetKeyFields(new HashMap(sourceToTargetKeyFieldAssociations.size() + 1));
811         setTargetToSourceKeyFields(new HashMap(sourceToTargetKeyFieldAssociations.size() + 1));
812         for (Enumeration associationsEnum = sourceToTargetKeyFieldAssociations.elements();
813                  associationsEnum.hasMoreElements();) {
814             Association association = (Association)associationsEnum.nextElement();
815             Object JavaDoc sourceField = new DatabaseField((String JavaDoc)association.getKey());
816             Object JavaDoc targetField = new DatabaseField((String JavaDoc)association.getValue());
817             getSourceToTargetKeyFields().put(sourceField, targetField);
818             getTargetToSourceKeyFields().put(targetField, sourceField);
819         }
820     }
821
822     /**
823      * INTERNAL:
824      * Set the source keys to target keys fields association.
825      */

826     public void setSourceToTargetKeyFields(Map sourceToTargetKeyFields) {
827         this.sourceToTargetKeyFields = sourceToTargetKeyFields;
828     }
829
830     /**
831      * PUBLIC:
832      * Define the target foreign key relationship in the 1-1 mapping.
833      * This method is used for singleton target foreign key relationships only,
834      * that is the target object's table has a foreign key field to
835      * the source object's primary key field.
836      * The target foreign key field name is specified.
837      * The distinction between a foreign key and target foreign key is that the 1-1
838      * mapping will not populate the target foreign key value when written (because it is in the target table).
839      * Normally 1-1's are through foreign keys but in bi-directional 1-1's
840      * the back reference will be a target foreign key.
841      */

842     public void setTargetForeignKeyFieldName(String JavaDoc targetForeignKeyFieldName) {
843         DatabaseField targetField = new DatabaseField(targetForeignKeyFieldName);
844         getTargetToSourceKeyFields().put(targetField, new DatabaseField());
845     }
846
847     /**
848      * INTERNAL:
849      * Set the target keys to source keys fields association.
850      */

851     public void setTargetToSourceKeyFields(Map targetToSourceKeyFields) {
852         this.targetToSourceKeyFields = targetToSourceKeyFields;
853     }
854
855     /**
856      * PUBLIC:
857      * Indicates whether the referenced object should always be joined on read queries.
858      * Joining will join the two classes tables to read all of the data in a single query.
859      * This should only be used if it is know that the related objects are always required with the source object, or indirection is not used.
860      */

861     public void setUsesJoining(boolean usesJoining) {
862         if (usesJoining == this.usesJoining) {
863             return;
864         }
865         this.usesJoining = usesJoining;
866
867         // For 3524579 now cache joined mappings on the object builder.
868
// This allows a user to set joining dynamically after the
869
// descriptors have been initialized. Generally this is not
870
// supported, but since we were checking this flag in prepare after
871
// initialization some degree of backward compatibility should be
872
// provided.
873
if (getDescriptor() != null) {
874             getDescriptor().reInitializeJoinedAttributes();
875         }
876
877         // Still every query which is already prepared, like all selection
878
// queries, will not pick up this change.
879
}
880
881     /**
882      * PUBLIC:
883      * Indicates whether the referenced object should always be joined on read queries.
884      * Joining will join the two classes tables to read all of the data in a single query.
885      * This should only be used if it is know that the related objects are always required with the source object, or indirection is not used.
886      */

887     public boolean shouldUseJoining() {
888         return usesJoining;
889     }
890
891     /**
892      * PUBLIC:
893      * Verify delete is used during delete and update outside of a unit of work only.
894      * It checks for the previous value of the target object through joining the source and target tables.
895      */

896     public boolean shouldVerifyDelete() {
897         return shouldVerifyDelete;
898     }
899
900     /**
901      * INTERNAL
902      * Return true if this mapping supports cascaded version optimistic locking.
903      */

904     public boolean isCascadedLockingSupported() {
905         return true;
906     }
907     
908     /**
909      * INTERNAL:
910      * Return if this mapping support joining.
911      */

912     public boolean isJoiningSupported() {
913         return true;
914     }
915
916     /**
917      * PUBLIC:
918      * Indicates whether the referenced object should always be joined on read queries.
919      * Joining will join the two classes tables to read all of the data in a single query.
920      * This should only be used if it is know that the related objects are always required with the source object, or indirection is not used.
921      */

922     public void useJoining() {
923         setUsesJoining(true);
924     }
925
926     /**
927      * INTERNAL:
928      * A subclass should implement this method if it wants different behaviour.
929      * Write the foreign key values from the attribute to the row.
930      */

931     public void writeFromAttributeIntoRow(Object JavaDoc attribute, AbstractRecord row, AbstractSession session)
932     {
933           for (Enumeration fieldsEnum = getForeignKeyFields().elements(); fieldsEnum.hasMoreElements();) {
934                   DatabaseField sourceKey = (DatabaseField) fieldsEnum.nextElement();
935                   DatabaseField targetKey = (DatabaseField) getSourceToTargetKeyFields().get(sourceKey);
936                   Object JavaDoc referenceValue = null;
937                           // If privately owned part is null then method cannot be invoked.
938
if (attribute != null) {
939                           referenceValue = getReferenceDescriptor().getObjectBuilder().extractValueFromObjectForField(attribute, targetKey, session);
940                   }
941                   row.add(sourceKey, referenceValue);
942           }
943     }
944
945     /**
946      * INTERNAL:
947      * Get a value from the object and set that in the respective field of the row.
948      */

949     public Object JavaDoc valueFromObject(Object JavaDoc object, DatabaseField field, AbstractSession session) {
950         // First check if the value can be obtained from the value holder's row.
951
AbstractRecord referenceRow = getIndirectionPolicy().extractReferenceRow(getAttributeValueFromObject(object));
952         if (referenceRow != null) {
953             Object JavaDoc value = referenceRow.get(field);
954
955             // Must ensure the classification to get a cache hit.
956
try {
957                 value = session.getDatasourcePlatform().convertObject(value, getFieldClassification(field));
958             } catch (ConversionException e) {
959                 throw ConversionException.couldNotBeConverted(this, getDescriptor(), e);
960             }
961             return value;
962         }
963
964         Object JavaDoc referenceObject = getRealAttributeValueFromObject(object, session);
965         if (referenceObject == null) {
966             return null;
967         }
968         DatabaseField targetField = (DatabaseField)getSourceToTargetKeyFields().get(field);
969
970         return getReferenceDescriptor().getObjectBuilder().extractValueFromObjectForField(referenceObject, targetField, session);
971     }
972
973     /**
974      * INTERNAL:
975      * If the query used joining or partial attributes, build the target object directly.
976      */

977     protected Object JavaDoc valueFromRowInternalWithJoin(AbstractRecord row, JoinedAttributeManager joinManager, AbstractSession executionSession) throws DatabaseException {
978         // PERF: Direct variable access.
979
Object JavaDoc referenceObject;
980         // CR #... the field for many objects may be in the row,
981
// so build the subpartion of the row through the computed values in the query,
982
// this also helps the field indexing match.
983
AbstractRecord targetRow = trimRowForJoin(row, joinManager, executionSession);
984         // PERF: Only check for null row if an outer-join was used.
985
if (joinManager.isAttributeJoined(getDescriptor(), getAttributeName()) && joinManager.hasOuterJoinedAttributeQuery()) {
986             Vector key = this.referenceDescriptor.getObjectBuilder().extractPrimaryKeyFromRow(targetRow, executionSession);
987             if (key == null) {
988                 return this.indirectionPolicy.nullValueFromRow();
989             }
990         }
991         // A nested query must be built to pass to the descriptor that looks like the real query execution would,
992
// these should be cached on the query during prepare.
993
ObjectLevelReadQuery nestedQuery = null;
994         if (joinManager.getJoinedMappingQueries_() != null) {
995             nestedQuery = (ObjectLevelReadQuery) joinManager.getJoinedMappingQueries_().get(this);
996         } else {
997             nestedQuery = prepareNestedJoins(joinManager, executionSession);
998         }
999         nestedQuery = (ObjectLevelReadQuery)nestedQuery.clone();
1000        nestedQuery.setTranslationRow(targetRow);
1001        nestedQuery.setSession(executionSession);
1002        //CR #4365 - used to prevent infinite recursion on refresh object cascade all
1003
nestedQuery.setQueryId(joinManager.getBaseQuery().getQueryId());
1004        referenceObject = this.referenceDescriptor.getObjectBuilder().buildObject(nestedQuery, targetRow, nestedQuery.getJoinedAttributeManager());
1005
1006        // For bug 3641713 buildObject doesn't wrap if called on a UnitOfWork for performance reasons,
1007
// must wrap here as this is the last time we can look at the query and tell whether to wrap or not.
1008
if (nestedQuery.shouldUseWrapperPolicy() && nestedQuery.getSession().isUnitOfWork()) {
1009            referenceObject = this.referenceDescriptor.getObjectBuilder().wrapObject(referenceObject, nestedQuery.getSession());
1010        }
1011        return this.indirectionPolicy.valueFromRow(referenceObject);
1012    }
1013    
1014    /**
1015     * INTERNAL:
1016     * Return the value of the field from the row or a value holder on the query to obtain the object.
1017     * Check for batch + aggregation reading.
1018     */

1019    protected Object JavaDoc valueFromRowInternal(AbstractRecord row, JoinedAttributeManager joinManager, AbstractSession executionSession) throws DatabaseException {
1020        // If any field in the foreign key is null then it means there are no referenced objects
1021
// Skip for partial objects as fk may not be present.
1022
int size = this.fields.size();
1023        for (int index = 0; index < size; index++) {
1024            DatabaseField field = (DatabaseField)this.fields.get(index);
1025            if (row.get(field) == null) {
1026                return this.indirectionPolicy.nullValueFromRow();
1027            }
1028        }
1029
1030        // Call the default which executes the selection query,
1031
// or wraps the query with a value holder.
1032
return super.valueFromRowInternal(row, joinManager, executionSession);
1033    }
1034
1035    /**
1036     * INTERNAL:
1037     * Get a value from the object and set that in the respective field of the row.
1038     */

1039    public void writeFromObjectIntoRow(Object JavaDoc object, AbstractRecord databaseRow, AbstractSession session) {
1040        if (isReadOnly() || (!isForeignKeyRelationship())) {
1041            return;
1042        }
1043
1044        AbstractRecord referenceRow = getIndirectionPolicy().extractReferenceRow(getAttributeValueFromObject(object));
1045        if (referenceRow == null) {
1046            // Extract from object.
1047
Object JavaDoc referenceObject = getRealAttributeValueFromObject(object, session);
1048
1049            for (Enumeration fieldsEnum = getForeignKeyFields().elements();
1050                     fieldsEnum.hasMoreElements();) {
1051                DatabaseField sourceKey = (DatabaseField)fieldsEnum.nextElement();
1052                DatabaseField targetKey = (DatabaseField)getSourceToTargetKeyFields().get(sourceKey);
1053
1054                Object JavaDoc referenceValue = null;
1055
1056                // If privately owned part is null then method cannot be invoked.
1057
if (referenceObject != null) {
1058                    referenceValue = getReferenceDescriptor().getObjectBuilder().extractValueFromObjectForField(referenceObject, targetKey, session);
1059                }
1060                databaseRow.add(sourceKey, referenceValue);
1061            }
1062        } else {
1063            for (Enumeration fieldsEnum = getForeignKeyFields().elements();
1064                     fieldsEnum.hasMoreElements();) {
1065                DatabaseField sourceKey = (DatabaseField)fieldsEnum.nextElement();
1066                Object JavaDoc referenceValue = referenceRow.get(sourceKey);
1067                databaseRow.add(sourceKey, referenceValue);
1068            }
1069        }
1070    }
1071
1072    /**
1073     * INTERNAL:
1074     * This row is built for shallow insert which happens in case of bidirectional inserts.
1075     * The foreign keys must be set to null to avoid constraints.
1076     */

1077    public void writeFromObjectIntoRowForShallowInsert(Object JavaDoc object, AbstractRecord databaseRow, AbstractSession session) {
1078        if (isReadOnly() || (!isForeignKeyRelationship())) {
1079            return;
1080        }
1081
1082        for (Enumeration fieldsEnum = getForeignKeyFields().elements();
1083                 fieldsEnum.hasMoreElements();) {
1084            DatabaseField sourceKey = (DatabaseField)fieldsEnum.nextElement();
1085            databaseRow.add(sourceKey, null);
1086        }
1087    }
1088
1089    /**
1090     * INTERNAL:
1091     * Get a value from the object and set that in the respective field of the row.
1092     */

1093    public void writeFromObjectIntoRowWithChangeRecord(ChangeRecord changeRecord, AbstractRecord databaseRow, AbstractSession session) {
1094        if (isReadOnly() || (!isForeignKeyRelationship())) {
1095            return;
1096        }
1097
1098        // the object must be used here as the foreign key may include more than just the
1099
// primary key of the referenced object and the changeSet may not have the rquired information
1100
Object JavaDoc object = ((ObjectChangeSet)changeRecord.getOwner()).getUnitOfWorkClone();
1101        AbstractRecord referenceRow = getIndirectionPolicy().extractReferenceRow(getAttributeValueFromObject(object));
1102        if (referenceRow == null) {
1103            // Extract from object.
1104
Object JavaDoc referenceObject = getRealAttributeValueFromObject(object, session);
1105
1106            for (Enumeration fieldsEnum = getForeignKeyFields().elements();
1107                     fieldsEnum.hasMoreElements();) {
1108                DatabaseField sourceKey = (DatabaseField)fieldsEnum.nextElement();
1109                DatabaseField targetKey = (DatabaseField)getSourceToTargetKeyFields().get(sourceKey);
1110
1111                Object JavaDoc referenceValue = null;
1112
1113                // If privately owned part is null then method cannot be invoked.
1114
if (referenceObject != null) {
1115                    referenceValue = getReferenceDescriptor().getObjectBuilder().extractValueFromObjectForField(referenceObject, targetKey, session);
1116                }
1117                databaseRow.add(sourceKey, referenceValue);
1118            }
1119        } else {
1120            for (Enumeration fieldsEnum = getForeignKeyFields().elements();
1121                     fieldsEnum.hasMoreElements();) {
1122                DatabaseField sourceKey = (DatabaseField)fieldsEnum.nextElement();
1123                Object JavaDoc referenceValue = referenceRow.get(sourceKey);
1124                databaseRow.add(sourceKey, referenceValue);
1125            }
1126        }
1127    }
1128
1129    /**
1130     * INTERNAL:
1131     * This row is built for shallow insert which happens in case of bidirectional inserts.
1132     * The foreign keys must be set to null to avoid constraints.
1133     */

1134    public void writeFromObjectIntoRowForShallowInsertWithChangeRecord(ChangeRecord ChangeRecord, AbstractRecord databaseRow, AbstractSession session) {
1135        if (isReadOnly() || (!isForeignKeyRelationship())) {
1136            return;
1137        }
1138
1139        for (Enumeration fieldsEnum = getForeignKeyFields().elements();
1140                 fieldsEnum.hasMoreElements();) {
1141            DatabaseField sourceKey = (DatabaseField)fieldsEnum.nextElement();
1142            databaseRow.add(sourceKey, null);
1143        }
1144    }
1145
1146    /**
1147     * INTERNAL:
1148     * Write fields needed for insert into the template for with null values.
1149     */

1150    public void writeInsertFieldsIntoRow(AbstractRecord databaseRow, AbstractSession session) {
1151        if (isReadOnly() || (!isForeignKeyRelationship())) {
1152            return;
1153        }
1154
1155        for (Enumeration fieldsEnum = getForeignKeyFields().elements();
1156                 fieldsEnum.hasMoreElements();) {
1157            DatabaseField sourceKey = (DatabaseField)fieldsEnum.nextElement();
1158            databaseRow.add(sourceKey, null);
1159        }
1160    }
1161}
1162
Popular Tags