KickJava   Java API By Example, From Geeks To Geeks.

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


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.*;
28 import oracle.toplink.essentials.internal.expressions.*;
29 import oracle.toplink.essentials.internal.helper.*;
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
38 /**
39  * <p><b>Purpose</b>: Many to many mappings are used to represent the relationships
40  * between a collection of source objects and a collection of target objects.
41  * The mapping require the creation of an intermediate table for managing the
42  * associations between the source and target records.
43  *
44  * @author Sati
45  * @since TOPLink/Java 1.0
46  */

47 public class ManyToManyMapping extends CollectionMapping implements RelationalMapping {
48
49     /** Used for data modification events. */
50     protected static final String JavaDoc PostInsert = "postInsert";
51     protected static final String JavaDoc ObjectRemoved = "objectRemoved";
52     protected static final String JavaDoc ObjectAdded = "objectAdded";
53
54     /** The intermediate relation table. */
55     protected transient DatabaseTable relationTable;
56
57     /** The field in the source table that corresponds to the key in the relation table */
58     protected transient Vector sourceKeyFields;
59
60     /** The field in the target table that corresponds to the key in the relation table */
61     protected transient Vector targetKeyFields;
62
63     /** The field in the intermediate table that corresponds to the key in the source table */
64     protected transient Vector sourceRelationKeyFields;
65
66     /** The field in the intermediate table that corresponds to the key in the target table */
67     protected transient Vector targetRelationKeyFields;
68
69     /** Query used for single row deletion. */
70     protected transient DataModifyQuery deleteQuery;
71     protected transient boolean hasCustomDeleteQuery;
72
73     /** Used for insertion. */
74     protected transient DataModifyQuery insertQuery;
75     protected transient boolean hasCustomInsertQuery;
76     
77     /**
78      * PUBLIC:
79      * Default constructor.
80      */

81     public ManyToManyMapping() {
82         this.insertQuery = new DataModifyQuery();
83         this.deleteQuery = new DataModifyQuery();
84         this.sourceRelationKeyFields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(1);
85         this.targetRelationKeyFields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(1);
86         this.sourceKeyFields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(1);
87         this.targetKeyFields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(1);
88         this.hasCustomDeleteQuery = false;
89         this.hasCustomInsertQuery = false;
90     }
91
92     /**
93      * INTERNAL:
94      */

95     public boolean isRelationalMapping() {
96         return true;
97     }
98
99     /**
100      * PUBLIC:
101      * Add the fields in the intermediate table that corresponds to the primary
102      * key in the source table. This method is used if the keys are composite.
103      */

104     public void addSourceRelationKeyField(DatabaseField sourceRelationKeyField, DatabaseField sourcePrimaryKeyField) {
105         getSourceRelationKeyFields().addElement(sourceRelationKeyField);
106         getSourceKeyFields().addElement(sourcePrimaryKeyField);
107     }
108     
109     /**
110      * PUBLIC:
111      * Add the fields in the intermediate table that corresponds to the primary
112      * key in the source table. This method is used if the keys are composite.
113      */

114     public void addSourceRelationKeyFieldName(String JavaDoc sourceRelationKeyFieldName, String JavaDoc sourcePrimaryKeyFieldName) {
115         addSourceRelationKeyField(new DatabaseField(sourceRelationKeyFieldName), new DatabaseField(sourcePrimaryKeyFieldName));
116     }
117
118     /**
119      * PUBLIC:
120      * Add the fields in the intermediate table that corresponds to the primary
121      * key in the target table. This method is used if the keys are composite.
122      */

123     public void addTargetRelationKeyField(DatabaseField targetRelationKeyField, DatabaseField targetPrimaryKeyField) {
124         getTargetRelationKeyFields().addElement(targetRelationKeyField);
125         getTargetKeyFields().addElement(targetPrimaryKeyField);
126     }
127     
128     /**
129      * PUBLIC:
130      * Add the fields in the intermediate table that corresponds to the primary
131      * key in the target table. This method is used if the keys are composite.
132      */

133     public void addTargetRelationKeyFieldName(String JavaDoc targetRelationKeyFieldName, String JavaDoc targetPrimaryKeyFieldName) {
134         addTargetRelationKeyField(new DatabaseField(targetRelationKeyFieldName), new DatabaseField(targetPrimaryKeyFieldName));
135     }
136
137     /**
138      * INTERNAL:
139      * The mapping clones itself to create deep copy.
140      */

141     public Object JavaDoc clone() {
142         ManyToManyMapping clone = (ManyToManyMapping)super.clone();
143
144         clone.setTargetKeyFields(cloneFields(getTargetKeyFields()));
145         clone.setSourceKeyFields(cloneFields(getSourceKeyFields()));
146         clone.setTargetRelationKeyFields(cloneFields(getTargetRelationKeyFields()));
147         clone.setSourceRelationKeyFields(cloneFields(getSourceRelationKeyFields()));
148
149         return clone;
150     }
151
152     /**
153      * INTERNAL:
154      * Extract the source primary key value from the relation row.
155      * Used for batch reading, most following same order and fields as in the mapping.
156      */

157     protected Vector extractKeyFromRelationRow(AbstractRecord row, AbstractSession session) {
158         Vector key = new Vector(getSourceRelationKeyFields().size());
159
160         for (int index = 0; index < getSourceRelationKeyFields().size(); index++) {
161             DatabaseField relationField = (DatabaseField)getSourceRelationKeyFields().elementAt(index);
162             DatabaseField sourceField = (DatabaseField)getSourceKeyFields().elementAt(index);
163             Object JavaDoc value = row.get(relationField);
164
165             // Must ensure the classificatin to get a cache hit.
166
try {
167                 value = session.getDatasourcePlatform().getConversionManager().convertObject(value, getDescriptor().getObjectBuilder().getFieldClassification(sourceField));
168             } catch (ConversionException e) {
169                 throw ConversionException.couldNotBeConverted(this, getDescriptor(), e);
170             }
171
172             key.addElement(value);
173         }
174
175         return key;
176     }
177
178     /**
179      * INTERNAL:
180      * Extract the primary key value from the source row.
181      * Used for batch reading, most following same order and fields as in the mapping.
182      */

183     protected Vector extractPrimaryKeyFromRow(AbstractRecord row, AbstractSession session) {
184         Vector key = new Vector(getSourceKeyFields().size());
185
186         for (Enumeration fieldEnum = getSourceKeyFields().elements(); fieldEnum.hasMoreElements();) {
187             DatabaseField field = (DatabaseField)fieldEnum.nextElement();
188             Object JavaDoc value = row.get(field);
189
190             // Must ensure the classificatin to get a cache hit.
191
try {
192                 value = session.getDatasourcePlatform().getConversionManager().convertObject(value, getDescriptor().getObjectBuilder().getFieldClassification(field));
193             } catch (ConversionException e) {
194                 throw ConversionException.couldNotBeConverted(this, getDescriptor(), e);
195             }
196
197             key.addElement(value);
198         }
199
200         return key;
201     }
202
203     protected DataModifyQuery getDeleteQuery() {
204         return deleteQuery;
205     }
206
207     protected DataModifyQuery getInsertQuery() {
208         return insertQuery;
209     }
210
211     /**
212      * INTERNAL:
213      * Return the relation table associated with the mapping.
214      */

215     public DatabaseTable getRelationTable() {
216         return relationTable;
217     }
218
219     /**
220      * PUBLIC:
221      * Return the relation table name associated with the mapping.
222      */

223     public String JavaDoc getRelationTableName() {
224         if (relationTable == null) {
225             return null;
226         }
227         return relationTable.getName();
228     }
229
230     //CR#2407 This method is added to include table qualifier.
231

232     /**
233      * PUBLIC:
234      * Return the relation table qualified name associated with the mapping.
235      */

236     public String JavaDoc getRelationTableQualifiedName() {
237         if (relationTable == null) {
238             return null;
239         }
240         return relationTable.getQualifiedName();
241     }
242
243     /**
244      * INTERNAL:
245      * Returns the selection criteria stored in the mapping selection query. This criteria
246      * is used to read reference objects from the database.
247      */

248     public Expression getSelectionCriteria() {
249         return getSelectionQuery().getSelectionCriteria();
250     }
251
252     /**
253      * INTERNAL:
254      * Returns the read query assoicated with the mapping.
255      */

256     public ReadQuery getSelectionQuery() {
257         return selectionQuery;
258     }
259
260     /**
261      * PUBLIC:
262      * Return the source key field names associated with the mapping.
263      * These are in-order with the sourceRelationKeyFieldNames.
264      */

265     public Vector getSourceKeyFieldNames() {
266         Vector fieldNames = new Vector(getSourceKeyFields().size());
267         for (Enumeration fieldsEnum = getSourceKeyFields().elements();
268                  fieldsEnum.hasMoreElements();) {
269             fieldNames.addElement(((DatabaseField)fieldsEnum.nextElement()).getQualifiedName());
270         }
271
272         return fieldNames;
273     }
274
275     /**
276      * INTERNAL:
277      * Return all the source key fields associated with the mapping.
278      */

279     public Vector getSourceKeyFields() {
280         return sourceKeyFields;
281     }
282
283     /**
284      * PUBLIC:
285      * Return the source relation key field names associated with the mapping.
286      * These are in-order with the sourceKeyFieldNames.
287      */

288     public Vector getSourceRelationKeyFieldNames() {
289         Vector fieldNames = new Vector(getSourceRelationKeyFields().size());
290         for (Enumeration fieldsEnum = getSourceRelationKeyFields().elements();
291                  fieldsEnum.hasMoreElements();) {
292             fieldNames.addElement(((DatabaseField)fieldsEnum.nextElement()).getQualifiedName());
293         }
294
295         return fieldNames;
296     }
297
298     /**
299      * INTERNAL:
300      * Return all the source realtion key fields associated with the mapping.
301      */

302     public Vector getSourceRelationKeyFields() {
303         return sourceRelationKeyFields;
304     }
305
306     /**
307      * PUBLIC:
308      * Return the target key field names associated with the mapping.
309      * These are in-order with the targetRelationKeyFieldNames.
310      */

311     public Vector getTargetKeyFieldNames() {
312         Vector fieldNames = new Vector(getTargetKeyFields().size());
313         for (Enumeration fieldsEnum = getTargetKeyFields().elements();
314                  fieldsEnum.hasMoreElements();) {
315             fieldNames.addElement(((DatabaseField)fieldsEnum.nextElement()).getQualifiedName());
316         }
317
318         return fieldNames;
319     }
320
321     /**
322      * INTERNAL:
323      * Return all the target keys associated with the mapping.
324      */

325     public Vector getTargetKeyFields() {
326         return targetKeyFields;
327     }
328
329     /**
330      * PUBLIC:
331      * Return the target relation key field names associated with the mapping.
332      * These are in-order with the targetKeyFieldNames.
333      */

334     public Vector getTargetRelationKeyFieldNames() {
335         Vector fieldNames = new Vector(getTargetRelationKeyFields().size());
336         for (Enumeration fieldsEnum = getTargetRelationKeyFields().elements();
337                  fieldsEnum.hasMoreElements();) {
338             fieldNames.addElement(((DatabaseField)fieldsEnum.nextElement()).getQualifiedName());
339         }
340
341         return fieldNames;
342     }
343
344     /**
345      * INTERNAL:
346      * Return all the target relation key fields associated with the mapping.
347      */

348     public Vector getTargetRelationKeyFields() {
349         return targetRelationKeyFields;
350     }
351
352     protected boolean hasCustomDeleteQuery() {
353         return hasCustomDeleteQuery;
354     }
355
356     protected boolean hasCustomInsertQuery() {
357         return hasCustomInsertQuery;
358     }
359
360     /**
361      * INTERNAL:
362      * The join table is a dependency if not read-only.
363      */

364     public boolean hasDependency() {
365         return isPrivateOwned() || (!isReadOnly());
366     }
367
368     /**
369      * INTERNAL:
370      * Initialize mappings
371      */

372     public void initialize(AbstractSession session) throws DescriptorException {
373         super.initialize(session);
374         
375         initializeRelationTable(session);
376         initializeSourceRelationKeys(session);
377         initializeTargetRelationKeys(session);
378
379         if (isSingleSourceRelationKeySpecified()) {
380             initializeSourceKeysWithDefaults(session);
381         } else {
382             initializeSourceKeys(session);
383         }
384
385         if (isSingleTargetRelationKeySpecified()) {
386             initializeTargetKeysWithDefaults(session);
387         } else {
388             initializeTargetKeys(session);
389         }
390
391         if (shouldInitializeSelectionCriteria()) {
392             initializeSelectionCriteria(session);
393         }
394
395         if (!getSelectionQuery().hasSessionName()) {
396             getSelectionQuery().setSessionName(session.getName());
397         }
398         initializeDeleteAllQuery(session);
399         initializeInsertQuery(session);
400         initializeDeleteQuery(session);
401     }
402
403     /**
404      * Initialize delete all query. This query is used to all relevant rows from the
405      * relation table.
406      */

407     protected void initializeDeleteAllQuery(AbstractSession session) {
408         if (!getDeleteAllQuery().hasSessionName()) {
409             getDeleteAllQuery().setSessionName(session.getName());
410         }
411
412         if (hasCustomDeleteAllQuery()) {
413             return;
414         }
415
416         Expression expression = null;
417         Expression subExpression;
418         Expression builder = new ExpressionBuilder();
419         SQLDeleteStatement statement = new SQLDeleteStatement();
420
421         // Construct an expression to delete from the relation table.
422
for (int index = 0; index < getSourceRelationKeyFields().size(); index++) {
423             DatabaseField sourceRelationKey = (DatabaseField)getSourceRelationKeyFields().elementAt(index);
424             DatabaseField sourceKey = (DatabaseField)getSourceKeyFields().elementAt(index);
425
426             subExpression = builder.getField(sourceRelationKey).equal(builder.getParameter(sourceKey));
427             expression = subExpression.and(expression);
428         }
429
430         // All the enteries are deleted in one shot.
431
statement.setWhereClause(expression);
432         statement.setTable(getRelationTable());
433         getDeleteAllQuery().setSQLStatement(statement);
434     }
435
436     /**
437      * INTERNAL:
438      * Initialize delete query. This query is used to delete a specific row from the join table in uow,
439      * given the objects on both sides of the relation.
440      */

441     protected void initializeDeleteQuery(AbstractSession session) {
442         if (!getDeleteQuery().hasSessionName()) {
443             getDeleteQuery().setSessionName(session.getName());
444         }
445         if (hasCustomDeleteQuery()) {
446             return;
447         }
448
449         // Build where clause expression.
450
Expression whereClause = null;
451         Expression builder = new ExpressionBuilder();
452
453         Enumeration relationKeyEnum = getSourceRelationKeyFields().elements();
454         for (; relationKeyEnum.hasMoreElements();) {
455             DatabaseField relationKey = (DatabaseField)relationKeyEnum.nextElement();
456
457             Expression expression = builder.getField(relationKey).equal(builder.getParameter(relationKey));
458             whereClause = expression.and(whereClause);
459         }
460
461         relationKeyEnum = getTargetRelationKeyFields().elements();
462         for (; relationKeyEnum.hasMoreElements();) {
463             DatabaseField relationKey = (DatabaseField)relationKeyEnum.nextElement();
464
465             Expression expression = builder.getField(relationKey).equal(builder.getParameter(relationKey));
466             whereClause = expression.and(whereClause);
467         }
468
469         SQLDeleteStatement statement = new SQLDeleteStatement();
470         statement.setTable(getRelationTable());
471         statement.setWhereClause(whereClause);
472         getDeleteQuery().setSQLStatement(statement);
473     }
474
475     /**
476      * INTERNAL:
477      * Initialize insert query. This query is used to insert the collection of objects into the
478      * relation table.
479      */

480     protected void initializeInsertQuery(AbstractSession session) {
481         if (!getInsertQuery().hasSessionName()) {
482             getInsertQuery().setSessionName(session.getName());
483         }
484         if (hasCustomInsertQuery()) {
485             return;
486         }
487
488         SQLInsertStatement statement = new SQLInsertStatement();
489         statement.setTable(getRelationTable());
490         AbstractRecord joinRow = new DatabaseRecord();
491         for (Enumeration targetEnum = getTargetRelationKeyFields().elements();
492                  targetEnum.hasMoreElements();) {
493             joinRow.put((DatabaseField)targetEnum.nextElement(), null);
494         }
495         for (Enumeration sourceEnum = getSourceRelationKeyFields().elements();
496                  sourceEnum.hasMoreElements();) {
497             joinRow.put((DatabaseField)sourceEnum.nextElement(), null);
498         }
499         statement.setModifyRow(joinRow);
500         getInsertQuery().setSQLStatement(statement);
501         getInsertQuery().setModifyRow(joinRow);
502     }
503
504     /**
505      * INTERNAL:
506      * Set the table qualifier on the relation table if required
507      */

508     protected void initializeRelationTable(AbstractSession session) throws DescriptorException {
509         Platform platform = session.getDatasourcePlatform();
510
511         if ((getRelationTable() == null) || (getRelationTableName().length() == 0)) {
512             throw DescriptorException.noRelationTable(this);
513         }
514
515         if (platform.getTableQualifier().length() == 0) {
516             return;
517         }
518
519         if (getRelationTable().getTableQualifier().length() == 0) {
520             getRelationTable().setTableQualifier(platform.getTableQualifier());
521         }
522     }
523
524     /**
525      * INTERNAL:
526      * Selection criteria is created to read target records from the table.
527      */

528     protected void initializeSelectionCriteria(AbstractSession session) {
529         DatabaseField relationKey;
530         DatabaseField sourceKey;
531         DatabaseField targetKey;
532         Expression exp1;
533         Expression exp2;
534         Expression expression;
535         Expression criteria;
536         Expression builder = new ExpressionBuilder();
537         Enumeration relationKeyEnum;
538         Enumeration sourceKeyEnum;
539         Enumeration targetKeyEnum;
540
541         Expression linkTable = null;
542
543         targetKeyEnum = getTargetKeyFields().elements();
544         relationKeyEnum = getTargetRelationKeyFields().elements();
545         for (; targetKeyEnum.hasMoreElements();) {
546             relationKey = (DatabaseField)relationKeyEnum.nextElement();
547             targetKey = (DatabaseField)targetKeyEnum.nextElement();
548             if (linkTable == null) {// We could just call getTable repeatedly, but it's a waste
549
linkTable = builder.getTable(relationKey.getTable());
550             }
551
552             exp1 = builder.getField(targetKey);
553             exp2 = linkTable.getField(relationKey);
554             expression = exp1.equal(exp2);
555
556             if ((criteria = getSelectionCriteria()) == null) {
557                 criteria = expression;
558             } else {
559                 criteria = expression.and(criteria);
560             }
561
562             setSelectionCriteria(criteria);
563         }
564
565         relationKeyEnum = getSourceRelationKeyFields().elements();
566         sourceKeyEnum = getSourceKeyFields().elements();
567
568         for (; relationKeyEnum.hasMoreElements();) {
569             relationKey = (DatabaseField)relationKeyEnum.nextElement();
570             sourceKey = (DatabaseField)sourceKeyEnum.nextElement();
571
572             exp1 = linkTable.getField(relationKey);
573             exp2 = builder.getParameter(sourceKey);
574             expression = exp1.equal(exp2);
575
576             if ((criteria = getSelectionCriteria()) == null) {
577                 criteria = expression;
578             } else {
579                 criteria = expression.and(criteria);
580             }
581
582             setSelectionCriteria(criteria);
583         }
584     }
585
586     /**
587      * INTERNAL:
588      * All the source key field names are converted to DatabaseField and stored.
589      */

590     protected void initializeSourceKeys(AbstractSession session) {
591         for (Enumeration sourceEnum = getSourceKeyFields().elements();
592                  sourceEnum.hasMoreElements();) {
593             getDescriptor().buildField((DatabaseField)sourceEnum.nextElement());
594         }
595     }
596
597     /**
598      * INTERNAL:
599      * If a user does not specify the source key then the primary keys of the source table are used.
600      */

601     protected void initializeSourceKeysWithDefaults(AbstractSession session) {
602         List primaryKeyFields = getDescriptor().getPrimaryKeyFields();
603         for (int index = 0; index < primaryKeyFields.size(); index++) {
604             getSourceKeyFields().addElement(primaryKeyFields.get(index));
605         }
606     }
607
608     /**
609      * INTERNAL:
610      * All the source relation key field names are converted to DatabaseField and stored.
611      */

612     protected void initializeSourceRelationKeys(AbstractSession session) throws DescriptorException {
613         if (getSourceRelationKeyFields().size() == 0) {
614             throw DescriptorException.noSourceRelationKeysSpecified(this);
615         }
616
617         for (Enumeration entry = getSourceRelationKeyFields().elements(); entry.hasMoreElements();) {
618             DatabaseField field = (DatabaseField)entry.nextElement();
619             if (field.hasTableName() && (!(field.getTableName().equals(getRelationTable().getName())))) {
620                 throw DescriptorException.relationKeyFieldNotProperlySpecified(field, this);
621             }
622             field.setTable(getRelationTable());
623         }
624     }
625
626     /**
627      * INTERNAL:
628      * All the target key field names are converted to DatabaseField and stored.
629      */

630     protected void initializeTargetKeys(AbstractSession session) {
631         for (Enumeration targetEnum = getTargetKeyFields().elements();
632                  targetEnum.hasMoreElements();) {
633             getReferenceDescriptor().buildField((DatabaseField)targetEnum.nextElement());
634         }
635     }
636
637     /**
638      * INTERNAL:
639      * If a user does not specify the target key then the primary keys of the target table are used.
640      */

641     protected void initializeTargetKeysWithDefaults(AbstractSession session) {
642         List primaryKeyFields = getReferenceDescriptor().getPrimaryKeyFields();
643         for (int index = 0; index < primaryKeyFields.size(); index++) {
644             getTargetKeyFields().addElement(primaryKeyFields.get(index));
645         }
646     }
647
648     /**
649      * INTERNAL:
650      * All the target relation key field names are converted to DatabaseField and stored.
651      */

652     protected void initializeTargetRelationKeys(AbstractSession session) {
653         if (getTargetRelationKeyFields().size() == 0) {
654             throw DescriptorException.noTargetRelationKeysSpecified(this);
655         }
656
657         for (Enumeration targetEnum = getTargetRelationKeyFields().elements();
658                  targetEnum.hasMoreElements();) {
659             DatabaseField field = (DatabaseField)targetEnum.nextElement();
660             if (field.hasTableName() && (!(field.getTableName().equals(getRelationTable().getName())))) {
661                 throw DescriptorException.relationKeyFieldNotProperlySpecified(field, this);
662             }
663             field.setTable(getRelationTable());
664         }
665     }
666
667     /**
668      * INTERNAL:
669      * An object was added to the collection during an update, insert it.
670      */

671     protected void insertAddedObjectEntry(ObjectLevelModifyQuery query, Object JavaDoc objectAdded) throws DatabaseException, OptimisticLockException {
672         //cr 3819 added the line below to fix the translationtable to ensure that it
673
// contains the required values
674
prepareTranslationRow(query.getTranslationRow(), query.getObject(), query.getSession());
675         AbstractRecord databaseRow = new DatabaseRecord();
676
677         // Extract primary key and value from the source.
678
for (int index = 0; index < getSourceRelationKeyFields().size(); index++) {
679             DatabaseField sourceRelationKey = (DatabaseField)getSourceRelationKeyFields().elementAt(index);
680             DatabaseField sourceKey = (DatabaseField)getSourceKeyFields().elementAt(index);
681             Object JavaDoc sourceKeyValue = query.getTranslationRow().get(sourceKey);
682             databaseRow.put(sourceRelationKey, sourceKeyValue);
683         }
684
685         // Extract target field and its value. Construct insert statement and execute it
686
for (int index = 0; index < getTargetRelationKeyFields().size(); index++) {
687             DatabaseField targetRelationKey = (DatabaseField)getTargetRelationKeyFields().elementAt(index);
688             DatabaseField targetKey = (DatabaseField)getTargetKeyFields().elementAt(index);
689             Object JavaDoc targetKeyValue = getReferenceDescriptor().getObjectBuilder().extractValueFromObjectForField(objectAdded, targetKey, query.getSession());
690             databaseRow.put(targetRelationKey, targetKeyValue);
691         }
692
693         query.getSession().executeQuery(getInsertQuery(), databaseRow);
694     }
695
696     /**
697      * INTERNAL:
698      * Insert into relation table. This follows following steps.
699      * <p>- Extract primary key and its value from the source object.
700      * <p>- Extract target key and its value from the target object.
701      * <p>- Construct a insert statement with above fields and values for relation table.
702      * <p>- execute the statement.
703      * <p>- Repeat above three statements until all the target objects are done.
704      */

705     public void insertIntoRelationTable(WriteObjectQuery query) throws DatabaseException {
706         if (isReadOnly()) {
707             return;
708         }
709
710         ContainerPolicy cp = getContainerPolicy();
711         Object JavaDoc objects = getRealCollectionAttributeValueFromObject(query.getObject(), query.getSession());
712         if (cp.isEmpty(objects)) {
713             return;
714         }
715
716         prepareTranslationRow(query.getTranslationRow(), query.getObject(), query.getSession());
717         AbstractRecord databaseRow = new DatabaseRecord();
718
719         // Extract primary key and value from the source.
720
for (int index = 0; index < getSourceRelationKeyFields().size(); index++) {
721             DatabaseField sourceRelationKey = (DatabaseField)getSourceRelationKeyFields().elementAt(index);
722             DatabaseField sourceKey = (DatabaseField)getSourceKeyFields().elementAt(index);
723             Object JavaDoc sourceKeyValue = query.getTranslationRow().get(sourceKey);
724             databaseRow.put(sourceRelationKey, sourceKeyValue);
725         }
726
727         // Extract target field and its value. Construct insert statement and execute it
728
for (Object JavaDoc iter = cp.iteratorFor(objects); cp.hasNext(iter);) {
729             Object JavaDoc object = cp.next(iter, query.getSession());
730             for (int index = 0; index < getTargetRelationKeyFields().size(); index++) {
731                 DatabaseField targetRelationKey = (DatabaseField)getTargetRelationKeyFields().elementAt(index);
732                 DatabaseField targetKey = (DatabaseField)getTargetKeyFields().elementAt(index);
733                 Object JavaDoc targetKeyValue = getReferenceDescriptor().getObjectBuilder().extractValueFromObjectForField(object, targetKey, query.getSession());
734                 databaseRow.put(targetRelationKey, targetKeyValue);
735             }
736
737             query.getSession().executeQuery(getInsertQuery(), databaseRow);
738         }
739     }
740
741     /**
742      * INTERNAL:
743      * Write the target objects if the cascade policy requires them to be written first.
744      * They must be written within a unit of work to ensure that they exist.
745      */

746     public void insertTargetObjects(WriteObjectQuery query) throws DatabaseException, OptimisticLockException {
747         if (!shouldObjectModifyCascadeToParts(query)) {
748             return;
749         }
750
751         // Only cascade dependents writes in uow.
752
if (query.shouldCascadeOnlyDependentParts()) {
753             return;
754         }
755
756         Object JavaDoc objects = getRealCollectionAttributeValueFromObject(query.getObject(), query.getSession());
757
758         ContainerPolicy cp = getContainerPolicy();
759         if (cp.isEmpty(objects)) {
760             return;
761         }
762
763         // Write each of the target objects
764
for (Object JavaDoc objectsIterator = cp.iteratorFor(objects); cp.hasNext(objectsIterator);) {
765             Object JavaDoc object = cp.next(objectsIterator, query.getSession());
766             if (isPrivateOwned()) {
767                 // no need to set changeset as insert is a straight copy anyway
768
InsertObjectQuery insertQuery = new InsertObjectQuery();
769                 insertQuery.setObject(object);
770                 insertQuery.setCascadePolicy(query.getCascadePolicy());
771                 query.getSession().executeQuery(insertQuery);
772             } else {
773                 ObjectChangeSet changeSet = null;
774                 UnitOfWorkChangeSet uowChangeSet = null;
775                 if (query.getSession().isUnitOfWork() && (((UnitOfWorkImpl)query.getSession()).getUnitOfWorkChangeSet() != null)) {
776                     uowChangeSet = (UnitOfWorkChangeSet)((UnitOfWorkImpl)query.getSession()).getUnitOfWorkChangeSet();
777                     changeSet = (ObjectChangeSet)uowChangeSet.getObjectChangeSetForClone(object);
778                 }
779                 WriteObjectQuery writeQuery = new WriteObjectQuery();
780                 writeQuery.setObject(object);
781                 writeQuery.setObjectChangeSet(changeSet);
782                 writeQuery.setCascadePolicy(query.getCascadePolicy());
783                 query.getSession().executeQuery(writeQuery);
784             }
785         }
786     }
787
788     /**
789      * INTERNAL:
790      * Return if this mapping support joining.
791      */

792     public boolean isJoiningSupported() {
793         return true;
794     }
795
796     /**
797      * INTERNAL:
798      */

799     public boolean isManyToManyMapping() {
800         return true;
801     }
802
803     /**
804      * INTERNAL:
805      * Checks if a single source key was specified.
806      */

807     protected boolean isSingleSourceRelationKeySpecified() {
808         return getSourceKeyFields().isEmpty();
809     }
810
811     /**
812      * INTERNAL:
813      * Checks if a single target key was specified.
814      */

815     protected boolean isSingleTargetRelationKeySpecified() {
816         return getTargetKeyFields().isEmpty();
817     }
818
819     /**
820      * INTERNAL:
821      * An object was added to the collection during an update, insert it if private.
822      */

823     protected void objectAddedDuringUpdate(ObjectLevelModifyQuery query, Object JavaDoc objectAdded, ObjectChangeSet changeSet) throws DatabaseException, OptimisticLockException {
824         // First insert/update object.
825
super.objectAddedDuringUpdate(query, objectAdded, changeSet);
826
827         // In the uow data queries are cached until the end of the commit.
828
if (query.shouldCascadeOnlyDependentParts()) {
829             // Hey I might actually want to use an inner class here... ok array for now.
830
Object JavaDoc[] event = new Object JavaDoc[3];
831             event[0] = ObjectAdded;
832             event[1] = query;
833             event[2] = objectAdded;
834             query.getSession().getCommitManager().addDataModificationEvent(this, event);
835         } else {
836             insertAddedObjectEntry(query, objectAdded);
837         }
838     }
839
840     /**
841      * INTERNAL:
842      * An object was removed to the collection during an update, delete it if private.
843      */

844     protected void objectRemovedDuringUpdate(ObjectLevelModifyQuery query, Object JavaDoc objectDeleted) throws DatabaseException, OptimisticLockException {
845         AbstractRecord databaseRow = new DatabaseRecord();
846
847         // Extract primary key and value from the source.
848
for (int index = 0; index < getSourceRelationKeyFields().size(); index++) {
849             DatabaseField sourceRelationKey = (DatabaseField)getSourceRelationKeyFields().elementAt(index);
850             DatabaseField sourceKey = (DatabaseField)getSourceKeyFields().elementAt(index);
851             Object JavaDoc sourceKeyValue = query.getTranslationRow().get(sourceKey);
852             databaseRow.put(sourceRelationKey, sourceKeyValue);
853         }
854
855         // Extract target field and its value. Construct insert statement and execute it
856
for (int index = 0; index < getTargetRelationKeyFields().size(); index++) {
857             DatabaseField targetRelationKey = (DatabaseField)getTargetRelationKeyFields().elementAt(index);
858             DatabaseField targetKey = (DatabaseField)getTargetKeyFields().elementAt(index);
859             Object JavaDoc targetKeyValue = getReferenceDescriptor().getObjectBuilder().extractValueFromObjectForField(objectDeleted, targetKey, query.getSession());
860             databaseRow.put(targetRelationKey, targetKeyValue);
861         }
862
863         // In the uow data queries are cached until the end of the commit.
864
if (query.shouldCascadeOnlyDependentParts()) {
865             // Hey I might actually want to use an inner class here... ok array for now.
866
Object JavaDoc[] event = new Object JavaDoc[3];
867             event[0] = ObjectRemoved;
868             event[1] = getDeleteQuery();
869             event[2] = databaseRow;
870             query.getSession().getCommitManager().addDataModificationEvent(this, event);
871         } else {
872             query.getSession().executeQuery(getDeleteQuery(), databaseRow);
873         }
874
875         // Delete object after join entry is delete if private.
876
super.objectRemovedDuringUpdate(query, objectDeleted);
877     }
878
879     /**
880      * INTERNAL:
881      * Perform the commit event.
882      * This is used in the uow to delay data modifications.
883      */

884     public void performDataModificationEvent(Object JavaDoc[] event, AbstractSession session) throws DatabaseException, DescriptorException {
885         // Hey I might actually want to use an inner class here... ok array for now.
886
if (event[0] == PostInsert) {
887             insertIntoRelationTable((WriteObjectQuery)event[1]);
888         } else if (event[0] == ObjectRemoved) {
889             session.executeQuery((DataModifyQuery)event[1], (AbstractRecord)event[(2)]);
890         } else if (event[0] == ObjectAdded) {
891             insertAddedObjectEntry((WriteObjectQuery)event[1], event[2]);
892         } else {
893             throw DescriptorException.invalidDataModificationEventCode(event[0], this);
894         }
895     }
896
897     /**
898      * INTERNAL:
899      * Insert into relation table. This follows following steps.
900      * <p>- Extract primary key and its value from the source object.
901      * <p>- Extract target key and its value from the target object.
902      * <p>- Construct a insert statement with above fields and values for relation table.
903      * <p>- execute the statement.
904      * <p>- Repeat above three statements until all the target objects are done.
905      */

906     public void postInsert(WriteObjectQuery query) throws DatabaseException {
907         insertTargetObjects(query);
908         // Batch data modification in the uow
909
if (query.shouldCascadeOnlyDependentParts()) {
910             // Hey I might actually want to use an inner class here... ok array for now.
911
Object JavaDoc[] event = new Object JavaDoc[2];
912             event[0] = PostInsert;
913             event[1] = query;
914             query.getSession().getCommitManager().addDataModificationEvent(this, event);
915         } else {
916             insertIntoRelationTable(query);
917         }
918     }
919
920     /**
921      * INTERNAL:
922      * Update the relation table with the entries related to this mapping.
923      * Delete entries removed, insert entries added.
924      * If private also insert/delete/update target objects.
925      */

926     public void postUpdate(WriteObjectQuery query) throws DatabaseException {
927         if (isReadOnly()) {
928             return;
929         }
930
931         // If objects are not instantiated that means they are not changed.
932
if (!isAttributeValueInstantiated(query.getObject())) {
933             return;
934         }
935
936         Object JavaDoc objectsInMemoryModel = getRealCollectionAttributeValueFromObject(query.getObject(), query.getSession());
937
938         // This accesses the backup in uow otherwise goes to database (may be better of to delete all in non uow case).
939
Object JavaDoc currentObjectsInDB = readPrivateOwnedForObject(query);
940         if (currentObjectsInDB == null) {
941             currentObjectsInDB = getContainerPolicy().containerInstance(1);
942         }
943         compareObjectsAndWrite(currentObjectsInDB, objectsInMemoryModel, query);
944     }
945
946     /**
947      * INTERNAL:
948      * Delete entries related to this mapping from the relation table.
949      */

950     public void preDelete(DeleteObjectQuery query) throws DatabaseException {
951         Object JavaDoc objectsIterator = null;
952         ContainerPolicy containerPolicy = getContainerPolicy();
953
954         if (isReadOnly()) {
955             return;
956         }
957
958         Object JavaDoc objects = getRealCollectionAttributeValueFromObject(query.getObject(), query.getSession());
959
960         if (shouldObjectModifyCascadeToParts(query)) {
961             //this must be done up here because the select must be done before the entry in the relation table is deleted.
962
objectsIterator = containerPolicy.iteratorFor(objects);
963         }
964         prepareTranslationRow(query.getTranslationRow(), query.getObject(), query.getSession());
965         query.getSession().executeQuery(getDeleteAllQuery(), query.getTranslationRow());
966
967         // If privately owned delete the objects, this does not handle removed objects (i.e. verify delete, not req in uow).
968
// Does not try to optimize delete all like 1-m, (rarely used and hard to do).
969
if (shouldObjectModifyCascadeToParts(query)) {
970             if (objects != null) {
971                 //objectsIterator will not be null because cascade check will still return true.
972
while (containerPolicy.hasNext(objectsIterator)) {
973                     DeleteObjectQuery deleteQuery = new DeleteObjectQuery();
974                     deleteQuery.setObject(containerPolicy.next(objectsIterator, query.getSession()));
975                     deleteQuery.setCascadePolicy(query.getCascadePolicy());
976                     query.getSession().executeQuery(deleteQuery);
977                 }
978             }
979         }
980     }
981
982     /**
983      * INTERNAL:
984      * The translation row may require additional fields than the primary key if the mapping in not on the primary key.
985      */

986     protected void prepareTranslationRow(AbstractRecord translationRow, Object JavaDoc object, AbstractSession session) {
987         // Make sure that each source key field is in the translation row.
988
for (Enumeration sourceFieldsEnum = getSourceKeyFields().elements();
989                  sourceFieldsEnum.hasMoreElements();) {
990             DatabaseField sourceKey = (DatabaseField)sourceFieldsEnum.nextElement();
991             if (!translationRow.containsKey(sourceKey)) {
992                 Object JavaDoc value = getDescriptor().getObjectBuilder().extractValueFromObjectForField(object, sourceKey, session);
993                 translationRow.put(sourceKey, value);
994             }
995         }
996     }
997
998     /**
999      * PUBLIC:
1000     * The default delete query for mapping can be overridden by specifying the new query.
1001     * This query must delete the row from the M-M join table.
1002     */

1003    public void setCustomDeleteQuery(DataModifyQuery query) {
1004        setDeleteQuery(query);
1005        setHasCustomDeleteQuery(true);
1006    }
1007
1008    /**
1009     * PUBLIC:
1010     * The default insert query for mapping can be overridden by specifying the new query.
1011     * This query must insert the row into the M-M join table.
1012     */

1013    public void setCustomInsertQuery(DataModifyQuery query) {
1014        setInsertQuery(query);
1015        setHasCustomInsertQuery(true);
1016    }
1017
1018    protected void setDeleteQuery(DataModifyQuery deleteQuery) {
1019        this.deleteQuery = deleteQuery;
1020    }
1021
1022    /**
1023     * PUBLIC:
1024     * Set the receiver's delete SQL string. This allows the user to override the SQL
1025     * generated by TOPLink, with there own SQL or procedure call. The arguments are
1026     * translated from the fields of the source row, through replacing the field names
1027     * marked by '#' with the values for those fields.
1028     * This is used to delete a single entry from the M-M join table.
1029     * Example, 'delete from PROJ_EMP where PROJ_ID = #PROJ_ID AND EMP_ID = #EMP_ID'.
1030     */

1031    public void setDeleteSQLString(String JavaDoc sqlString) {
1032        DataModifyQuery query = new DataModifyQuery();
1033        query.setSQLString(sqlString);
1034        setCustomDeleteQuery(query);
1035    }
1036    
1037    /**
1038     * PUBLIC:
1039     * Set the receiver's delete Call. This allows the user to override the SQL
1040     * generated by TOPLink, with there own SQL or procedure call. The arguments are
1041     * translated from the fields of the source row.
1042     * This is used to delete a single entry from the M-M join table.
1043     * Example, 'new SQLCall("delete from PROJ_EMP where PROJ_ID = #PROJ_ID AND EMP_ID = #EMP_ID")'.
1044     */

1045    public void setDeleteCall(Call call) {
1046        DataModifyQuery query = new DataModifyQuery();
1047        query.setCall(call);
1048        setCustomDeleteQuery(query);
1049    }
1050
1051    protected void setHasCustomDeleteQuery(boolean hasCustomDeleteQuery) {
1052        this.hasCustomDeleteQuery = hasCustomDeleteQuery;
1053    }
1054
1055    protected void setHasCustomInsertQuery(boolean bool) {
1056        hasCustomInsertQuery = bool;
1057    }
1058
1059    protected void setInsertQuery(DataModifyQuery insertQuery) {
1060        this.insertQuery = insertQuery;
1061    }
1062
1063    /**
1064     * PUBLIC:
1065     * Set the receiver's insert SQL string. This allows the user to override the SQL
1066     * generated by TOPLink, with there own SQL or procedure call. The arguments are
1067     * translated from the fields of the source row, through replacing the field names
1068     * marked by '#' with the values for those fields.
1069     * This is used to insert an entry into the M-M join table.
1070     * Example, 'insert into PROJ_EMP (EMP_ID, PROJ_ID) values (#EMP_ID, #PROJ_ID)'.
1071     */

1072    public void setInsertSQLString(String JavaDoc sqlString) {
1073        DataModifyQuery query = new DataModifyQuery();
1074        query.setSQLString(sqlString);
1075        setCustomInsertQuery(query);
1076    }
1077    
1078    /**
1079     * PUBLIC:
1080     * Set the receiver's insert Call. This allows the user to override the SQL
1081     * generated by TOPLink, with there own SQL or procedure call. The arguments are
1082     * translated from the fields of the source row.
1083     * This is used to insert an entry into the M-M join table.
1084     * Example, 'new SQLCall("insert into PROJ_EMP (EMP_ID, PROJ_ID) values (#EMP_ID, #PROJ_ID)")'.
1085     */

1086    public void setInsertCall(Call call) {
1087        DataModifyQuery query = new DataModifyQuery();
1088        query.setCall(call);
1089        setCustomInsertQuery(query);
1090    }
1091
1092    /**
1093     * PUBLIC:
1094     * Set the relational table.
1095     * This is the join table that store both the source and target primary keys.
1096     */

1097    public void setRelationTable(DatabaseTable relationTable) {
1098        this.relationTable = relationTable;
1099    }
1100    
1101    /**
1102     * PUBLIC:
1103     * Set the name of the relational table.
1104     * This is the join table that store both the source and target primary keys.
1105     */

1106    public void setRelationTableName(String JavaDoc tableName) {
1107        relationTable = new DatabaseTable(tableName);
1108    }
1109
1110    /**
1111     * PUBLIC:
1112     * Set the name of the session to execute the mapping's queries under.
1113     * This can be used by the session broker to override the default session
1114     * to be used for the target class.
1115     */

1116    public void setSessionName(String JavaDoc name) {
1117        super.setSessionName(name);
1118        getInsertQuery().setSessionName(name);
1119        getDeleteQuery().setSessionName(name);
1120    }
1121
1122    /**
1123     * PUBLIC:
1124     * Set the source key field names associated with the mapping.
1125     * These must be in-order with the sourceRelationKeyFieldNames.
1126     */

1127    public void setSourceKeyFieldNames(Vector fieldNames) {
1128        Vector fields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(fieldNames.size());
1129        for (Enumeration fieldNamesEnum = fieldNames.elements(); fieldNamesEnum.hasMoreElements();) {
1130            fields.addElement(new DatabaseField((String JavaDoc)fieldNamesEnum.nextElement()));
1131        }
1132
1133        setSourceKeyFields(fields);
1134    }
1135
1136    /**
1137     * INTERNAL:
1138     * Set the source fields.
1139     */

1140    public void setSourceKeyFields(Vector sourceKeyFields) {
1141        this.sourceKeyFields = sourceKeyFields;
1142    }
1143
1144    /**
1145     * PUBLIC:
1146     * Set the source key field in the relation table.
1147     * This is the name of the foreign key in the relation table to the source's primary key field.
1148     * This method is used if the source primary key is a singleton only.
1149     */

1150    public void setSourceRelationKeyFieldName(String JavaDoc sourceRelationKeyFieldName) {
1151        getSourceRelationKeyFields().addElement(new DatabaseField(sourceRelationKeyFieldName));
1152    }
1153
1154    /**
1155     * PUBLIC:
1156     * Set the source relation key field names associated with the mapping.
1157     * These must be in-order with the sourceKeyFieldNames.
1158     */

1159    public void setSourceRelationKeyFieldNames(Vector fieldNames) {
1160        Vector fields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(fieldNames.size());
1161        for (Enumeration fieldNamesEnum = fieldNames.elements(); fieldNamesEnum.hasMoreElements();) {
1162            fields.addElement(new DatabaseField((String JavaDoc)fieldNamesEnum.nextElement()));
1163        }
1164
1165        setSourceRelationKeyFields(fields);
1166    }
1167
1168    /**
1169     * INTERNAL:
1170     * Set the source fields.
1171     */

1172    public void setSourceRelationKeyFields(Vector sourceRelationKeyFields) {
1173        this.sourceRelationKeyFields = sourceRelationKeyFields;
1174    }
1175
1176    /**
1177     * INTERNAL:
1178     * Set the target key field names associated with the mapping.
1179     * These must be in-order with the targetRelationKeyFieldNames.
1180     */

1181    public void setTargetKeyFieldNames(Vector fieldNames) {
1182        Vector fields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(fieldNames.size());
1183        for (Enumeration fieldNamesEnum = fieldNames.elements(); fieldNamesEnum.hasMoreElements();) {
1184            fields.addElement(new DatabaseField((String JavaDoc)fieldNamesEnum.nextElement()));
1185        }
1186
1187        setTargetKeyFields(fields);
1188    }
1189
1190    /**
1191     * INTERNAL:
1192     * Set the target fields.
1193     */

1194    public void setTargetKeyFields(Vector targetKeyFields) {
1195        this.targetKeyFields = targetKeyFields;
1196    }
1197
1198    /**
1199     * PUBLIC:
1200     * Set the target key field in the relation table.
1201     * This is the name of the foreign key in the relation table to the target's primary key field.
1202     * This method is used if the target's primary key is a singleton only.
1203     */

1204    public void setTargetRelationKeyFieldName(String JavaDoc targetRelationKeyFieldName) {
1205        getTargetRelationKeyFields().addElement(new DatabaseField(targetRelationKeyFieldName));
1206    }
1207
1208    /**
1209     * INTERNAL:
1210     * Set the target relation key field names associated with the mapping.
1211     * These must be in-order with the targetKeyFieldNames.
1212     */

1213    public void setTargetRelationKeyFieldNames(Vector fieldNames) {
1214        Vector fields = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(fieldNames.size());
1215        for (Enumeration fieldNamesEnum = fieldNames.elements(); fieldNamesEnum.hasMoreElements();) {
1216            fields.addElement(new DatabaseField((String JavaDoc)fieldNamesEnum.nextElement()));
1217        }
1218
1219        setTargetRelationKeyFields(fields);
1220    }
1221
1222    /**
1223     * INTERNAL:
1224     * Set the target fields.
1225     */

1226    public void setTargetRelationKeyFields(Vector targetRelationKeyFields) {
1227        this.targetRelationKeyFields = targetRelationKeyFields;
1228    }
1229}
1230
Popular Tags