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.lang.reflect.Modifier;
22 import java.security.AccessController;
23 import java.security.PrivilegedActionException;
24 import java.sql.Types;
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29
30 import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
31 import org.apache.openjpa.jdbc.meta.strats.BlobValueHandler;
32 import org.apache.openjpa.jdbc.meta.strats.ByteArrayValueHandler;
33 import org.apache.openjpa.jdbc.meta.strats.CharArrayStreamValueHandler;
34 import org.apache.openjpa.jdbc.meta.strats.CharArrayValueHandler;
35 import org.apache.openjpa.jdbc.meta.strats.ClassNameDiscriminatorStrategy;
36 import org.apache.openjpa.jdbc.meta.strats.ClobValueHandler;
37 import org.apache.openjpa.jdbc.meta.strats.ElementEmbedValueHandler;
38 import org.apache.openjpa.jdbc.meta.strats.EmbedFieldStrategy;
39 import org.apache.openjpa.jdbc.meta.strats.EmbeddedClassStrategy;
40 import org.apache.openjpa.jdbc.meta.strats.FlatClassStrategy;
41 import org.apache.openjpa.jdbc.meta.strats.FullClassStrategy;
42 import org.apache.openjpa.jdbc.meta.strats.HandlerCollectionTableFieldStrategy;
43 import org.apache.openjpa.jdbc.meta.strats.HandlerFieldStrategy;
44 import org.apache.openjpa.jdbc.meta.strats.HandlerHandlerMapTableFieldStrategy;
45 import org.apache.openjpa.jdbc.meta.strats.HandlerRelationMapTableFieldStrategy;
46 import org.apache.openjpa.jdbc.meta.strats.ImmutableValueHandler;
47 import org.apache.openjpa.jdbc.meta.strats.LobFieldStrategy;
48 import org.apache.openjpa.jdbc.meta.strats.MaxEmbeddedBlobFieldStrategy;
49 import org.apache.openjpa.jdbc.meta.strats.MaxEmbeddedByteArrayFieldStrategy;
50 import org.apache.openjpa.jdbc.meta.strats.MaxEmbeddedCharArrayFieldStrategy;
51 import org.apache.openjpa.jdbc.meta.strats.MaxEmbeddedClobFieldStrategy;
52 import org.apache.openjpa.jdbc.meta.strats.NanoPrecisionTimestampVersionStrategy;
53 import org.apache.openjpa.jdbc.meta.strats.NoneClassStrategy;
54 import org.apache.openjpa.jdbc.meta.strats.NoneDiscriminatorStrategy;
55 import org.apache.openjpa.jdbc.meta.strats.NoneFieldStrategy;
56 import org.apache.openjpa.jdbc.meta.strats.NoneVersionStrategy;
57 import org.apache.openjpa.jdbc.meta.strats.NumberVersionStrategy;
58 import org.apache.openjpa.jdbc.meta.strats.ObjectIdClassStrategy;
59 import org.apache.openjpa.jdbc.meta.strats.ObjectIdValueHandler;
60 import org.apache.openjpa.jdbc.meta.strats.PrimitiveFieldStrategy;
61 import org.apache.openjpa.jdbc.meta.strats.RelationCollectionInverseKeyFieldStrategy;
62 import org.apache.openjpa.jdbc.meta.strats.RelationCollectionTableFieldStrategy;
63 import org.apache.openjpa.jdbc.meta.strats.RelationFieldStrategy;
64 import org.apache.openjpa.jdbc.meta.strats.RelationHandlerMapTableFieldStrategy;
65 import org.apache.openjpa.jdbc.meta.strats.RelationMapInverseKeyFieldStrategy;
66 import org.apache.openjpa.jdbc.meta.strats.RelationMapTableFieldStrategy;
67 import org.apache.openjpa.jdbc.meta.strats.RelationRelationMapTableFieldStrategy;
68 import org.apache.openjpa.jdbc.meta.strats.StateComparisonVersionStrategy;
69 import org.apache.openjpa.jdbc.meta.strats.StringFieldStrategy;
70 import org.apache.openjpa.jdbc.meta.strats.SubclassJoinDiscriminatorStrategy;
71 import org.apache.openjpa.jdbc.meta.strats.SuperclassDiscriminatorStrategy;
72 import org.apache.openjpa.jdbc.meta.strats.SuperclassVersionStrategy;
73 import org.apache.openjpa.jdbc.meta.strats.TimestampVersionStrategy;
74 import org.apache.openjpa.jdbc.meta.strats.UntypedPCValueHandler;
75 import org.apache.openjpa.jdbc.meta.strats.ValueMapDiscriminatorStrategy;
76 import org.apache.openjpa.jdbc.meta.strats.VerticalClassStrategy;
77 import org.apache.openjpa.jdbc.schema.Column;
78 import org.apache.openjpa.jdbc.schema.SchemaGroup;
79 import org.apache.openjpa.jdbc.sql.DBDictionary;
80 import org.apache.openjpa.jdbc.sql.JoinSyntaxes;
81 import org.apache.openjpa.lib.conf.Configurable;
82 import org.apache.openjpa.lib.conf.Configurations;
83 import org.apache.openjpa.lib.util.J2DoPrivHelper;
84 import org.apache.openjpa.lib.util.JavaVersions;
85 import org.apache.openjpa.lib.util.Localizer;
86 import org.apache.openjpa.meta.ClassMetaData;
87 import org.apache.openjpa.meta.FieldMetaData;
88 import org.apache.openjpa.meta.JavaTypes;
89 import org.apache.openjpa.meta.MetaDataRepository;
90 import org.apache.openjpa.meta.Order;
91 import org.apache.openjpa.meta.SequenceMetaData;
92 import org.apache.openjpa.meta.ValueMetaData;
93 import org.apache.openjpa.util.MetaDataException;
94
95 /**
96 * Repository of object/relational mapping information.
97 *
98 * @author Abe White
99 */
100 public class MappingRepository
101 extends MetaDataRepository {
102
103 private static final Localizer _loc = Localizer.forPackage
104 (MappingRepository.class);
105
106 private transient DBDictionary _dict = null;
107 private transient MappingDefaults _defaults = null;
108
109 private Map _results = new HashMap(); // object->queryresultmapping
110 private SchemaGroup _schema = null;
111 private StrategyInstaller _installer = null;
112
113 /**
114 * Default constructor. Configure via
115 * {@link org.apache.openjpa.lib.conf.Configurable}.
116 */
117 public MappingRepository() {
118 setValidate(VALIDATE_MAPPING, true);
119 }
120
121 /**
122 * Convenient access to dictionary for mappings.
123 */
124 public DBDictionary getDBDictionary() {
125 return _dict;
126 }
127
128 /**
129 * Mapping defaults.
130 */
131 public MappingDefaults getMappingDefaults() {
132 return _defaults;
133 }
134
135 /**
136 * Mapping default.
137 */
138 public void setMappingDefaults(MappingDefaults defaults) {
139 _defaults = defaults;
140 }
141
142 /**
143 * Representation of the database schema.
144 */
145 public synchronized SchemaGroup getSchemaGroup() {
146 if (_schema == null)
147 _schema = ((JDBCConfiguration) getConfiguration()).
148 getSchemaFactoryInstance().readSchema();
149 return _schema;
150 }
151
152 /**
153 * Representation of the database schema.
154 */
155 public synchronized void setSchemaGroup(SchemaGroup schema) {
156 _schema = schema;
157 }
158
159 /**
160 * Installs mapping strategies on components.
161 */
162 public synchronized StrategyInstaller getStrategyInstaller() {
163 if (_installer == null)
164 _installer = new RuntimeStrategyInstaller(this);
165 return _installer;
166 }
167
168 /**
169 * Installs mapping strategies on components.
170 */
171 public synchronized void setStrategyInstaller(StrategyInstaller installer) {
172 _installer = installer;
173 }
174
175 /**
176 * Return the query result mapping for the given name.
177 */
178 public synchronized QueryResultMapping getQueryResultMapping(Class cls,
179 String name, ClassLoader envLoader, boolean mustExist) {
180 QueryResultMapping res = getQueryResultMappingInternal(cls, name,
181 envLoader);
182 if (res == null && mustExist)
183 throw new MetaDataException(_loc.get("no-query-res", cls, name));
184 return res;
185 }
186
187 /**
188 * Returned the query result mapping with the given name.
189 */
190 private QueryResultMapping getQueryResultMappingInternal(Class cls,
191 String name, ClassLoader envLoader) {
192 if (name == null)
193 return null;
194
195 // check cache
196 Object key = getQueryResultKey(cls, name);
197 QueryResultMapping res = (QueryResultMapping) _results.get(key);
198 if (res != null)
199 return res;
200
201 // get metadata for class, which will find results in metadata file
202 if (cls != null && getMetaData(cls, envLoader, false) != null) {
203 res = (QueryResultMapping) _results.get(key);
204 if (res != null)
205 return res;
206 }
207 if ((getSourceMode() & MODE_QUERY) == 0)
208 return null;
209
210 if (cls == null)
211 cls = getMetaDataFactory()
212 .getResultSetMappingScope(name, envLoader);
213 // not in cache; load
214 getMetaDataFactory().load(cls, MODE_META | MODE_MAPPING, envLoader);
215 return (QueryResultMapping) _results.get(key);
216 }
217
218 /**
219 * Return all cached query result mappings.
220 */
221 public synchronized QueryResultMapping[] getQueryResultMappings() {
222 Collection values = _results.values();
223 return (QueryResultMapping[]) values.toArray
224 (new QueryResultMapping[values.size()]);
225 }
226
227 /**
228 * Return the cached query result mapping with the given name, or null if
229 * none.
230 */
231 public synchronized QueryResultMapping getCachedQueryResultMapping
232 (Class cls, String name) {
233 return (QueryResultMapping) _results.get(getQueryResultKey(cls, name));
234 }
235
236 /**
237 * Add a query result mapping.
238 */
239 public synchronized QueryResultMapping addQueryResultMapping(Class cls,
240 String name) {
241 QueryResultMapping res = new QueryResultMapping(name, this);
242 res.setDefiningType(cls);
243 _results.put(getQueryResultKey(res), res);
244 return res;
245 }
246
247 /**
248 * Remove a query result mapping.
249 */
250 public synchronized boolean removeQueryResultMapping
251 (QueryResultMapping res) {
252 return _results.remove(getQueryResultKey(res)) != null;
253 }
254
255 /**
256 * Remove a query result mapping.
257 */
258 public synchronized boolean removeQueryResultMapping(Class cls,
259 String name) {
260 if (name == null)
261 return false;
262 return _results.remove(getQueryResultKey(cls, name)) != null;
263 }
264
265 /**
266 * Return a unique key for the given mapping.
267 */
268 private static Object getQueryResultKey(QueryResultMapping res) {
269 if (res == null)
270 return null;
271 return getQueryResultKey(res.getDefiningType(), res.getName());
272 }
273
274 /**
275 * Return a unique key for the given class / name. The class argument
276 * can be null.
277 */
278 private static Object getQueryResultKey(Class cls, String name) {
279 return getQueryKey(cls, name);
280 }
281
282 public ClassMapping getMapping(Class cls, ClassLoader envLoader,
283 boolean mustExist) {
284 return (ClassMapping) super.getMetaData(cls, envLoader, mustExist);
285 }
286
287 public ClassMapping[] getMappings() {
288 return (ClassMapping[]) super.getMetaDatas();
289 }
290
291 public ClassMapping getMapping(Object oid, ClassLoader envLoader,
292 boolean mustExist) {
293 return (ClassMapping) super.getMetaData(oid, envLoader, mustExist);
294 }
295
296 public ClassMapping[] getImplementorMappings(Class cls,
297 ClassLoader envLoader, boolean mustExist) {
298 return (ClassMapping[]) super.getImplementorMetaDatas(cls, envLoader,
299 mustExist);
300 }
301
302 public synchronized void clear() {
303 super.clear();
304 _schema = null;
305 _results.clear();
306 }
307
308 protected void prepareMapping(ClassMetaData meta) {
309 // make sure superclass resolved first; resolving superclass may have
310 // resolved this mapping
311 ClassMapping mapping = (ClassMapping) meta;
312 ClassMapping sup = mapping.getPCSuperclassMapping();
313 if (sup != null && (mapping.getResolve() & MODE_MAPPING) != 0)
314 return;
315
316 // define superclass fields after mapping class, so we can tell whether
317 // the class is mapped and needs to redefine abstract superclass fields
318 getStrategyInstaller().installStrategy(mapping);
319 mapping.defineSuperclassFields(mapping.getJoinablePCSuperclassMapping()
320 == null);
321
322 // resolve everything that doesn't involve relations to allow relation
323 // mappings to use the others as joinables
324 mapping.resolveNonRelationMappings();
325 }
326
327 protected ClassMetaData newClassMetaData(Class type) {
328 return new ClassMapping(type, this);
329 }
330
331 protected ClassMetaData[] newClassMetaDataArray(int length) {
332 return new ClassMapping[length];
333 }
334
335 protected FieldMetaData newFieldMetaData(String name, Class type,
336 ClassMetaData owner) {
337 return new FieldMapping(name, type, (ClassMapping) owner);
338 }
339
340 protected FieldMetaData[] newFieldMetaDataArray(int length) {
341 return new FieldMapping[length];
342 }
343
344 protected ClassMetaData newEmbeddedClassMetaData(ValueMetaData owner) {
345 return new ClassMapping(owner);
346 }
347
348 protected ValueMetaData newValueMetaData(FieldMetaData owner) {
349 return new ValueMappingImpl((FieldMapping) owner);
350 }
351
352 protected SequenceMetaData newSequenceMetaData(String name) {
353 return new SequenceMapping(name, this);
354 }
355
356 protected Order newValueOrder(FieldMetaData owner, boolean asc) {
357 return new JDBCValueOrder((FieldMapping) owner, asc);
358 }
359
360 protected Order newRelatedFieldOrder(FieldMetaData owner,
361 FieldMetaData rel, boolean asc) {
362 return new JDBCRelatedFieldOrder((FieldMapping) owner,
363 (FieldMapping) rel, asc);
364 }
365
366 protected Order[] newOrderArray(int size) {
367 return new JDBCOrder[size];
368 }
369
370 /**
371 * Create version metadata for the given class.
372 */
373 protected Version newVersion(ClassMapping cls) {
374 return new Version(cls);
375 }
376
377 /**
378 * Create discriminator metadata for the given class.
379 */
380 protected Discriminator newDiscriminator(ClassMapping cls) {
381 return new Discriminator(cls);
382 }
383
384 /**
385 * Create raw mapping info for the given instance.
386 */
387 protected ClassMappingInfo newMappingInfo(ClassMapping cls) {
388 ClassMappingInfo info = new ClassMappingInfo();
389 info.setClassName(cls.getDescribedType().getName());
390 return info;
391 }
392
393 /**
394 * Create raw mapping info for the given instance.
395 */
396 protected FieldMappingInfo newMappingInfo(FieldMapping fm) {
397 return new FieldMappingInfo();
398 }
399
400 /**
401 * Create raw mapping info for the given instance.
402 */
403 protected ValueMappingInfo newMappingInfo(ValueMapping vm) {
404 return new ValueMappingInfo();
405 }
406
407 /**
408 * Create raw mapping info for the given instance.
409 */
410 protected VersionMappingInfo newMappingInfo(Version version) {
411 return new VersionMappingInfo();
412 }
413
414 /**
415 * Create raw mapping info for the given instance.
416 */
417 protected DiscriminatorMappingInfo newMappingInfo(Discriminator disc) {
418 return new DiscriminatorMappingInfo();
419 }
420
421 /**
422 * Instantiate the given class' named strategy, or return null if no
423 * named strategy.
424 */
425 protected ClassStrategy namedStrategy(ClassMapping cls) {
426 String name = cls.getMappingInfo().getStrategy();
427 return instantiateClassStrategy(name, cls);
428 }
429
430 /**
431 * Return the strategy for the given name.
432 */
433 protected ClassStrategy instantiateClassStrategy(String name,
434 ClassMapping cls) {
435 if (name == null)
436 return null;
437 if (NoneClassStrategy.ALIAS.equals(name))
438 return NoneClassStrategy.getInstance();
439
440 String props = Configurations.getProperties(name);
441 name = Configurations.getClassName(name);
442 Class strat = null;
443
444 // base and vertical strategies use same alias; differentiate on join
445 if (FullClassStrategy.ALIAS.equals(name))
446 strat = FullClassStrategy.class;
447 else if (FlatClassStrategy.ALIAS.equals(name))
448 strat = FlatClassStrategy.class;
449 else if (VerticalClassStrategy.ALIAS.equals(name))
450 strat = VerticalClassStrategy.class;
451 try {
452 if (strat == null)
453 strat = JavaTypes.classForName(name, cls,
454 (ClassLoader) AccessController.doPrivileged(
455 J2DoPrivHelper.getClassLoaderAction(
456 ClassStrategy.class)));
457 ClassStrategy strategy =
458 (ClassStrategy) AccessController.doPrivileged(
459 J2DoPrivHelper.newInstanceAction(strat));
460 Configurations.configureInstance(strategy, getConfiguration(),
461 props);
462 return strategy;
463 } catch (Exception e) {
464 if (e instanceof PrivilegedActionException)
465 e = ((PrivilegedActionException) e).getException();
466 throw new MetaDataException(_loc.get("bad-cls-strategy",
467 cls, name), e);
468 }
469 }
470
471 /**
472 * Instantiate the given field's named strategy, or return null if no
473 * named strategy.
474 */
475 protected FieldStrategy namedStrategy(FieldMapping field,
476 boolean installHandlers) {
477 String name = field.getMappingInfo().getStrategy();
478 if (name == null)
479 return null;
480
481 if (NoneFieldStrategy.ALIAS.equals(name))
482 return NoneFieldStrategy.getInstance();
483
484 String props = Configurations.getProperties(name);
485 name = Configurations.getClassName(name);
486 try {
487 Class c = JavaTypes.classForName(name, field,
488 (ClassLoader) AccessController.doPrivileged(
489 J2DoPrivHelper.getClassLoaderAction(FieldStrategy.class)));
490 if (FieldStrategy.class.isAssignableFrom(c)) {
491 FieldStrategy strat = (FieldStrategy)
492 AccessController.doPrivileged(
493 J2DoPrivHelper.newInstanceAction(c));
494 Configurations.configureInstance(strat, getConfiguration(),
495 props);
496 return strat;
497 }
498
499 // must be named handler
500 if (installHandlers) {
501 ValueHandler vh = (ValueHandler) AccessController.doPrivileged(
502 J2DoPrivHelper.newInstanceAction(c));
503 Configurations.configureInstance(vh, getConfiguration(),
504 props);
505 field.setHandler(vh);
506 }
507 return new HandlerFieldStrategy();
508 } catch (Exception e) {
509 if (e instanceof PrivilegedActionException)
510 e = ((PrivilegedActionException) e).getException();
511 throw new MetaDataException(_loc.get("bad-field-strategy",
512 field, name), e);
513 }
514 }
515
516 /**
517 * Instantiate the given discriminator's named strategy, or return null
518 * if no named strategy.
519 */
520 protected DiscriminatorStrategy namedStrategy(Discriminator discrim) {
521 String name = discrim.getMappingInfo().getStrategy();
522 if (name == null)
523 return null;
524
525 // if there is a named strategy present, discard it if it matches
526 // the base strategy, so that we won't create an independent instance
527 ClassMapping cls = discrim.getClassMapping();
528 while (cls.getJoinablePCSuperclassMapping() != null)
529 cls = cls.getJoinablePCSuperclassMapping();
530 Discriminator base = cls.getDiscriminator();
531 if (base != discrim && base.getStrategy() != null
532 && name.equals(base.getStrategy().getAlias()))
533 return null;
534
535 return instantiateDiscriminatorStrategy(name, discrim);
536 }
537
538 /**
539 * Instantiate the given discriminator strategy.
540 */
541 protected DiscriminatorStrategy instantiateDiscriminatorStrategy
542 (String name, Discriminator discrim) {
543 if (NoneDiscriminatorStrategy.ALIAS.equals(name))
544 return NoneDiscriminatorStrategy.getInstance();
545
546 String props = Configurations.getProperties(name);
547 name = Configurations.getClassName(name);
548 Class strat = null;
549
550 if (ClassNameDiscriminatorStrategy.ALIAS.equals(name))
551 strat = ClassNameDiscriminatorStrategy.class;
552 else if (ValueMapDiscriminatorStrategy.ALIAS.equals(name))
553 strat = ValueMapDiscriminatorStrategy.class;
554 else if (SubclassJoinDiscriminatorStrategy.ALIAS.equals(name))
555 strat = SubclassJoinDiscriminatorStrategy.class;
556
557 try {
558 if (strat == null)
559 strat = JavaTypes.classForName(name,
560 discrim.getClassMapping(),
561 (ClassLoader) AccessController.doPrivileged(
562 J2DoPrivHelper.getClassLoaderAction(
563 DiscriminatorStrategy.class)));
564 DiscriminatorStrategy strategy = (DiscriminatorStrategy)
565 AccessController.doPrivileged(
566 J2DoPrivHelper.newInstanceAction(strat));
567 Configurations.configureInstance(strategy, getConfiguration(),
568 props);
569 return strategy;
570 } catch (Exception e) {
571 if (e instanceof PrivilegedActionException)
572 e = ((PrivilegedActionException) e).getException();
573 throw new MetaDataException(_loc.get("bad-discrim-strategy",
574 discrim.getClassMapping(), name), e);
575 }
576 }
577
578 /**
579 * Instantiate the given version's named strategy, or return null
580 * if no named strategy.
581 */
582 protected VersionStrategy namedStrategy(Version version) {
583 String name = version.getMappingInfo().getStrategy();
584 if (name == null)
585 return null;
586
587 // if there is a named strategy present, discard it if it matches
588 // the base strategy, so that we won't create an independent instance
589 ClassMapping cls = version.getClassMapping();
590 while (cls.getJoinablePCSuperclassMapping() != null)
591 cls = cls.getJoinablePCSuperclassMapping();
592 Version base = cls.getVersion();
593 if (base != version && base.getStrategy() != null
594 && name.equals(base.getStrategy().getAlias()))
595 return null;
596
597 return instantiateVersionStrategy(name, version);
598 }
599
600 /**
601 * Instantiate the given version strategy.
602 */
603 protected VersionStrategy instantiateVersionStrategy(String name,
604 Version version) {
605 if (NoneVersionStrategy.ALIAS.equals(name))
606 return NoneVersionStrategy.getInstance();
607
608 String props = Configurations.getProperties(name);
609 name = Configurations.getClassName(name);
610 Class strat = null;
611
612 if (NumberVersionStrategy.ALIAS.equals(name))
613 strat = NumberVersionStrategy.class;
614 else if (TimestampVersionStrategy.ALIAS.equals(name))
615 strat = TimestampVersionStrategy.class;
616 else if (NanoPrecisionTimestampVersionStrategy.ALIAS.equals(name))
617 strat = NanoPrecisionTimestampVersionStrategy.class;
618 else if (StateComparisonVersionStrategy.ALIAS.equals(name))
619 strat = StateComparisonVersionStrategy.class;
620
621 try {
622 if (strat == null)
623 strat = JavaTypes.classForName(name,
624 version.getClassMapping(),
625 (ClassLoader) AccessController.doPrivileged(
626 J2DoPrivHelper.getClassLoaderAction(
627 VersionStrategy.class)));
628 } catch (Exception e) {
629 throw new MetaDataException(_loc.get("bad-version-strategy",
630 version.getClassMapping(), name), e);
631 }
632
633 return instantiateVersionStrategy(strat, version, props);
634 }
635
636 /**
637 * Instantiate the given version strategy.
638 */
639 protected VersionStrategy instantiateVersionStrategy(Class strat,
640 Version version, String props) {
641 try {
642 VersionStrategy strategy = (VersionStrategy)
643 AccessController.doPrivileged(
644 J2DoPrivHelper.newInstanceAction(strat));
645 Configurations.configureInstance(strategy, getConfiguration(),
646 props);
647 return strategy;
648 } catch (Exception e) {
649 if (e instanceof PrivilegedActionException)
650 e = ((PrivilegedActionException) e).getException();
651 throw new MetaDataException(_loc.get("bad-version-strategy",
652 version.getClassMapping(), strat + ""), e);
653 }
654 }
655
656 /**
657 * Determine the default strategy to use for the given class. Does
658 * not take into account the current strategy, if any.
659 */
660 protected ClassStrategy defaultStrategy(ClassMapping cls) {
661 return defaultStrategy(cls, getStrategyInstaller().isAdapting());
662 }
663
664 /**
665 * Determine the default strategy to use for the given class. Does
666 * not take into account the current strategy, if any.
667 */
668 protected ClassStrategy defaultStrategy(ClassMapping cls,
669 boolean adapting) {
670 ValueMapping embed = cls.getEmbeddingMapping();
671 if (embed != null) {
672 // superclass of embedded class isn't mapped
673 if (embed.getType() != cls.getDescribedType()
674 || embed.getFieldMapping().getStrategy()
675 == NoneFieldStrategy.getInstance())
676 return NoneClassStrategy.getInstance();
677 if (embed.getTypeCode() == JavaTypes.OID)
678 return new ObjectIdClassStrategy();
679 return new EmbeddedClassStrategy();
680 }
681 if (cls.isEmbeddedOnly())
682 return NoneClassStrategy.getInstance();
683
684 Object strat = _defaults.getStrategy(cls, adapting);
685 if (strat instanceof String)
686 return instantiateClassStrategy((String) strat, cls);
687 if (strat != null)
688 return (ClassStrategy) strat;
689
690 // see if there is a declared hierarchy strategy
691 ClassStrategy hstrat = null;
692 for (ClassMapping base = cls; base != null && hstrat == null;) {
693 hstrat = instantiateClassStrategy(base.getMappingInfo().
694 getHierarchyStrategy(), cls);
695 base = base.getMappedPCSuperclassMapping();
696 }
697
698 // the full strategy as applied to a hierarchy is a
699 // table-per-concrete-class strategy, so don't map abstract types
700 if (hstrat instanceof FullClassStrategy
701 && !cls.isManagedInterface()
702 && Modifier.isAbstract(cls.getDescribedType().getModifiers()))
703 return NoneClassStrategy.getInstance();
704
705 ClassMapping sup = cls.getMappedPCSuperclassMapping();
706 if (sup == null)
707 return new FullClassStrategy();
708 if (hstrat != null)
709 return hstrat;
710 return new FlatClassStrategy();
711 }
712
713 /**
714 * Determine the default strategy to use for the given field. Does
715 * not take into account the named or current strategy, if any. If a
716 * non-null strategy is returned, this method may as a side effect install
717 * value handlers on the field's value mappings.
718 */
719 protected FieldStrategy defaultStrategy(FieldMapping field,
720 boolean installHandlers) {
721 return defaultStrategy(field, installHandlers,
722 getStrategyInstaller().isAdapting());
723 }
724
725 /**
726 * Determine the default strategy to use for the given field. Does
727 * not take into account the named or current strategy, if any. If a
728 * non-null strategy is returned, this method may as a side effect install
729 * value handlers on the field's value mappings.
730 */
731 protected FieldStrategy defaultStrategy(FieldMapping field,
732 boolean installHandlers, boolean adapting) {
733 // not persistent?
734 if (field.getManagement() != field.MANAGE_PERSISTENT
735 || field.isVersion())
736 return NoneFieldStrategy.getInstance();
737 if (field.getDefiningMapping().getStrategy() ==
738 NoneClassStrategy.getInstance())
739 return NoneFieldStrategy.getInstance();
740
741 // check for named handler first
742 ValueHandler handler = namedHandler(field);
743 if (handler != null) {
744 if (installHandlers)
745 field.setHandler(handler);
746 return new HandlerFieldStrategy();
747 }
748
749 if (field.isSerialized()) {
750 if (_dict.maxEmbeddedBlobSize != -1)
751 return new MaxEmbeddedBlobFieldStrategy();
752 } else {
753 // check for mapped strategy
754 Object strat = mappedStrategy(field, field.getType(), adapting);
755 if (strat instanceof FieldStrategy)
756 return (FieldStrategy) strat;
757 if (strat != null) {
758 if (installHandlers)
759 field.setHandler((ValueHandler) strat);
760 return new HandlerFieldStrategy();
761 }
762 }
763
764 // check for known field strategies
765 if (!field.isSerialized() && (field.getType() == byte[].class
766 || field.getType() == Byte[].class)) {
767 if (_dict.maxEmbeddedBlobSize != -1)
768 return new MaxEmbeddedByteArrayFieldStrategy();
769 } else if (!field.isSerialized()
770 && (field.getType() == char[].class
771 || field.getType() == Character[].class)) {
772 if (_dict.maxEmbeddedClobSize != -1 && isClob(field, false))
773 return new MaxEmbeddedCharArrayFieldStrategy();
774 } else if (!field.isSerialized()) {
775 FieldStrategy strat = defaultTypeStrategy(field, installHandlers,
776 adapting);
777 if (strat != null)
778 return strat;
779 }
780
781 // check for default handler
782 handler = defaultHandler(field, adapting);
783 if (handler != null) {
784 if (installHandlers)
785 field.setHandler(handler);
786 return new HandlerFieldStrategy();
787 }
788
789 // default to blob
790 if (installHandlers) {
791 if (getLog().isWarnEnabled())
792 getLog().warn(_loc.get("no-field-strategy", field));
793 field.setSerialized(true);
794 }
795 if (_dict.maxEmbeddedBlobSize == -1) {
796 if (installHandlers)
797 field.setHandler(BlobValueHandler.getInstance());
798 return new HandlerFieldStrategy();
799 }
800 return new MaxEmbeddedBlobFieldStrategy();
801 }
802
803 /**
804 * Return the built-in strategy for the field's type, or null if none.
805 */
806 protected FieldStrategy defaultTypeStrategy(FieldMapping field,
807 boolean installHandlers, boolean adapting) {
808 switch (field.getTypeCode()) {
809 case JavaTypes.BOOLEAN:
810 case JavaTypes.BYTE:
811 case JavaTypes.CHAR:
812 case JavaTypes.DOUBLE:
813 case JavaTypes.FLOAT:
814 case JavaTypes.INT:
815 case JavaTypes.LONG:
816 case JavaTypes.SHORT:
817 return new PrimitiveFieldStrategy();
818 case JavaTypes.STRING:
819 if (!isClob(field, false))
820 return new StringFieldStrategy();
821 if (_dict.maxEmbeddedClobSize != -1)
822 return new MaxEmbeddedClobFieldStrategy();
823 break;
824 case JavaTypes.PC:
825 if (field.isEmbeddedPC())
826 return new EmbedFieldStrategy();
827 if (field.getTypeMapping().isMapped()
828 || !useUntypedPCHandler(field))
829 return new RelationFieldStrategy();
830 break;
831 case JavaTypes.ARRAY:
832 case JavaTypes.COLLECTION:
833 ValueMapping elem = field.getElementMapping();
834 ValueHandler ehandler = namedHandler(elem);
835 if (ehandler == null)
836 ehandler = defaultHandler(elem);
837 if (ehandler != null)
838 return handlerCollectionStrategy(field, ehandler,
839 installHandlers);
840 if (elem.getTypeCode() == JavaTypes.PC
841 && !elem.isSerialized() && !elem.isEmbeddedPC()) {
842 if (useInverseKeyMapping(field))
843 return new RelationCollectionInverseKeyFieldStrategy();
844 return new RelationCollectionTableFieldStrategy();
845 }
846 break;
847 case JavaTypes.MAP:
848 ValueMapping key = field.getKeyMapping();
849 ValueHandler khandler = namedHandler(key);
850 if (khandler == null)
851 khandler = defaultHandler(key);
852 ValueMapping val = field.getElementMapping();
853 ValueHandler vhandler = namedHandler(val);
854 if (vhandler == null)
855 vhandler = defaultHandler(val);
856 boolean krel = khandler == null
857 && key.getTypeCode() == JavaTypes.PC
858 && !key.isSerialized() && !key.isEmbeddedPC();
859 boolean vrel = vhandler == null
860 && val.getTypeCode() == JavaTypes.PC
861 && !val.isSerialized() && !val.isEmbeddedPC();
862 if (!krel && vrel && key.getValueMappedBy() != null) {
863 if (useInverseKeyMapping(field))
864 return new RelationMapInverseKeyFieldStrategy();
865 return new RelationMapTableFieldStrategy();
866 }
867 if (!krel && khandler == null)
868 break;
869 if (!vrel && vhandler == null)
870 break;
871 return handlerMapStrategy(field, khandler, vhandler, krel,
872 vrel, installHandlers);
873 case JavaTypes.INPUT_STREAM:
874 case JavaTypes.INPUT_READER:
875 return new LobFieldStrategy();
876 }
877 return null;
878 }
879
880 /**
881 * Return the collection strategy for the given element handler, or null
882 * if none.
883 */
884 protected FieldStrategy handlerCollectionStrategy(FieldMapping field,
885 ValueHandler ehandler, boolean installHandlers) {
886 if (getConfiguration().getCompatibilityInstance()
887 .getStoreMapCollectionInEntityAsBlob())
888 return null;
889 if (installHandlers)
890 field.getElementMapping().setHandler(ehandler);
891 return new HandlerCollectionTableFieldStrategy();
892 }
893
894 /**
895 * Return the map strategy for the given key and value handlers / relations,
896 * or null if none.
897 */
898 protected FieldStrategy handlerMapStrategy(FieldMapping field,
899 ValueHandler khandler, ValueHandler vhandler, boolean krel,
900 boolean vrel, boolean installHandlers) {
901 if (getConfiguration().getCompatibilityInstance()
902 .getStoreMapCollectionInEntityAsBlob())
903 return null;
904 if (installHandlers) {
905 field.getKeyMapping().setHandler(khandler);
906 field.getElementMapping().setHandler(vhandler);
907 }
908 if (!krel && !vrel)
909 return new HandlerHandlerMapTableFieldStrategy();
910 if (!krel && vrel)
911 return new HandlerRelationMapTableFieldStrategy();
912 if (krel && !vrel)
913 return new RelationHandlerMapTableFieldStrategy();
914 return new RelationRelationMapTableFieldStrategy();
915 }
916
917 /**
918 * Use hints in mapping data to figure out whether the given relation
919 * field should use an inverse foreign key or an association table mapping.
920 */
921 private boolean useInverseKeyMapping(FieldMapping field) {
922 FieldMapping mapped = field.getMappedByMapping();
923 if (mapped != null) {
924 if (mapped.getTypeCode() == JavaTypes.PC)
925 return true;
926 if (mapped.getElement().getTypeCode() == JavaTypes.PC)
927 return false;
928 throw new MetaDataException(_loc.get("bad-mapped-by", field,
929 mapped));
930 }
931
932 // without a mapped-by, we have to look for clues as to the mapping.
933 // we assume that anything with element foreign key columns but no join
934 // columns or table uses an inverse foreign key, and anything else uses
935 // an association table
936 FieldMappingInfo info = field.getMappingInfo();
937 ValueMapping elem = field.getElementMapping();
938 return info.getTableName() == null && info.getColumns().isEmpty()
939 && !elem.getValueInfo().getColumns().isEmpty();
940 }
941
942 /**
943 * Check the given value against mapped strategies.
944 */
945 private Object mappedStrategy(ValueMapping val, Class type,
946 boolean adapting) {
947 if (type == null || type == Object.class)
948 return null;
949
950 Object strat = _defaults.getStrategy(val, type, adapting);
951
952 // recurse on superclass so that, for example, a registered handler
953 // for java.lang.Enum will work on all enums
954 if (strat == null)
955 return mappedStrategy(val, type.getSuperclass(), adapting);
956 if (!(strat instanceof String))
957 return strat;
958
959 String name = (String) strat;
960 if (NoneFieldStrategy.ALIAS.equals(name))
961 return NoneFieldStrategy.getInstance();
962
963 String props = Configurations.getProperties(name);
964 name = Configurations.getClassName(name);
965 try {
966 Class c = JavaTypes.classForName(name, val,
967 (ClassLoader) AccessController.doPrivileged(
968 J2DoPrivHelper.getClassLoaderAction(FieldStrategy.class)));
969 Object o = AccessController.doPrivileged(
970 J2DoPrivHelper.newInstanceAction(c));
971 Configurations.configureInstance(o, getConfiguration(), props);
972 return o;
973 } catch (Exception e) {
974 if (e instanceof PrivilegedActionException)
975 e = ((PrivilegedActionException) e).getException();
976 throw new MetaDataException(_loc.get("bad-mapped-strategy",
977 val, name), e);
978 }
979 }
980
981 /**
982 * Instantiate the given value's named handler, or return null if no
983 * named handler.
984 */
985 protected ValueHandler namedHandler(ValueMapping val) {
986 String name = val.getValueInfo().getStrategy();
987 if (name == null)
988 return null;
989
990 String props = Configurations.getProperties(name);
991 name = Configurations.getClassName(name);
992 try {
993 Class c = JavaTypes.classForName(name, val,
994 (ClassLoader) AccessController.doPrivileged(
995 J2DoPrivHelper.getClassLoaderAction(ValueHandler.class)));
996 if (ValueHandler.class.isAssignableFrom(c)) {
997 ValueHandler vh = (ValueHandler) AccessController.doPrivileged(
998 J2DoPrivHelper.newInstanceAction(c));
999 Configurations.configureInstance(vh, getConfiguration(),
1000 props);
1001 return vh;
1002 }
1003 return null; // named field strategy
1004 } catch (Exception e) {
1005 if (e instanceof PrivilegedActionException)
1006 e = ((PrivilegedActionException) e).getException();
1007 throw new MetaDataException(_loc.get("bad-value-handler",
1008 val, name), e);
1009 }
1010 }
1011
1012 /**
1013 * Determine the default handler to use for the given value. Does
1014 * not take into account the named handler, if any.
1015 */
1016 protected ValueHandler defaultHandler(ValueMapping val) {
1017 return defaultHandler(val, getStrategyInstaller().isAdapting());
1018 }
1019
1020 /**
1021 * Determine the default handler to use for the given value. Does
1022 * not take into account the named handler, if any.
1023 */
1024 protected ValueHandler defaultHandler(ValueMapping val, boolean adapting) {
1025 if (val.isSerialized()) {
1026 if (_dict.maxEmbeddedBlobSize != -1)
1027 warnMaxEmbedded(val, _dict.maxEmbeddedBlobSize);
1028 return BlobValueHandler.getInstance();
1029 }
1030
1031 Object handler = mappedStrategy(val, val.getType(), adapting);
1032 if (handler instanceof ValueHandler)
1033 return (ValueHandler) handler;
1034
1035 if (val.getType() == byte[].class
1036 || val.getType() == Byte[].class) {
1037 if (_dict.maxEmbeddedBlobSize != -1)
1038 warnMaxEmbedded(val, _dict.maxEmbeddedBlobSize);
1039 return ByteArrayValueHandler.getInstance();
1040 }
1041 if (val.getType() == char[].class
1042 || val.getType() == Character[].class) {
1043 if (isClob(val, true))
1044 return CharArrayStreamValueHandler.getInstance();
1045 return CharArrayValueHandler.getInstance();
1046 }
1047
1048 switch (val.getTypeCode()) {
1049 case JavaTypes.BOOLEAN:
1050 case JavaTypes.BYTE:
1051 case JavaTypes.CHAR:
1052 case JavaTypes.DOUBLE:
1053 case JavaTypes.FLOAT:
1054 case JavaTypes.INT:
1055 case JavaTypes.LONG:
1056 case JavaTypes.SHORT:
1057 case JavaTypes.BOOLEAN_OBJ:
1058 case JavaTypes.BYTE_OBJ:
1059 case JavaTypes.CHAR_OBJ:
1060 case JavaTypes.DOUBLE_OBJ:
1061 case JavaTypes.FLOAT_OBJ:
1062 case JavaTypes.INT_OBJ:
1063 case JavaTypes.LONG_OBJ:
1064 case JavaTypes.SHORT_OBJ:
1065 case JavaTypes.BIGINTEGER:
1066 case JavaTypes.BIGDECIMAL:
1067 case JavaTypes.NUMBER:
1068 case JavaTypes.DATE:
1069 case JavaTypes.CALENDAR:
1070 case JavaTypes.LOCALE:
1071 return ImmutableValueHandler.getInstance();
1072 case JavaTypes.STRING:
1073 if (isClob(val, true))
1074 return ClobValueHandler.getInstance();
1075 return ImmutableValueHandler.getInstance();
1076 case JavaTypes.PC:
1077 if (!val.getTypeMapping().isMapped()
1078 && useUntypedPCHandler(val))
1079 return UntypedPCValueHandler.getInstance();
1080 break;
1081 case JavaTypes.PC_UNTYPED:
1082 return UntypedPCValueHandler.getInstance();
1083 case JavaTypes.OID:
1084 return new ObjectIdValueHandler();
1085 }
1086 if (!getConfiguration().getCompatibilityInstance()
1087 .getStoreMapCollectionInEntityAsBlob()
1088 && val.isEmbeddedPC())
1089 return new ElementEmbedValueHandler();
1090 return null;
1091 }
1092
1093 /**
1094 * Return true if we should use the generic untyped PC handler for the
1095 * given unmapped relation.
1096 */
1097 private boolean useUntypedPCHandler(ValueMapping val) {
1098 ClassMapping rel = val.getTypeMapping();
1099 return rel.getIdentityType() == ClassMapping.ID_UNKNOWN
1100 || (rel.getIdentityType() == ClassMapping.ID_APPLICATION
1101 && (rel.getPrimaryKeyFields().length == 0
1102 || (!rel.isOpenJPAIdentity() && Modifier.isAbstract
1103 (rel.getObjectIdType().getModifiers()))));
1104 }
1105
1106 /**
1107 * Checks for hints as to whether the given column is a CLOB.
1108 */
1109 private boolean isClob(ValueMapping val, boolean warn) {
1110 List cols = val.getValueInfo().getColumns();
1111 if (cols.size() != 1)
1112 return false;
1113
1114 Column col = (Column) cols.get(0);
1115 if (col.getSize() != -1 && col.getType() != Types.CLOB)
1116 return false;
1117
1118 if (_dict.getPreferredType(Types.CLOB) != Types.CLOB)
1119 return false;
1120
1121 if (warn && _dict.maxEmbeddedClobSize != -1)
1122 warnMaxEmbedded(val, _dict.maxEmbeddedClobSize);
1123 return true;
1124 }
1125
1126 /**
1127 * Warn that the given value is being mapped to a handler that will not
1128 * be able to store large lobs.
1129 */
1130 private void warnMaxEmbedded(ValueMapping val, int size) {
1131 if (getLog().isWarnEnabled())
1132 getLog().warn(_loc.get("max-embed-lob", val,
1133 String.valueOf(size)));
1134 }
1135
1136 /**
1137 * Determine the default strategy to use for the given discriminator.
1138 * Does not take into account the current strategy, if any.
1139 */
1140 protected DiscriminatorStrategy defaultStrategy(Discriminator discrim) {
1141 return defaultStrategy(discrim, getStrategyInstaller().isAdapting());
1142 }
1143
1144 /**
1145 * Determine the default strategy to use for the given discriminator.
1146 * Does not take into account the current strategy, if any.
1147 */
1148 protected DiscriminatorStrategy defaultStrategy(Discriminator discrim,
1149 boolean adapting) {
1150 ClassMapping cls = discrim.getClassMapping();
1151 if (cls.getEmbeddingMetaData() != null)
1152 return NoneDiscriminatorStrategy.getInstance();
1153 if (cls.getJoinablePCSuperclassMapping() == null
1154 && (cls.getStrategy() == NoneClassStrategy.getInstance()
1155 || Modifier.isFinal(discrim.getClassMapping().getDescribedType().
1156 getModifiers())))
1157 return NoneDiscriminatorStrategy.getInstance();
1158
1159 Object strat = _defaults.getStrategy(discrim, adapting);
1160 if (strat instanceof String)
1161 return instantiateDiscriminatorStrategy((String) strat, discrim);
1162 if (strat != null)
1163 return (DiscriminatorStrategy) strat;
1164
1165 if (cls.getJoinablePCSuperclassMapping() != null)
1166 return new SuperclassDiscriminatorStrategy();
1167 if (discrim.getMappingInfo().getValue() != null)
1168 return new ValueMapDiscriminatorStrategy();
1169 if (cls.getMappedPCSuperclassMapping() != null)
1170 return NoneDiscriminatorStrategy.getInstance();
1171 if (adapting || _defaults.defaultMissingInfo())
1172 return new ClassNameDiscriminatorStrategy();
1173 DBDictionary dict = ((JDBCConfiguration) getConfiguration()).
1174 getDBDictionaryInstance();
1175 if (dict.joinSyntax == JoinSyntaxes.SYNTAX_TRADITIONAL)
1176 return NoneDiscriminatorStrategy.getInstance();
1177 return new SubclassJoinDiscriminatorStrategy();
1178 }
1179
1180 /**
1181 * Determine the default strategy to use for the given version.
1182 * Does not take into account the current strategy, if any.
1183 */
1184 protected VersionStrategy defaultStrategy(Version version) {
1185 return defaultStrategy(version, getStrategyInstaller().isAdapting());
1186 }
1187
1188 /**
1189 * Determine the default strategy to use for the given version.
1190 * Does not take into account the current strategy, if any.
1191 */
1192 protected VersionStrategy defaultStrategy(Version version,
1193 boolean adapting) {
1194 ClassMapping cls = version.getClassMapping();
1195 if (cls.getEmbeddingMetaData() != null)
1196 return NoneVersionStrategy.getInstance();
1197 if (cls.getJoinablePCSuperclassMapping() == null
1198 && cls.getStrategy() == NoneClassStrategy.getInstance())
1199 return NoneVersionStrategy.getInstance();
1200
1201 Object strat = _defaults.getStrategy(version, adapting);
1202 if (strat instanceof String)
1203 return instantiateVersionStrategy((String) strat, version);
1204 if (strat != null)
1205 return (VersionStrategy) strat;
1206
1207 if (cls.getJoinablePCSuperclassMapping() != null)
1208 return new SuperclassVersionStrategy();
1209
1210 FieldMapping vfield = version.getClassMapping().
1211 getVersionFieldMapping();
1212 if (vfield != null)
1213 return defaultStrategy(version, vfield);
1214 if (adapting || _defaults.defaultMissingInfo())
1215 return new NumberVersionStrategy();
1216 return NoneVersionStrategy.getInstance();
1217 }
1218
1219 /**
1220 * Return the default version strategy, given a version field.
1221 */
1222 protected VersionStrategy defaultStrategy(Version vers,
1223 FieldMapping vfield) {
1224 switch (vfield.getTypeCode()) {
1225 case JavaTypes.DATE:
1226 case JavaTypes.CALENDAR:
1227 return (JavaVersions.VERSION >= 5)
1228 ? new NanoPrecisionTimestampVersionStrategy()
1229 : new TimestampVersionStrategy();
1230 case JavaTypes.BYTE:
1231 case JavaTypes.INT:
1232 case JavaTypes.LONG:
1233 case JavaTypes.SHORT:
1234 case JavaTypes.BYTE_OBJ:
1235 case JavaTypes.INT_OBJ:
1236 case JavaTypes.LONG_OBJ:
1237 case JavaTypes.SHORT_OBJ:
1238 case JavaTypes.NUMBER:
1239 return new NumberVersionStrategy();
1240 default:
1241 return NoneVersionStrategy.getInstance();
1242 }
1243 }
1244
1245 public void endConfiguration()
1246 {
1247 super.endConfiguration();
1248
1249 JDBCConfiguration conf = (JDBCConfiguration) getConfiguration();
1250 _dict = conf.getDBDictionaryInstance();
1251 if (_defaults == null)
1252 _defaults = conf.getMappingDefaultsInstance();
1253 if (_schema != null && _schema instanceof Configurable) {
1254 ((Configurable) _schema).setConfiguration(conf);
1255 ((Configurable) _schema).startConfiguration();
1256 ((Configurable) _schema).endConfiguration();
1257 }
1258 }
1259 }