1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.jdbc.meta;
20
21 import java.sql.SQLException;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.LinkedHashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32
33 import org.apache.commons.lang.StringUtils;
34 import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
35 import org.apache.openjpa.jdbc.kernel.JDBCStore;
36 import org.apache.openjpa.jdbc.meta.strats.NoneClassStrategy;
37 import org.apache.openjpa.jdbc.schema.Column;
38 import org.apache.openjpa.jdbc.schema.ColumnIO;
39 import org.apache.openjpa.jdbc.schema.ForeignKey;
40 import org.apache.openjpa.jdbc.schema.Schemas;
41 import org.apache.openjpa.jdbc.schema.Table;
42 import org.apache.openjpa.jdbc.sql.Joins;
43 import org.apache.openjpa.jdbc.sql.Result;
44 import org.apache.openjpa.jdbc.sql.RowManager;
45 import org.apache.openjpa.jdbc.sql.Select;
46 import org.apache.openjpa.kernel.FetchConfiguration;
47 import org.apache.openjpa.kernel.OpenJPAStateManager;
48 import org.apache.openjpa.kernel.PCState;
49 import org.apache.openjpa.lib.log.Log;
50 import org.apache.openjpa.lib.rop.ResultObjectProvider;
51 import org.apache.openjpa.lib.util.Localizer;
52 import org.apache.openjpa.meta.ClassMetaData;
53 import org.apache.openjpa.meta.ValueMetaData;
54 import org.apache.openjpa.util.ApplicationIds;
55 import org.apache.openjpa.util.InternalException;
56 import org.apache.openjpa.util.MetaDataException;
57 import org.apache.openjpa.util.OpenJPAId;
58 import org.apache.openjpa.util.ImplHelper;
59
60 /**
61 * Specialization of metadata for relational databases.
62 *
63 * @author Abe White
64 */
65 public class ClassMapping
66 extends ClassMetaData
67 implements ClassStrategy {
68
69 public static final ClassMapping[] EMPTY_MAPPINGS = new ClassMapping[0];
70
71 private static final Localizer _loc = Localizer.forPackage
72 (ClassMapping.class);
73
74 private final ClassMappingInfo _info;
75 private final Discriminator _discrim;
76 private final Version _version;
77 private ClassStrategy _strategy = null;
78
79 private Table _table = null;
80 private ColumnIO _io = null;
81 private Column[] _cols = Schemas.EMPTY_COLUMNS;
82 private ForeignKey _fk = null;
83 private int _subclassMode = Integer.MAX_VALUE;
84
85 private ClassMapping[] _joinSubMaps = null;
86 private ClassMapping[] _assignMaps = null;
87
88 // maps columns to joinables
89 private final Map _joinables = Collections.synchronizedMap(new HashMap());
90
91 /**
92 * Constructor. Supply described type and owning repository.
93 */
94 protected ClassMapping(Class type, MappingRepository repos) {
95 super(type, repos);
96 _discrim = repos.newDiscriminator(this);
97 _version = repos.newVersion(this);
98 _info = repos.newMappingInfo(this);
99 }
100
101 /**
102 * Embedded constructor. Supply embedding value and owning repository.
103 */
104 protected ClassMapping(ValueMetaData vmd) {
105 super(vmd);
106 _discrim = getMappingRepository().newDiscriminator(this);
107 _version = getMappingRepository().newVersion(this);
108 _info = getMappingRepository().newMappingInfo(this);
109 }
110
111 /**
112 * The class discriminator.
113 */
114 public Discriminator getDiscriminator() {
115 return _discrim;
116 }
117
118 /**
119 * The version indicator.
120 */
121 public Version getVersion() {
122 return _version;
123 }
124
125 ///////////
126 // Runtime
127 ///////////
128
129 /**
130 * Return the oid value stored in the result. This implementation will
131 * recurse until it finds an ancestor class who uses oid values for its
132 * primary key.
133 *
134 * @param fk if non-null, use the local columns of the given foreign
135 * key in place of this class' primary key columns
136 * @see #isPrimaryKeyObjectId
137 */
138 public Object getObjectId(JDBCStore store, Result res, ForeignKey fk,
139 boolean subs, Joins joins)
140 throws SQLException {
141 ValueMapping embed = getEmbeddingMapping();
142 if (embed != null)
143 return embed.getFieldMapping().getDefiningMapping().
144 getObjectId(store, res, fk, subs, joins);
145
146 return getObjectId(this, store, res, fk, subs, joins);
147 }
148
149 /**
150 * Recursive helper for public <code>getObjectId</code> method.
151 */
152 private Object getObjectId(ClassMapping cls, JDBCStore store, Result res,
153 ForeignKey fk, boolean subs, Joins joins)
154 throws SQLException {
155 if (!isPrimaryKeyObjectId(true))
156 return getPCSuperclassMapping().getObjectId(cls, store, res, fk,
157 subs, joins);
158 if (getIdentityType() == ID_UNKNOWN)
159 throw new InternalException();
160
161 Column[] pks = getPrimaryKeyColumns();
162 if (getIdentityType() == ID_DATASTORE) {
163 Column col = (fk == null) ? pks[0] : fk.getColumn(pks[0]);
164 long id = res.getLong(col, joins);
165 return (id == 0 && res.wasNull()) ? null
166 : store.newDataStoreId(id, cls, subs);
167 }
168
169 // application identity
170 Object[] vals = new Object[getPrimaryKeyFields().length];
171 FieldMapping fm;
172 Joinable join;
173 int pkIdx;
174 for (int i = 0; i < pks.length; i++) {
175 // we know that all pk column join mappings use primary key fields,
176 // cause this mapping uses the oid as its primary key (we recursed
177 // at the beginning of the method to ensure this)
178 join = assertJoinable(pks[i]);
179 fm = getFieldMapping(join.getFieldIndex());
180 pkIdx = fm.getPrimaryKeyIndex();
181
182 // could have already set value with previous multi-column joinable
183 if (vals[pkIdx] == null) {
184 res.startDataRequest(fm);
185 vals[pkIdx] = join.getPrimaryKeyValue(res, join.getColumns(),
186 fk, store, joins);
187 res.endDataRequest();
188 if (vals[pkIdx] == null)
189 return null;
190 }
191 }
192 Object oid = ApplicationIds.fromPKValues(vals, cls);
193
194 /**
195 * For polymorphic relations,
196 * the type field in the oid is initially set to base type.
197 * If the discriminator value is preset in the current result,
198 * then the type field needs reset based on the discriminator value.
199 * If the discriminator value is not present or invalid,
200 * ignore any exceptions being thrown.
201 */
202 if (oid instanceof OpenJPAId) {
203 Class type = cls.getDescribedType();
204 if (!subs)
205 // non-polymorphic relations
206 ((OpenJPAId) oid).setManagedInstanceType(type);
207 else if (cls.getDiscriminator() != null
208 && !StringUtils.equals("none",
209 cls.getDiscriminator().getStrategy().getAlias())) {
210 // polymorphic relations
211 res.startDataRequest(cls.getDiscriminator());
212 try {
213 type = cls.getDiscriminator().getClass(store, cls, res);
214 ((OpenJPAId) oid).setManagedInstanceType(type, true);
215 } catch (Exception e) {
216 // intentionally ignored
217 }
218 res.endDataRequest();
219 }
220 }
221 return oid;
222 }
223
224 /**
225 * Return the given column value(s) for the given object. The given
226 * columns will be primary key columns of this mapping, but may be in
227 * any order. If there is only one column, return its value. If there
228 * are multiple columns, return an object array of their values, in the
229 * same order the columns are given.
230 */
231 public Object toDataStoreValue(Object obj, Column[] cols, JDBCStore store) {
232 Object ret = (cols.length == 1) ? null : new Object[cols.length];
233
234 // in the past we've been lenient about being able to translate objects
235 // from other persistence contexts, so try to get sm directly from
236 // instance before asking our context
237 OpenJPAStateManager sm;
238 if (ImplHelper.isManageable(obj))
239 sm = (OpenJPAStateManager) (ImplHelper.toPersistenceCapable(obj,
240 getRepository().getConfiguration()))
241 .pcGetStateManager();
242 else
243 sm = store.getContext().getStateManager(obj);
244 if (sm == null)
245 return ret;
246
247 Object val;
248 for (int i = 0; i < cols.length; i++) {
249 val = assertJoinable(cols[i]).getJoinValue(sm, cols[i], store);
250 if (cols.length == 1)
251 ret = val;
252 else
253 ((Object[]) ret)[i] = val;
254 }
255 return ret;
256 }
257
258 /**
259 * Return the joinable for the given column, or throw an exception if
260 * none is available.
261 */
262 public Joinable assertJoinable(Column col) {
263 Joinable join = getJoinable(col);
264 if (join == null)
265 throw new MetaDataException(_loc.get("no-joinable",
266 col.getFullName()));
267 return join;
268 }
269
270 /**
271 * Return the {@link Joinable} for the given column. Any column that
272 * another mapping joins to must be controlled by a joinable.
273 */
274 public Joinable getJoinable(Column col) {
275 Joinable join;
276 if (getEmbeddingMetaData() != null) {
277 join = getEmbeddingMapping().getFieldMapping().
278 getDefiningMapping().getJoinable(col);
279 if (join != null)
280 return join;
281 }
282 ClassMapping sup = getJoinablePCSuperclassMapping();
283 if (sup != null) {
284 join = sup.getJoinable(col);
285 if (join != null)
286 return join;
287 }
288 return (Joinable) _joinables.get(col);
289 }
290
291 /**
292 * Add the given column-to-joinable mapping.
293 */
294 public void setJoinable(Column col, Joinable joinable) {
295 // don't let non-pk override pk
296 Joinable join = (Joinable) _joinables.get(col);
297 if (join == null || (join.getFieldIndex() != -1
298 && getField(join.getFieldIndex()).getPrimaryKeyIndex() == -1))
299 _joinables.put(col, joinable);
300 }
301
302 /**
303 * Return whether the columns of the given foreign key to this mapping
304 * can be used to construct an object id for this type. This is a
305 * relatively expensive operation; its results should be cached.
306 *
307 * @return {@link Boolean#TRUE} if the foreign key contains all oid
308 * columns, <code>null</code> if it contains only some columns,
309 * or {@link Boolean#FALSE} if it contains non-oid columns
310 */
311 public Boolean isForeignKeyObjectId(ForeignKey fk) {
312 // if this mapping's primary key can't construct an oid, then no way
313 // foreign key can
314 if (getIdentityType() == ID_UNKNOWN || !isPrimaryKeyObjectId(false))
315 return Boolean.FALSE;
316
317 // with datastore identity, it's all or nothing
318 Column[] cols = fk.getPrimaryKeyColumns();
319 if (getIdentityType() == ID_DATASTORE) {
320 if (cols.length != 1 || cols[0] != getPrimaryKeyColumns()[0])
321 return Boolean.FALSE;
322 return Boolean.TRUE;
323 }
324
325 // check the join mapping for each pk column to see if it links up to
326 // a primary key field
327 Joinable join;
328 for (int i = 0; i < cols.length; i++) {
329 join = assertJoinable(cols[i]);
330 if (join.getFieldIndex() != -1
331 && getField(join.getFieldIndex()).getPrimaryKeyIndex() == -1)
332 return Boolean.FALSE;
333 }
334
335 // if all primary key links, see whether we join to all pks
336 if (isPrimaryKeyObjectId(true)
337 && cols.length == getPrimaryKeyColumns().length)
338 return Boolean.TRUE;
339 return null;
340 }
341
342 ///////
343 // ORM
344 ///////
345
346 /**
347 * Raw mapping data.
348 */
349 public ClassMappingInfo getMappingInfo() {
350 return _info;
351 }
352
353 /**
354 * The strategy used to map this mapping.
355 */
356 public ClassStrategy getStrategy() {
357 return _strategy;
358 }
359
360 /**
361 * The strategy used to map this mapping. The <code>adapt</code>
362 * parameter determines whether to adapt when mapping the strategy;
363 * use null if the strategy should not be mapped.
364 */
365 public void setStrategy(ClassStrategy strategy, Boolean adapt) {
366 // set strategy first so we can access it during mapping
367 ClassStrategy orig = _strategy;
368 _strategy = strategy;
369 if (strategy != null) {
370 try {
371 strategy.setClassMapping(this);
372 if (adapt != null)
373 strategy.map(adapt.booleanValue());
374 } catch (RuntimeException re) {
375 // reset strategy
376 _strategy = orig;
377 throw re;
378 }
379 }
380 }
381
382 /**
383 * The mapping's primary table.
384 */
385 public Table getTable() {
386 return _table;
387 }
388
389 /**
390 * The mapping's primary table.
391 */
392 public void setTable(Table table) {
393 _table = table;
394 }
395
396 /**
397 * The columns this mapping uses to uniquely identify an object.
398 * These will typically be the primary key columns or the columns this
399 * class uses to link to its superclass table.
400 */
401 public Column[] getPrimaryKeyColumns() {
402 if (_cols.length == 0 && getIdentityType() == ID_APPLICATION
403 && isMapped()) {
404 FieldMapping[] pks = getPrimaryKeyFieldMappings();
405 Collection cols = new ArrayList(pks.length);
406 Column[] fieldCols;
407 for (int i = 0; i < pks.length; i++) {
408 fieldCols = pks[i].getColumns();
409 for (int j = 0; j < fieldCols.length; j++)
410 cols.add(fieldCols[j]);
411 }
412 _cols = (Column[]) cols.toArray(new Column[cols.size()]);
413 }
414 return _cols;
415 }
416
417 /**
418 * The columns this mapping uses to uniquely identify an object.
419 * These will typically be the primary key columns or the columns this
420 * class uses to link to its superclass table.
421 */
422 public void setPrimaryKeyColumns(Column[] cols) {
423 if (cols == null)
424 cols = Schemas.EMPTY_COLUMNS;
425 _cols = cols;
426 }
427
428 /**
429 * I/O information on the key columns / join key.
430 */
431 public ColumnIO getColumnIO() {
432 return (_io == null) ? ColumnIO.UNRESTRICTED : _io;
433 }
434
435 /**
436 * I/O information on the key columns / join key.
437 */
438 public void setColumnIO(ColumnIO io) {
439 _io = io;
440 }
441
442 /**
443 * Foreign key linking the primary key columns to the superclass table,
444 * or null if none.
445 */
446 public ForeignKey getJoinForeignKey() {
447 return _fk;
448 }
449
450 /**
451 * Foreign key linking the primary key columns to the superclass table,
452 * or null if none.
453 */
454 public void setJoinForeignKey(ForeignKey fk) {
455 _fk = fk;
456 }
457
458 public void refSchemaComponents() {
459 if (getEmbeddingMetaData() == null) {
460 if (_table != null && _table.getPrimaryKey() != null)
461 _table.getPrimaryKey().ref();
462 if (_fk != null)
463 _fk.ref();
464 Column[] pks = getPrimaryKeyColumns();
465 for (int i = 0; i < pks.length; i++)
466 pks[i].ref();
467 } else {
468 FieldMapping[] fields = getFieldMappings();
469 for (int i = 0; i < fields.length; i++)
470 fields[i].refSchemaComponents();
471 }
472 }
473
474 /**
475 * Clear mapping information, including strategy.
476 */
477 public void clearMapping() {
478 _strategy = null;
479 _cols = Schemas.EMPTY_COLUMNS;
480 _fk = null;
481 _table = null;
482 _info.clear();
483 setResolve(MODE_MAPPING | MODE_MAPPING_INIT, false);
484 }
485
486 /**
487 * Update {@link MappingInfo} with our current mapping information.
488 */
489 public void syncMappingInfo() {
490 if (getEmbeddingMetaData() == null)
491 _info.syncWith(this);
492 else {
493 _info.clear();
494 FieldMapping[] fields = getFieldMappings();
495 for (int i = 0; i < fields.length; i++)
496 fields[i].syncMappingInfo();
497 }
498 }
499
500 //////////////////////
501 // MetaData interface
502 //////////////////////
503
504 protected void setDescribedType(Class type) {
505 super.setDescribedType(type);
506 // this method called from superclass constructor, so _info not yet
507 // initialized
508 if (_info != null)
509 _info.setClassName(type.getName());
510 }
511
512 /**
513 * The subclass fetch mode, as one of the eager constants in
514 * {@link JDBCFetchConfiguration}.
515 */
516 public int getSubclassFetchMode() {
517 if (_subclassMode == Integer.MAX_VALUE) {
518 if (getPCSuperclass() != null)
519 _subclassMode = getPCSuperclassMapping().
520 getSubclassFetchMode();
521 else
522 _subclassMode = FetchConfiguration.DEFAULT;
523 }
524 return _subclassMode;
525 }
526
527 /**
528 * The subclass fetch mode, as one of the eager constants in
529 * {@link JDBCFetchConfiguration}.
530 */
531 public void setSubclassFetchMode(int mode) {
532 _subclassMode = mode;
533 }
534
535 /**
536 * Convenience method to perform cast from
537 * {@link ClassMetaData#getRepository}.
538 */
539 public MappingRepository getMappingRepository() {
540 return (MappingRepository) getRepository();
541 }
542
543 /**
544 * Convenience method to perform cast from
545 * {@link ClassMetaData#getEmbeddingMetaData}
546 */
547 public ValueMapping getEmbeddingMapping() {
548 return (ValueMapping) getEmbeddingMetaData();
549 }
550
551 /**
552 * Returns true if this class does not use the "none" strategy (including
553 * if it has a null strategy, and therefore is probably in the process of
554 * being mapped).
555 */
556 public boolean isMapped() {
557 if (!super.isMapped())
558 return false;
559 if (_strategy != null)
560 return _strategy != NoneClassStrategy.getInstance();
561 return !NoneClassStrategy.ALIAS.equals(_info.getStrategy());
562 }
563
564 /**
565 * Convenience method to perform cast from
566 * {@link ClassMetaData#getPCSuperclassMetaData}.
567 */
568 public ClassMapping getPCSuperclassMapping() {
569 return (ClassMapping) getPCSuperclassMetaData();
570 }
571
572 /**
573 * Convenience method to perform cast from
574 * {@link ClassMetaData#getMappedPCSuperclassMetaData}.
575 */
576 public ClassMapping getMappedPCSuperclassMapping() {
577 return (ClassMapping) getMappedPCSuperclassMetaData();
578 }
579
580 /**
581 * Return the nearest mapped superclass that can join to this class.
582 */
583 public ClassMapping getJoinablePCSuperclassMapping() {
584 ClassMapping sup = getMappedPCSuperclassMapping();
585 if (sup == null)
586 return null;
587 if (_fk != null || _table == null || _table.equals(sup.getTable()))
588 return sup;
589 return null;
590 }
591
592 /**
593 * Convenience method to perform cast from
594 * {@link ClassMetaData#getPCSubclassMetaDatas}.
595 */
596 public ClassMapping[] getPCSubclassMappings() {
597 return (ClassMapping[]) getPCSubclassMetaDatas();
598 }
599
600 /**
601 * Convenience method to perform cast from
602 * {@link ClassMetaData#getMappedPCSubclassMetaDatas}.
603 */
604 public ClassMapping[] getMappedPCSubclassMappings() {
605 return (ClassMapping[]) getMappedPCSubclassMetaDatas();
606 }
607
608 /**
609 * Return mapped subclasses that are reachable via joins.
610 */
611 public ClassMapping[] getJoinablePCSubclassMappings() {
612 ClassMapping[] subs = getMappedPCSubclassMappings(); // checks for new
613 if (_joinSubMaps == null) {
614 if (subs.length == 0)
615 _joinSubMaps = subs;
616 else {
617 List joinable = new ArrayList(subs.length);
618 for (int i = 0; i < subs.length; i++)
619 if (isSubJoinable(subs[i]))
620 joinable.add(subs[i]);
621 _joinSubMaps = (ClassMapping[]) joinable.toArray
622 (new ClassMapping[joinable.size()]);
623 }
624 }
625 return _joinSubMaps;
626 }
627
628 /**
629 * Return whether we can reach the given subclass via joins.
630 */
631 private boolean isSubJoinable(ClassMapping sub) {
632 if (sub == null)
633 return false;
634 if (sub == this)
635 return true;
636 return isSubJoinable(sub.getJoinablePCSuperclassMapping());
637 }
638
639 /**
640 * Returns the closest-derived list of non-inter-joinable mapped types
641 * assignable to this type. May return this mapping.
642 */
643 public ClassMapping[] getIndependentAssignableMappings() {
644 ClassMapping[] subs = getMappedPCSubclassMappings(); // checks for new
645 if (_assignMaps == null) {
646 // remove unmapped subs
647 if (subs.length == 0) {
648 if (isMapped())
649 _assignMaps = new ClassMapping[]{ this };
650 else
651 _assignMaps = subs;
652 } else {
653 int size = (int) (subs.length * 1.33 + 2);
654 Set independent = new LinkedHashSet(size);
655 if (isMapped())
656 independent.add(this);
657 independent.addAll(Arrays.asList(subs));
658
659 // remove all mappings that have a superclass mapping in the set
660 ClassMapping map, sup;
661 List clear = null;
662 for (Iterator itr = independent.iterator(); itr.hasNext();) {
663 map = (ClassMapping) itr.next();
664 sup = map.getJoinablePCSuperclassMapping();
665 if (sup != null && independent.contains(sup)) {
666 if (clear == null)
667 clear = new ArrayList(independent.size() - 1);
668 clear.add(map);
669 }
670 }
671 if (clear != null)
672 independent.removeAll(clear);
673
674 _assignMaps = (ClassMapping[]) independent.toArray
675 (new ClassMapping[independent.size()]);
676 }
677 }
678 return _assignMaps;
679 }
680
681 /**
682 * Convenience method to perform cast from {@link ClassMetaData#getFields}.
683 */
684 public FieldMapping[] getFieldMappings() {
685 return (FieldMapping[]) getFields();
686 }
687
688 /**
689 * Convenience method to perform cast from
690 * {@link ClassMetaData#getDeclaredFields}.
691 */
692 public FieldMapping[] getDeclaredFieldMappings() {
693 return (FieldMapping[]) getDeclaredFields();
694 }
695
696 /**
697 * Convenience method to perform cast from
698 * {@link ClassMetaData#getPrimaryKeyFields}.
699 */
700 public FieldMapping[] getPrimaryKeyFieldMappings() {
701 return (FieldMapping[]) getPrimaryKeyFields();
702 }
703
704 /**
705 * Convenience method to perform cast from
706 * {@link ClassMetaData#getVersionField}.
707 */
708 public FieldMapping getVersionFieldMapping() {
709 return (FieldMapping) getVersionField();
710 }
711
712 /**
713 * Convenience method to perform cast from
714 * {@link ClassMetaData#getDefaultFetchGroupFields}.
715 */
716 public FieldMapping[] getDefaultFetchGroupFieldMappings() {
717 return (FieldMapping[]) getDefaultFetchGroupFields();
718 }
719
720 /**
721 * Convenience method to perform cast from
722 * {@link ClassMetaData#getDefinedFields}.
723 */
724 public FieldMapping[] getDefinedFieldMappings() {
725 return (FieldMapping[]) getDefinedFields();
726 }
727
728 /**
729 * Convenience method to perform cast from
730 * {@link ClassMetaData#getFieldsInListingOrder}.
731 */
732 public FieldMapping[] getFieldMappingsInListingOrder() {
733 return (FieldMapping[]) getFieldsInListingOrder();
734 }
735
736 /**
737 * Convenience method to perform cast from
738 * {@link ClassMetaData#getDefinedFieldsInListingOrder}.
739 */
740 public FieldMapping[] getDefinedFieldMappingsInListingOrder() {
741 return (FieldMapping[]) getDefinedFieldsInListingOrder();
742 }
743
744 /**
745 * Convenience method to perform cast from {@link ClassMetaData#getField}.
746 */
747 public FieldMapping getFieldMapping(int index) {
748 return (FieldMapping) getField(index);
749 }
750
751 /**
752 * Convenience method to perform cast from
753 * {@link ClassMetaData#getDeclaredField}.
754 */
755 public FieldMapping getDeclaredFieldMapping(int index) {
756 return (FieldMapping) getDeclaredField(index);
757 }
758
759 /**
760 * Convenience method to perform cast from {@link ClassMetaData#getField}.
761 */
762 public FieldMapping getFieldMapping(String name) {
763 return (FieldMapping) getField(name);
764 }
765
766 /**
767 * Convenience method to perform cast from
768 * {@link ClassMetaData#getDeclaredField}.
769 */
770 public FieldMapping getDeclaredFieldMapping(String name) {
771 return (FieldMapping) getDeclaredField(name);
772 }
773
774 /**
775 * Convenience method to perform cast from
776 * {@link ClassMetaData#getDeclaredUnmanagedFields}.
777 */
778 public FieldMapping[] getDeclaredUnmanagedFieldMappings() {
779 return (FieldMapping[]) getDeclaredUnmanagedFields();
780 }
781
782 /**
783 * Convenience method to perform cast from
784 * {@link ClassMetaData#addDeclaredField}.
785 */
786 public FieldMapping addDeclaredFieldMapping(String name, Class type) {
787 return (FieldMapping) addDeclaredField(name, type);
788 }
789
790 protected void resolveMapping(boolean runtime) {
791 super.resolveMapping(runtime);
792
793 // map class strategy; it may already be mapped by the repository before
794 // the resolve process begins
795 MappingRepository repos = getMappingRepository();
796 if (_strategy == null)
797 repos.getStrategyInstaller().installStrategy(this);
798 Log log = getRepository().getLog();
799 if (log.isTraceEnabled())
800 log.trace(_loc.get("strategy", this, _strategy.getAlias()));
801
802 // make sure unmapped superclass fields are defined if we're mapped;
803 // also may have been done by repository already
804 defineSuperclassFields(getJoinablePCSuperclassMapping() == null);
805
806 // resolve everything that doesn't rely on any relations to avoid
807 // recursion, then resolve all fields
808 resolveNonRelationMappings();
809 FieldMapping[] fms = getFieldMappings();
810 for (int i = 0; i < fms.length; i++)
811 if (fms[i].getDefiningMetaData() == this)
812 fms[i].resolve(MODE_MAPPING);
813 fms = getDeclaredUnmanagedFieldMappings();
814 for (int i = 0; i < fms.length; i++)
815 fms[i].resolve(MODE_MAPPING);
816
817 // mark mapped columns
818 if (_cols != null) {
819 ColumnIO io = getColumnIO();
820 for (int i = 0; i < _cols.length; i++) {
821 if (io.isInsertable(i, false))
822 _cols[i].setFlag(Column.FLAG_DIRECT_INSERT, true);
823 if (io.isUpdatable(i, false))
824 _cols[i].setFlag(Column.FLAG_DIRECT_UPDATE, true);
825 }
826 }
827 // once columns are resolved, resolve unique constraints as they need
828 // the columns be resolved
829 _info.getUniques(this, true);
830 }
831
832 /**
833 * Resolve non-relation field mappings so that when we do relation
834 * mappings they can rely on them for joins.
835 */
836 void resolveNonRelationMappings() {
837 // make sure primary key fields are resolved first because other
838 // fields might rely on them
839 FieldMapping[] fms = getPrimaryKeyFieldMappings();
840 for (int i = 0; i < fms.length; i++)
841 fms[i].resolve(MODE_MAPPING);
842
843 // resolve defined fields that are safe; that don't rely on other types
844 // also being resolved. don't use getDefinedFields b/c it relies on
845 // whether fields are mapped, which isn't known yet
846 fms = getFieldMappings();
847 for (int i = 0; i < fms.length; i++)
848 if (fms[i].getDefiningMetaData() == this
849 && !fms[i].isTypePC() && !fms[i].getKey().isTypePC()
850 && !fms[i].getElement().isTypePC())
851 fms[i].resolve(MODE_MAPPING);
852
853 _discrim.resolve(MODE_MAPPING);
854 _version.resolve(MODE_MAPPING);
855 }
856
857 protected void initializeMapping() {
858 super.initializeMapping();
859
860 FieldMapping[] fields = getDefinedFieldMappings();
861 for (int i = 0; i < fields.length; i++)
862 fields[i].resolve(MODE_MAPPING_INIT);
863 _discrim.resolve(MODE_MAPPING_INIT);
864 _version.resolve(MODE_MAPPING_INIT);
865 _strategy.initialize();
866 }
867
868 protected void clearDefinedFieldCache() {
869 // just make this method available to other classes in this package
870 super.clearDefinedFieldCache();
871 }
872
873 protected void clearSubclassCache() {
874 super.clearSubclassCache();
875 _joinSubMaps = null;
876 _assignMaps = null;
877 }
878
879 public void copy(ClassMetaData cls) {
880 super.copy(cls);
881 if (_subclassMode == Integer.MAX_VALUE)
882 _subclassMode = ((ClassMapping) cls).getSubclassFetchMode();
883 }
884
885 protected boolean validateDataStoreExtensionPrefix(String prefix) {
886 return "jdbc-".equals(prefix);
887 }
888
889 ////////////////////////////////
890 // ClassStrategy implementation
891 ////////////////////////////////
892
893 public String getAlias() {
894 return assertStrategy().getAlias();
895 }
896
897 public void map(boolean adapt) {
898 assertStrategy().map(adapt);
899 }
900
901 public void initialize() {
902 assertStrategy().initialize();
903 }
904
905 public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
906 throws SQLException {
907 assertStrategy().insert(sm, store, rm);
908 }
909
910 public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
911 throws SQLException {
912 assertStrategy().update(sm, store, rm);
913 }
914
915 public void delete(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
916 throws SQLException {
917 assertStrategy().delete(sm, store, rm);
918 }
919
920 public Boolean isCustomInsert(OpenJPAStateManager sm, JDBCStore store) {
921 return assertStrategy().isCustomInsert(sm, store);
922 }
923
924 public Boolean isCustomUpdate(OpenJPAStateManager sm, JDBCStore store) {
925 return assertStrategy().isCustomUpdate(sm, store);
926 }
927
928 public Boolean isCustomDelete(OpenJPAStateManager sm, JDBCStore store) {
929 return assertStrategy().isCustomDelete(sm, store);
930 }
931
932 public void customInsert(OpenJPAStateManager sm, JDBCStore store)
933 throws SQLException {
934 assertStrategy().customInsert(sm, store);
935 }
936
937 public void customUpdate(OpenJPAStateManager sm, JDBCStore store)
938 throws SQLException {
939 assertStrategy().customUpdate(sm, store);
940 }
941
942 public void customDelete(OpenJPAStateManager sm, JDBCStore store)
943 throws SQLException {
944 assertStrategy().customDelete(sm, store);
945 }
946
947 public void setClassMapping(ClassMapping owner) {
948 assertStrategy().setClassMapping(owner);
949 }
950
951 public boolean isPrimaryKeyObjectId(boolean hasAll) {
952 return assertStrategy().isPrimaryKeyObjectId(hasAll);
953 }
954
955 public Joins joinSuperclass(Joins joins, boolean toThis) {
956 return assertStrategy().joinSuperclass(joins, toThis);
957 }
958
959 public boolean supportsEagerSelect(Select sel, OpenJPAStateManager sm,
960 JDBCStore store, ClassMapping base, JDBCFetchConfiguration fetch) {
961 return assertStrategy().supportsEagerSelect(sel, sm, store, base,
962 fetch);
963 }
964
965 public ResultObjectProvider customLoad(JDBCStore store, boolean subclasses,
966 JDBCFetchConfiguration fetch, long startIdx, long endIdx)
967 throws SQLException {
968 return assertStrategy().customLoad(store, subclasses, fetch,
969 startIdx, endIdx);
970 }
971
972 public boolean customLoad(OpenJPAStateManager sm, JDBCStore store,
973 PCState state, JDBCFetchConfiguration fetch)
974 throws SQLException, ClassNotFoundException {
975 return assertStrategy().customLoad(sm, store, state, fetch);
976 }
977
978 public boolean customLoad(OpenJPAStateManager sm, JDBCStore store,
979 JDBCFetchConfiguration fetch, Result result)
980 throws SQLException {
981 return assertStrategy().customLoad(sm, store, fetch, result);
982 }
983
984 private ClassStrategy assertStrategy() {
985 if (_strategy == null)
986 throw new InternalException();
987 return _strategy;
988 }
989 }