KickJava   Java API By Example, From Geeks To Geeks.

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


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.indirection.ValueHolderInterface;
28 import oracle.toplink.essentials.internal.helper.*;
29 import oracle.toplink.essentials.internal.identitymaps.*;
30 import oracle.toplink.essentials.internal.queryframework.*;
31 import oracle.toplink.essentials.internal.sessions.*;
32 import oracle.toplink.essentials.sessions.DatabaseRecord;
33 import oracle.toplink.essentials.queryframework.*;
34 import oracle.toplink.essentials.internal.sessions.AbstractRecord;
35 import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
36 import oracle.toplink.essentials.internal.sessions.AbstractSession;
37 import oracle.toplink.essentials.descriptors.ClassDescriptor;
38
39 /**
40  * <p><b>Purpose</b>: This mapping is used to represent the
41  * typical RDBMS relationship between a single
42  * source object and collection of target objects; where,
43  * on the database, the target objects have references
44  * (foreign keys) to the source object.
45  *
46  * @author Sati
47  * @since TOPLink/Java 1.0
48  */

49 public class OneToManyMapping extends CollectionMapping implements RelationalMapping {
50
51     /** The target foreign key fields that reference the sourceKeyFields. */
52     protected transient Vector targetForeignKeyFields;
53
54     /** The (typically primary) source key fields that are referenced by the targetForeignKeyFields. */
55     protected transient Vector sourceKeyFields;
56
57     /** This maps the target foreign key fields to the corresponding (primary) source key fields. */
58     protected transient Map targetForeignKeysToSourceKeys;
59     
60     /** This maps the (primary) source key fields to the corresponding target foreign key fields. */
61     protected transient Map sourceKeysToTargetForeignKeys;
62
63     /**
64      * PUBLIC:
65      * Default constructor.
66      */

67     public OneToManyMapping() {
68         super();
69
70         this.targetForeignKeysToSourceKeys = new HashMap(2);
71         this.sourceKeysToTargetForeignKeys = new HashMap(2);
72         
73         this.sourceKeyFields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(1);
74         this.targetForeignKeyFields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(1);
75
76         this.deleteAllQuery = new DeleteAllQuery();
77     }
78
79     /**
80      * INTERNAL:
81      */

82     public boolean isRelationalMapping() {
83         return true;
84     }
85
86     /**
87      * INTERNAL:
88      * Add the associated fields to the appropriate collections.
89      */

90     public void addTargetForeignKeyField(DatabaseField targetForeignKeyField, DatabaseField sourceKeyField) {
91         this.getTargetForeignKeyFields().addElement(targetForeignKeyField);
92         this.getSourceKeyFields().addElement(sourceKeyField);
93     }
94
95     /**
96      * PUBLIC:
97      * Define the target foreign key relationship in the one-to-many mapping.
98      * This method is used for composite target foreign key relationships.
99      * That is, the target object's table has multiple foreign key fields
100      * that are references to
101      * the source object's (typically primary) key fields.
102      * Both the target foreign key field name and the corresponding
103      * source primary key field name must be specified.
104      * Because the target object's table must store a foreign key to the source table,
105      * the target object must map that foreign key, this is normally done through a
106      * one-to-one mapping back-reference. Other options include:
107      * <ul>
108      * <li> use a DirectToFieldMapping and maintain the
109      * foreign key fields directly in the target
110      * <li> use a ManyToManyMapping
111      * <li> use an AggregateCollectionMapping
112      * </ul>
113      * @see DirectToFieldMapping
114      * @see ManyToManyMapping
115      * @see AggregateCollectionMapping
116      */

117     public void addTargetForeignKeyFieldName(String JavaDoc targetForeignKeyFieldName, String JavaDoc sourceKeyFieldName) {
118         this.addTargetForeignKeyField(new DatabaseField(targetForeignKeyFieldName), new DatabaseField(sourceKeyFieldName));
119     }
120
121     /**
122      * The selection criteria are created with target foreign keys and source "primary" keys.
123      * These criteria are then used to read the target records from the table.
124      * These criteria are also used as the default "delete all" criteria.
125      *
126      * CR#3922 - This method is almost the same as buildSelectionCriteria() the difference
127      * is that TargetForeignKeysToSourceKeys contains more information after login then SourceKeyFields
128      * contains before login.
129      */

130     protected Expression buildDefaultSelectionCriteria() {
131         Expression selectionCriteria = null;
132         Expression builder = new ExpressionBuilder();
133
134         for (Iterator keys = this.getTargetForeignKeysToSourceKeys().keySet().iterator();
135                  keys.hasNext();) {
136             DatabaseField targetForeignKey = (DatabaseField)keys.next();
137             DatabaseField sourceKey = (DatabaseField)this.getTargetForeignKeysToSourceKeys().get(targetForeignKey);
138
139             Expression partialSelectionCriteria = builder.getField(targetForeignKey).equal(builder.getParameter(sourceKey));
140             selectionCriteria = partialSelectionCriteria.and(selectionCriteria);
141         }
142         return selectionCriteria;
143     }
144     
145     /**
146      * This method would allow customers to get the potential selection criteria for a mapping
147      * prior to initialization. This would allow them to more easily create an ammendment method
148      * that would ammend the SQL for the join.
149      *
150      * CR#3922 - This method is almost the same as buildDefaultSelectionCriteria() the difference
151      * is that TargetForeignKeysToSourceKeys contains more information after login then SourceKeyFields
152      * contains before login.
153      */

154     public Expression buildSelectionCriteria() {
155         //CR3922
156
Expression selectionCriteria = null;
157         Expression builder = new ExpressionBuilder();
158
159         Enumeration sourceKeys = this.getSourceKeyFields().elements();
160         for (Enumeration targetForeignKeys = this.getTargetForeignKeyFields().elements();
161                  targetForeignKeys.hasMoreElements();) {
162             DatabaseField targetForeignKey = (DatabaseField)targetForeignKeys.nextElement();
163             DatabaseField sourceKey = (DatabaseField)sourceKeys.nextElement();
164             Expression partialSelectionCriteria = builder.getField(targetForeignKey).equal(builder.getParameter(sourceKey));
165             selectionCriteria = partialSelectionCriteria.and(selectionCriteria);
166         }
167         return selectionCriteria;
168     }
169
170     /**
171      * INTERNAL:
172      * Clone the appropriate attributes.
173      */

174     public Object JavaDoc clone() {
175         OneToManyMapping clone = (OneToManyMapping)super.clone();
176         clone.setTargetForeignKeysToSourceKeys(new HashMap(this.getTargetForeignKeysToSourceKeys()));
177         return clone;
178     }
179
180     /**
181      * Delete all the reference objects with a single query.
182      */

183     protected void deleteAll(DeleteObjectQuery query) throws DatabaseException {
184         Object JavaDoc referenceObjects = null;
185         if(usesIndirection()) {
186            Object JavaDoc attribute = getAttributeAccessor().getAttributeValueFromObject(query.getObject());
187            if(attribute == null || !((ValueHolderInterface)attribute).isInstantiated()) {
188                // An empty Vector indicates to DeleteAllQuery that no objects should be removed from cache
189
referenceObjects = new Vector(0);
190            }
191         }
192         if(referenceObjects == null) {
193             referenceObjects = this.getRealCollectionAttributeValueFromObject(query.getObject(), query.getSession());
194         }
195
196         ((DeleteAllQuery)this.getDeleteAllQuery()).executeDeleteAll(query.getSession().getSessionForClass(this.getReferenceClass()), query.getTranslationRow(), this.getContainerPolicy().vectorFor(referenceObjects, query.getSession()));
197     }
198
199     /**
200      * This method will make sure that all the records privately owned by this mapping are
201      * actually removed. If such records are found then those are all read and removed one
202      * by one along with their privately owned parts.
203      */

204     protected void deleteReferenceObjectsLeftOnDatabase(DeleteObjectQuery query) throws DatabaseException, OptimisticLockException {
205         Object JavaDoc objects = this.readPrivateOwnedForObject(query);
206
207         // Delete all these object one by one.
208
ContainerPolicy cp = this.getContainerPolicy();
209         for (Object JavaDoc iter = cp.iteratorFor(objects); cp.hasNext(iter);) {
210             query.getSession().deleteObject(cp.next(iter, query.getSession()));
211         }
212     }
213
214     /**
215      * Extract the foreign key value from the reference object.
216      * Used for batch reading. Keep the fields in the same order
217      * as in the targetForeignKeysToSourceKeys hashtable.
218      */

219     protected Vector extractForeignKeyFromReferenceObject(Object JavaDoc object, AbstractSession session) {
220         Vector foreignKey = new Vector(this.getTargetForeignKeysToSourceKeys().size());
221
222         for (Iterator stream = this.getTargetForeignKeysToSourceKeys().entrySet().iterator();
223                  stream.hasNext();) {
224             Map.Entry entry = (Map.Entry)stream.next();
225             DatabaseField targetField = (DatabaseField)entry.getKey();
226             DatabaseField sourceField = (DatabaseField)entry.getValue();
227             if (object == null) {
228                 foreignKey.addElement(null);
229             } else {
230                 Object JavaDoc value = this.getReferenceDescriptor().getObjectBuilder().extractValueFromObjectForField(object, targetField, session);
231
232                 //CR:somenewsgroupbug need to ensure source and target types match.
233
try {
234                     value = session.getDatasourcePlatform().convertObject(value, getDescriptor().getObjectBuilder().getFieldClassification(sourceField));
235                 } catch (ConversionException e) {
236                     throw ConversionException.couldNotBeConverted(this, getDescriptor(), e);
237                 }
238
239                 foreignKey.addElement(value);
240             }
241         }
242         return foreignKey;
243     }
244
245     /**
246      * Extract the key field values from the specified row.
247      * Used for batch reading. Keep the fields in the same order
248      * as in the targetForeignKeysToSourceKeys hashtable.
249      */

250     protected Vector extractKeyFromRow(AbstractRecord row, AbstractSession session) {
251         Vector key = new Vector(this.getTargetForeignKeysToSourceKeys().size());
252
253         for (Iterator stream = this.getTargetForeignKeysToSourceKeys().values().iterator();
254                  stream.hasNext();) {
255             DatabaseField field = (DatabaseField)stream.next();
256             Object JavaDoc value = row.get(field);
257
258             // Must ensure the classification to get a cache hit.
259
try {
260                 value = session.getDatasourcePlatform().convertObject(value, getDescriptor().getObjectBuilder().getFieldClassification(field));
261             } catch (ConversionException e) {
262                 throw ConversionException.couldNotBeConverted(this, getDescriptor(), e);
263             }
264
265             key.addElement(value);
266         }
267         return key;
268     }
269
270     /**
271      * PUBLIC:
272      * Return the source key field names associated with the mapping.
273      * These are in-order with the targetForeignKeyFieldNames.
274      */

275     public Vector getSourceKeyFieldNames() {
276         Vector fieldNames = new Vector(getSourceKeyFields().size());
277         for (Enumeration fieldsEnum = getSourceKeyFields().elements();
278                  fieldsEnum.hasMoreElements();) {
279             fieldNames.addElement(((DatabaseField)fieldsEnum.nextElement()).getQualifiedName());
280         }
281
282         return fieldNames;
283     }
284
285     /**
286      * INTERNAL:
287      * Return the source key fields.
288      */

289     public Vector getSourceKeyFields() {
290         return sourceKeyFields;
291     }
292     
293     /**
294      * INTERNAL:
295      * Return the source/target key fields.
296      */

297     public Map getSourceKeysToTargetForeignKeys() {
298         return sourceKeysToTargetForeignKeys;
299     }
300
301     /**
302      * INTERNAL:
303      * Return the target foreign key field names associated with the mapping.
304      * These are in-order with the targetForeignKeyFieldNames.
305      */

306     public Vector getTargetForeignKeyFieldNames() {
307         Vector fieldNames = new Vector(getTargetForeignKeyFields().size());
308         for (Enumeration fieldsEnum = getTargetForeignKeyFields().elements();
309                  fieldsEnum.hasMoreElements();) {
310             fieldNames.addElement(((DatabaseField)fieldsEnum.nextElement()).getQualifiedName());
311         }
312
313         return fieldNames;
314     }
315
316     /**
317      * INTERNAL:
318      * Return the target foreign key fields.
319      */

320     public Vector getTargetForeignKeyFields() {
321         return targetForeignKeyFields;
322     }
323
324     /**
325      * INTERNAL:
326      * Return the target/source key fields.
327      */

328     public Map getTargetForeignKeysToSourceKeys() {
329         return targetForeignKeysToSourceKeys;
330     }
331
332     /**
333      * INTERNAL:
334      * Maintain for backward compatibility.
335      * This is 'public' so StoredProcedureGenerator
336      * does not have to use the custom query expressions.
337      */

338     public Map getTargetForeignKeyToSourceKeys() {
339         return this.getTargetForeignKeysToSourceKeys();
340     }
341
342     /**
343      * INTERNAL:
344      * Return whether the mapping has any inverse constraint dependencies,
345      * such as foreign keys and join tables.
346      */

347     public boolean hasInverseConstraintDependency() {
348         return true;
349     }
350     
351     /**
352      * INTERNAL:
353      * Initialize the mapping.
354      */

355     public void initialize(AbstractSession session) throws DescriptorException {
356         super.initialize(session);
357
358         if (!this.isSourceKeySpecified()) {
359             // sourceKeyFields will be empty when #setTargetForeignKeyFieldName() is used
360
this.setSourceKeyFields(oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(getDescriptor().getPrimaryKeyFields()));
361         }
362         this.initializeTargetForeignKeysToSourceKeys();
363
364         if (this.shouldInitializeSelectionCriteria()) {
365             this.setSelectionCriteria(this.buildDefaultSelectionCriteria());
366         }
367
368         this.initializeDeleteAllQuery();
369     }
370
371     /**
372      * Initialize the delete all query.
373      * This query is used to delete the collection of objects from the
374      * database.
375      */

376     protected void initializeDeleteAllQuery() {
377         ((DeleteAllQuery)this.getDeleteAllQuery()).setReferenceClass(this.getReferenceClass());
378         if (!this.hasCustomDeleteAllQuery()) {
379             // the selection criteria are re-used by the delete all query
380
if (this.getSelectionCriteria() == null) {
381                 this.getDeleteAllQuery().setSelectionCriteria(this.buildDefaultSelectionCriteria());
382             } else {
383                 this.getDeleteAllQuery().setSelectionCriteria(this.getSelectionCriteria());
384             }
385         }
386     }
387
388     /**
389      * Verify, munge, and hash the target foreign keys and source keys.
390      */

391     protected void initializeTargetForeignKeysToSourceKeys() throws DescriptorException {
392         if (this.getTargetForeignKeyFields().isEmpty()) {
393             if (this.shouldInitializeSelectionCriteria()) {
394                 throw DescriptorException.noTargetForeignKeysSpecified(this);
395             } else {
396                 // if they have specified selection criteria, the keys do not need to be specified
397
return;
398             }
399         }
400
401         if (this.getTargetForeignKeyFields().size() != this.getSourceKeyFields().size()) {
402             throw DescriptorException.targetForeignKeysSizeMismatch(this);
403         }
404
405         for (Enumeration stream = this.getTargetForeignKeyFields().elements();
406                  stream.hasMoreElements();) {
407             this.getReferenceDescriptor().buildField((DatabaseField)stream.nextElement());
408         }
409
410         for (Enumeration keys = this.getSourceKeyFields().elements(); keys.hasMoreElements();) {
411             this.getDescriptor().buildField((DatabaseField)keys.nextElement());
412         }
413
414         Enumeration targetForeignKeys = this.getTargetForeignKeyFields().elements();
415         Enumeration sourceKeys = this.getSourceKeyFields().elements();
416         while (targetForeignKeys.hasMoreElements()) {
417             Object JavaDoc targetForeignKey = targetForeignKeys.nextElement();
418             Object JavaDoc sourcePrimaryKey = sourceKeys.nextElement();
419             this.getTargetForeignKeysToSourceKeys().put(targetForeignKey, sourcePrimaryKey);
420             this.getSourceKeysToTargetForeignKeys().put(sourcePrimaryKey, targetForeignKey);
421             
422             //this.getTargetForeignKeysToSourceKeys().put(targetForeignKeys.nextElement(), sourceKeys.nextElement());
423
}
424     }
425
426     /**
427      * INTERNAL:
428      */

429     public boolean isOneToManyMapping() {
430         return true;
431     }
432
433     /**
434      * Return whether the source key is specified.
435      * It will be empty when #setTargetForeignKeyFieldName(String) is used.
436      */

437     protected boolean isSourceKeySpecified() {
438         return !this.getSourceKeyFields().isEmpty();
439     }
440
441     /**
442      * Return whether the reference objects must be deleted
443      * one by one, as opposed to with a single DELETE statement.
444      */

445     protected boolean mustDeleteReferenceObjectsOneByOne() {
446         ClassDescriptor referenceDescriptor = this.getReferenceDescriptor();
447         return referenceDescriptor.hasDependencyOnParts() || referenceDescriptor.usesOptimisticLocking() || (referenceDescriptor.hasInheritance() && referenceDescriptor.getInheritancePolicy().shouldReadSubclasses()) || referenceDescriptor.hasMultipleTables();
448     }
449
450     /**
451      * INTERNAL:
452      * Insert the reference objects.
453      */

454     public void postInsert(WriteObjectQuery query) throws DatabaseException, OptimisticLockException {
455         if (!this.shouldObjectModifyCascadeToParts(query)) {
456             return;
457         }
458
459         // only cascade dependents in UOW
460
if (query.shouldCascadeOnlyDependentParts()) {
461             return;
462         }
463
464         Object JavaDoc objects = this.getRealCollectionAttributeValueFromObject(query.getObject(), query.getSession());
465
466         // insert each object one by one
467
ContainerPolicy cp = this.getContainerPolicy();
468         for (Object JavaDoc iter = cp.iteratorFor(objects); cp.hasNext(iter);) {
469             Object JavaDoc object = cp.next(iter, query.getSession());
470             if (this.isPrivateOwned()) {
471                 // no need to set changeSet as insert is a straight copy
472
InsertObjectQuery insertQuery = new InsertObjectQuery();
473                 insertQuery.setObject(object);
474                 insertQuery.setCascadePolicy(query.getCascadePolicy());
475                 query.getSession().executeQuery(insertQuery);
476             } else {
477                 // This will happen in a unit of work or cascaded query.
478
// This is done only for persistence by reachability and is not required if the targets are in the queue anyway
479
// Avoid cycles by checking commit manager, this is allowed because there is no dependency.
480
if (!query.getSession().getCommitManager().isCommitInPreModify(object)) {
481                     ObjectChangeSet changeSet = null;
482                     UnitOfWorkChangeSet uowChangeSet = null;
483                     if (query.getSession().isUnitOfWork() && (((UnitOfWorkImpl)query.getSession()).getUnitOfWorkChangeSet() != null)) {
484                         uowChangeSet = (UnitOfWorkChangeSet)((UnitOfWorkImpl)query.getSession()).getUnitOfWorkChangeSet();
485                         changeSet = (ObjectChangeSet)uowChangeSet.getObjectChangeSetForClone(object);
486                     }
487                     WriteObjectQuery writeQuery = new WriteObjectQuery();
488                     writeQuery.setObject(object);
489                     writeQuery.setCascadePolicy(query.getCascadePolicy());
490                     query.getSession().executeQuery(writeQuery);
491                 }
492             }
493         }
494     }
495
496     /**
497      * INTERNAL:
498      * Update the reference objects.
499      */

500     public void postUpdate(WriteObjectQuery query) throws DatabaseException, OptimisticLockException {
501         if (!this.shouldObjectModifyCascadeToParts(query)) {
502             return;
503         }
504
505         // if the target objects are not instantiated, they could not have been changed....
506
if (!this.isAttributeValueInstantiated(query.getObject())) {
507             return;
508         }
509
510         // manage objects added and removed from the collection
511
Object JavaDoc objectsInMemory = this.getRealCollectionAttributeValueFromObject(query.getObject(), query.getSession());
512         Object JavaDoc objectsInDB = this.readPrivateOwnedForObject(query);
513
514         this.compareObjectsAndWrite(objectsInDB, objectsInMemory, query);
515     }
516
517     /**
518      * INTERNAL:
519      * Delete the reference objects.
520      */

521     public void preDelete(DeleteObjectQuery query) throws DatabaseException, OptimisticLockException {
522         if (!this.shouldObjectModifyCascadeToParts(query)) {
523             return;
524         }
525
526         // if privately-owned parts have their privately-owned sub-parts, delete them one by one;
527
// else delete everything in one shot
528
if (this.mustDeleteReferenceObjectsOneByOne()) {
529             Object JavaDoc objects = this.getRealCollectionAttributeValueFromObject(query.getObject(), query.getSession());
530             ContainerPolicy cp = this.getContainerPolicy();
531             for (Object JavaDoc iter = cp.iteratorFor(objects); cp.hasNext(iter);) {
532                 DeleteObjectQuery deleteQuery = new DeleteObjectQuery();
533                 deleteQuery.setObject(cp.next(iter, query.getSession()));
534                 deleteQuery.setCascadePolicy(query.getCascadePolicy());
535                 query.getSession().executeQuery(deleteQuery);
536             }
537             if (!query.getSession().isUnitOfWork()) {
538                 // This deletes any objects on the database, as the collection in memory may have been changed.
539
// This is not required for unit of work, as the update would have already deleted these objects,
540
// and the backup copy will include the same objects causing double deletes.
541
this.deleteReferenceObjectsLeftOnDatabase(query);
542             }
543         } else {
544             this.deleteAll(query);
545         }
546     }
547
548     /**
549      * PUBLIC:
550      * Set the SQL string used by the mapping to delete the target objects.
551      * This allows the developer to override the SQL
552      * generated by TopLink with a custom SQL statement or procedure call.
553      * The arguments are
554      * translated from the fields of the source row, by replacing the field names
555      * marked by '#' with the values for those fields at execution time.
556      * A one-to-many mapping will only use this delete all optimization if the target objects
557      * can be deleted in a single SQL call. This is possible when the target objects
558      * are in a single table, do not using locking, do not contain other privately-owned
559      * parts, do not read subclasses, etc.
560      * <p>
561      * Example: "delete from PHONE where OWNER_ID = #EMPLOYEE_ID"
562      */

563     public void setDeleteAllSQLString(String JavaDoc sqlString) {
564         DeleteAllQuery query = new DeleteAllQuery();
565         query.setSQLString(sqlString);
566         setCustomDeleteAllQuery(query);
567     }
568
569     /**
570      * INTERNAL:
571      * Set the source key field names associated with the mapping.
572      * These must be in-order with the targetForeignKeyFieldNames.
573      */

574     public void setSourceKeyFieldNames(Vector fieldNames) {
575         Vector fields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(fieldNames.size());
576         for (Enumeration fieldNamesEnum = fieldNames.elements(); fieldNamesEnum.hasMoreElements();) {
577             fields.addElement(new DatabaseField((String JavaDoc)fieldNamesEnum.nextElement()));
578         }
579
580         setSourceKeyFields(fields);
581     }
582
583     /**
584      * INTERNAL:
585      * Set the source key fields.
586      */

587     public void setSourceKeyFields(Vector sourceKeyFields) {
588         this.sourceKeyFields = sourceKeyFields;
589     }
590
591     /**
592      * PUBLIC:
593      * Define the target foreign key relationship in the one-to-many mapping.
594      * This method can be used when the foreign and primary keys
595      * have only a single field each.
596      * (Use #addTargetForeignKeyFieldName(String, String)
597      * for "composite" keys.)
598      * Only the target foreign key field name is specified and the source
599      * (primary) key field is
600      * assumed to be the primary key of the source object.
601      * Because the target object's table must store a foreign key to the source table,
602      * the target object must map that foreign key, this is normally done through a
603      * one-to-one mapping back-reference. Other options include:
604      * <ul>
605      * <li> use a DirectToFieldMapping and maintain the
606      * foreign key fields directly in the target
607      * <li> use a ManyToManyMapping
608      * <li> use an AggregateCollectionMapping
609      * </ul>
610      * @see DirectToFieldMapping
611      * @see ManyToManyMapping
612      * @see AggregateCollectionMapping
613      */

614     public void setTargetForeignKeyFieldName(String JavaDoc targetForeignKeyFieldName) {
615         this.getTargetForeignKeyFields().addElement(new DatabaseField(targetForeignKeyFieldName));
616     }
617
618     /**
619      * PUBLIC:
620      * Define the target foreign key relationship in the one-to-many mapping.
621      * This method is used for composite target foreign key relationships.
622      * That is, the target object's table has multiple foreign key fields to
623      * the source object's (typically primary) key fields.
624      * Both the target foreign key field names and the corresponding source primary
625      * key field names must be specified.
626      */

627     public void setTargetForeignKeyFieldNames(String JavaDoc[] targetForeignKeyFieldNames, String JavaDoc[] sourceKeyFieldNames) {
628         if (targetForeignKeyFieldNames.length != sourceKeyFieldNames.length) {
629             throw DescriptorException.targetForeignKeysSizeMismatch(this);
630         }
631         for (int i = 0; i < targetForeignKeyFieldNames.length; i++) {
632             this.addTargetForeignKeyFieldName(targetForeignKeyFieldNames[i], sourceKeyFieldNames[i]);
633         }
634     }
635
636     /**
637      * INTERNAL:
638      * Set the target key field names associated with the mapping.
639      * These must be in-order with the sourceKeyFieldNames.
640      */

641     public void setTargetForeignKeyFieldNames(Vector fieldNames) {
642         Vector fields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(fieldNames.size());
643         for (Enumeration fieldNamesEnum = fieldNames.elements(); fieldNamesEnum.hasMoreElements();) {
644             fields.addElement(new DatabaseField((String JavaDoc)fieldNamesEnum.nextElement()));
645         }
646
647         setTargetForeignKeyFields(fields);
648     }
649
650     /**
651      * INTERNAL:
652      * Set the target fields.
653      */

654     public void setTargetForeignKeyFields(Vector targetForeignKeyFields) {
655         this.targetForeignKeyFields = targetForeignKeyFields;
656     }
657
658     /**
659      * INTERNAL:
660      * Set the target fields.
661      */

662     protected void setTargetForeignKeysToSourceKeys(Map targetForeignKeysToSourceKeys) {
663         this.targetForeignKeysToSourceKeys = targetForeignKeysToSourceKeys;
664     }
665
666     /**
667      * Return whether any process leading to object modification
668      * should also affect its parts.
669      * Used by write, insert, update, and delete.
670      */

671     protected boolean shouldObjectModifyCascadeToParts(ObjectLevelModifyQuery query) {
672         if (this.isReadOnly()) {
673             return false;
674         }
675
676         if (this.isPrivateOwned()) {
677             return true;
678         }
679
680         return query.shouldCascadeAllParts();
681     }
682     
683     /**
684      * INTERNAL
685      * Return true if this mapping supports cascaded version optimistic locking.
686      */

687     public boolean isCascadedLockingSupported() {
688         return true;
689     }
690     
691     /**
692      * INTERNAL:
693      * Return if this mapping support joining.
694      */

695     public boolean isJoiningSupported() {
696         return true;
697     }
698
699     /**
700      * INTERNAL:
701      * Used to verify whether the specified object is deleted or not.
702      */

703     public boolean verifyDelete(Object JavaDoc object, AbstractSession session) throws DatabaseException {
704         if (this.isPrivateOwned()) {
705             Object JavaDoc objects = this.getRealCollectionAttributeValueFromObject(object, session);
706
707             ContainerPolicy containerPolicy = getContainerPolicy();
708             for (Object JavaDoc iter = containerPolicy.iteratorFor(objects); containerPolicy.hasNext(iter);) {
709                 if (!session.verifyDelete(containerPolicy.next(iter, session))) {
710                     return false;
711                 }
712             }
713         }
714         return true;
715     }
716 }
717
Popular Tags