| /* |
| * Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| // 05/16/2008-1.0M8 Guy Pelletier |
| // - 218084: Implement metadata merging functionality between mapping files |
| // 05/23/2008-1.0M8 Guy Pelletier |
| // - 211330: Add attributes-complete support to the EclipseLink-ORM.XML Schema |
| // 07/15/2008-1.0.1 Guy Pelletier |
| // - 240679: MappedSuperclass Id not picked when on get() method accessor |
| // 09/23/2008-1.1 Guy Pelletier |
| // - 241651: JPA 2.0 Access Type support |
| // 10/01/2008-1.1 Guy Pelletier |
| // - 249329: To remain JPA 1.0 compliant, any new JPA 2.0 annotations should be referenced by name |
| // 01/28/2009-2.0 Guy Pelletier |
| // - 248293: JPA 2.0 Element Collections (part 1) |
| // 02/06/2009-2.0 Guy Pelletier |
| // - 248293: JPA 2.0 Element Collections (part 2) |
| // 02/26/2009-2.0 Guy Pelletier |
| // - 264001: dot notation for mapped-by and order-by |
| // 03/27/2009-2.0 Guy Pelletier |
| // - 241413: JPA 2.0 Add EclipseLink support for Map type attributes |
| // 04/03/2009-2.0 Guy Pelletier |
| // - 241413: JPA 2.0 Add EclipseLink support for Map type attributes |
| // 04/24/2009-2.0 Guy Pelletier |
| // - 270011: JPA 2.0 MappedById support |
| // 06/25/2009-2.0 Michael O'Brien |
| // - 266912: change MappedSuperclass handling in stage2 to pre process accessors |
| // in support of the custom descriptors holding mappings required by the Metamodel |
| // 04/09/2010-2.1 Guy Pelletier |
| // - 307050: Add defaults for access methods of a VIRTUAL access type |
| // 05/04/2010-2.1 Guy Pelletier |
| // - 309373: Add parent class attribute to EclipseLink-ORM |
| // 05/14/2010-2.1 Guy Pelletier |
| // - 253083: Add support for dynamic persistence using ORM.xml/eclipselink-orm.xml |
| // 06/01/2010-2.1 Guy Pelletier |
| // - 315195: Add new property to avoid reading XML during the canonical model generation |
| // 06/14/2010-2.2 Guy Pelletier |
| // - 264417: Table generation is incorrect for JoinTables in AssociationOverrides |
| // 07/05/2010-2.1.1 Guy Pelletier |
| // - 317708: Exception thrown when using LAZY fetch on VIRTUAL mapping |
| // 09/16/2010-2.2 Guy Pelletier |
| // - 283028: Add support for letting an @Embeddable extend a @MappedSuperclass |
| // 12/01/2010-2.2 Guy Pelletier |
| // - 331234: xml-mapping-metadata-complete overriden by metadata-complete specification |
| // 12/02/2010-2.2 Guy Pelletier |
| // - 251554: ExcludeDefaultMapping annotation needed |
| // 03/24/2011-2.3 Guy Pelletier |
| // - 337323: Multi-tenant with shared schema support (part 1) |
| // 09/11/2017-2.1 Will Dazey |
| // - 520387: multiple owning descriptors for an embeddable are not set |
| package org.eclipse.persistence.internal.jpa.metadata.accessors.classes; |
| |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.persistence.annotations.Cache; |
| import org.eclipse.persistence.exceptions.ValidationException; |
| import org.eclipse.persistence.internal.jpa.metadata.MetadataDescriptor; |
| import org.eclipse.persistence.internal.jpa.metadata.MetadataLogger; |
| import org.eclipse.persistence.internal.jpa.metadata.MetadataProject; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAnnotation; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataClass; |
| |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ACCESS_FIELD; |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ACCESS_PROPERTY; |
| |
| /** |
| * INTERNAL: |
| * An embeddable accessor. |
| * |
| * Key notes: |
| * - any metadata mapped from XML to this class must be compared in the |
| * equals method. |
| * - any metadata mapped from XML to this class must be handled in the merge |
| * method. (merging is done at the accessor/mapping level) |
| * - any metadata mapped from XML to this class must be initialized in the |
| * initXMLObject method. |
| * - methods should be preserved in alphabetical order. |
| * |
| * @author Guy Pelletier |
| * @since EclipseLink 1.0 |
| */ |
| public class EmbeddableAccessor extends ClassAccessor { |
| // Embedding accessors is a map of those classes that embed this embeddable. |
| // All embedding accessors are owning descriptors, but not vice versa. |
| private Map<String, ClassAccessor> m_embeddingAccessors = new HashMap<String, ClassAccessor>(); |
| |
| /** |
| * INTERNAL: |
| */ |
| public EmbeddableAccessor() { |
| super("<embeddable>"); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public EmbeddableAccessor(MetadataAnnotation annotation, MetadataClass cls, MetadataProject project) { |
| super(annotation, cls, project); |
| } |
| |
| /** |
| * INTERNAL: |
| * Embedding accessors are those accessors that actually embed the |
| * embeddable class with an embedded mapping. We use this list to extract |
| * and validate the access type of this embeddable when the embeddable |
| * does not specify an explicit access type. |
| */ |
| protected void addEmbeddingAccessor(ClassAccessor embeddingAccessor) { |
| m_embeddingAccessors.put(embeddingAccessor.getJavaClassName(), embeddingAccessor); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public void addEmbeddingAccessors(Map<String, ClassAccessor> embeddingAccessors) { |
| m_embeddingAccessors.putAll(embeddingAccessors); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public void addOwningDescriptor(MetadataDescriptor owningDescriptor) { |
| getOwningDescriptors().add(owningDescriptor); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public void addOwningDescriptors(List<MetadataDescriptor> owningDescriptors) { |
| getOwningDescriptors().addAll(owningDescriptors); |
| } |
| |
| /** |
| * INTERNAL |
| * Ensure any embeddable classes that are discovered during pre-process |
| * are added to the project. The newly discovered embeddable accesors will |
| * also be pre-processed now as well. |
| */ |
| @Override |
| protected void addPotentialEmbeddableAccessor(MetadataClass potentialEmbeddableClass, ClassAccessor embeddingAccessor) { |
| if (potentialEmbeddableClass != null) { |
| // Get embeddable accessor will add the embeddable to the project |
| // if it is a valid embeddable. That is, if the class has an |
| // Embeddable annotation of the class is used as an IdClass for |
| // another entity within the persistence unit. |
| EmbeddableAccessor embeddableAccessor = getProject().getEmbeddableAccessor(potentialEmbeddableClass, true); |
| |
| if (embeddableAccessor != null) { |
| embeddableAccessor.addEmbeddingAccessor(embeddingAccessor); |
| embeddableAccessor.addOwningDescriptors(getOwningDescriptors()); |
| if(!embeddableAccessor.isPreProcessed()) { |
| embeddableAccessor.preProcess(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Build a list of classes that are decorated with a MappedSuperclass |
| * annotation or that are tagged as a mapped-superclass in an XML document. |
| * |
| * This method will also do a couple other things as well since we are |
| * traversing the parent classes: |
| * - Build a map of generic types specified and will be used to resolve |
| * actual class types for mappings. |
| * - save mapped-superclass descriptors on the project for later use |
| * by the Metamodel API |
| * |
| * We don't support embeddable inheritance yet. When that is added, this |
| * method will need to change and in fact we may be able to re-use the |
| * existing discover method from EntityAccessor (with minor tweaks). |
| */ |
| protected void discoverMappedSuperclassesAndInheritanceParents(boolean addMappedSuperclassAccessors) { |
| // Clear any previous discovery. |
| clearMappedSuperclassesAndInheritanceParents(); |
| |
| MetadataClass parentClass = getJavaClass().getSuperclass(); |
| List<String> genericTypes = getJavaClass().getGenericType(); |
| |
| while (parentClass != null && ! parentClass.isObject()) { |
| // Our parent might be a mapped superclass, check and add as needed. |
| addPotentialMappedSuperclass(parentClass, addMappedSuperclassAccessors); |
| |
| // Resolve any generic types from the generic parent onto the |
| // current entity accessor. |
| resolveGenericTypes(genericTypes, parentClass); |
| |
| // Grab the generic types from the parent class. |
| genericTypes = parentClass.getGenericType(); |
| |
| // Finally, get the next parent and keep processing ... |
| parentClass = parentClass.getSuperclass(); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public Map<String, ClassAccessor> getEmbeddingAccessors() { |
| return m_embeddingAccessors; |
| } |
| |
| /** |
| * INTERNAL: |
| * So, here's the deal ... this method typically gets called when defaulting |
| * pk's name, primary table names etc. for various mappings. The problem |
| * however is that we go beyond the spec and allow more mappings to be |
| * specified on embeddable classes. For example, a M-M would default its |
| * join table and join columns using the info from its owning descriptor. |
| * The problem then is ... what to do when this embedabble has multiple |
| * owning descriptors? That is, is shared. Right now, their pk names from |
| * the owners better be same or mappings must be fully specified and not use |
| * any defaults. I think that is somewhat ok given we're going beyond the |
| * spec and TopLink doesn't even support it anyway??? |
| * |
| * So the stance is, we'll allow the extra mappings on embeddables that are |
| * not shared, however on shared cases there are restrictions. Users should |
| * use mapped superclasses when they have a need to share complex |
| * embeddables. |
| * |
| * Or they can write customizers to modify their embeddable descriptors |
| * after initialize (after they have been cloned) |
| * |
| * Future: the metadata processing 'could' set all necessary (per owning |
| * descriptor) metadata and have the descriptor initialize code handle it. |
| * Metadata processing would process embeddable classes as it currently does |
| * for MappedSuperclasses. Clone them and process under each owning entity |
| * context. At descriptor initialize time, we would avoid cloning the |
| * aggregate descriptor and use the one metadata processing provided. |
| * Investigate further at a later date ... |
| * |
| * Callers to this method are ... |
| * BasicCollectionAccessor - processCollectionTable - defaults pk names from the owning descriptor. |
| * RelationshipAccessor - processJoinTable - defaults the join table name and the source field name |
| * OneToManyAccessor - processUnidirectionalOneToManyMapping - defaults the pk field and table. |
| * MappingAccessor - processAssociationOverride and updatePrimaryKeyField. |
| * ObjectAccessor - processId |
| */ |
| @Override |
| public MetadataDescriptor getOwningDescriptor() { |
| if (getOwningDescriptors() != null && getOwningDescriptors().size() > 0) { |
| // Return the first owning descriptor. In most cases this will be OK |
| // since in most cases there is only one. |
| return getOwningDescriptors().get(0); |
| } |
| return this.getDescriptor(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return true if this accessor represents an embeddable accessor. |
| */ |
| @Override |
| public boolean isEmbeddableAccessor() { |
| return true; |
| } |
| |
| /** |
| * INTERNAL: |
| * The pre-process method is called during regular deployment and metadata |
| * processing. |
| * |
| * This method is called after each entity of the persistence unit has had |
| * an opportunity to pre-process itself first since we'll rely on owning |
| * entities for things like access type etc. The pre-process will run some |
| * validation. |
| * |
| * The order of processing is important, care must be taken if changes must |
| * be made. |
| */ |
| @Override |
| public void preProcess() { |
| // Perform the parent discovery process before processing any further. |
| discoverMappedSuperclassesAndInheritanceParents(true); |
| |
| // Process the correct access type before any other processing. |
| processAccessType(); |
| |
| // Process a virtual class specification after determining access type. |
| processVirtualClass(); |
| |
| // Process the default access methods after determining access type. |
| processAccessMethods(); |
| |
| // Process a @Struct and @EIS annotation to create the correct type of descriptor. |
| processStruct(); |
| processNoSql(); |
| |
| // Process our parents metadata after processing our own. |
| super.preProcess(); |
| } |
| |
| /** |
| * INTERNAL: |
| * The pre-process for canonical model method is called (and only called) |
| * during the canonical model generation. The use of this pre-process allows |
| * us to remove some items from the regular pre-process that do not apply |
| * to the canonical model generation. |
| * |
| * The order of processing is important, care must be taken if changes must |
| * be made. |
| */ |
| @Override |
| public void preProcessForCanonicalModel() { |
| // Perform the parent discovery process before processing any further. |
| discoverMappedSuperclassesAndInheritanceParents(false); |
| |
| // Process our parents metadata after processing our own. |
| super.preProcessForCanonicalModel(); |
| } |
| |
| /** |
| * INTERNAL |
| * Sub classes (Entity and Embeddable) must override this method to control |
| * the metadata that is processed for their context. |
| */ |
| @Override |
| protected void preProcessMappedSuperclassMetadata(MappedSuperclassAccessor mappedSuperclass) { |
| // Process the global converters. |
| mappedSuperclass.processConverters(); |
| |
| // Add the accessors and converters from this mapped superclass. |
| mappedSuperclass.addAccessors(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Process the metadata from this embeddable class. |
| */ |
| @Override |
| public void process() { |
| // If a Cache annotation is present throw an exception. |
| if (isAnnotationPresent(Cache.class)) { |
| throw ValidationException.cacheNotSupportedWithEmbeddable(getJavaClass()); |
| } |
| |
| // Process our parents metadata after processing our own. |
| super.process(); |
| |
| // Process the mapping accessors on this embeddable now. |
| processMappingAccessors(); |
| } |
| |
| /** |
| * INTERNAL: |
| * For VIRTUAL access we need to look for default access methods that we |
| * need to use with our mapping attributes. |
| */ |
| public void processAccessMethods() { |
| // If we use virtual access and do not have any access methods |
| // specified then get the default access methods from our owning |
| // entity. |
| if (hasAccessMethods()) { |
| getDescriptor().setDefaultAccessMethods(getAccessMethods()); |
| } else { |
| // The embeddable does not define default access methods. We |
| // need to look at our owning entities and 1) validate that |
| // they all use the same default access methods and 2) grab |
| // the default access methods from them. |
| ClassAccessor embeddingAccessor = null; |
| for (ClassAccessor currentEmbeddingAccessor : m_embeddingAccessors.values()) { |
| if (embeddingAccessor == null) { |
| embeddingAccessor = currentEmbeddingAccessor; |
| continue; |
| } |
| |
| if (! embeddingAccessor.getDescriptor().getDefaultAccessMethods().equals(currentEmbeddingAccessor.getDescriptor().getDefaultAccessMethods())) { |
| throw ValidationException.conflictingAccessMethodsForEmbeddable(getJavaClassName(), embeddingAccessor.getJavaClassName(), embeddingAccessor.getDescriptor().getDefaultAccessMethods(), currentEmbeddingAccessor.getJavaClassName(), currentEmbeddingAccessor.getDescriptor().getDefaultAccessMethods()); |
| } |
| } |
| |
| // Set the default access methods on the descriptor and log a |
| // message if there is an embedding accessor. Otherwise we'll |
| // use the default set on our descriptor. |
| if (embeddingAccessor != null) { |
| getDescriptor().setDefaultAccessMethods(embeddingAccessor.getDescriptor().getDefaultAccessMethods()); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Process the access type of this embeddable. If this embeddable is not |
| * embedded by at least one entity, it will not be processed. Therefore, |
| * embedding accessors can not be empty at this point. |
| */ |
| @Override |
| protected void processAccessType() { |
| // Validate that this embeddable is not used within entities with |
| // conflicting access types when the embeddable doesn't have its own |
| // explicit setting. When the embeddable is shared, the access type |
| // must be the same across the board. |
| if (! hasAccess()) { |
| ClassAccessor embeddingAccessor = null; |
| |
| // The access type of the embeddable is determined from the class |
| // that is embedding it. If there are multiple embedding |
| // accessors we will validate that there are no conflicting types. |
| for (ClassAccessor currentEmbeddingAccessor : m_embeddingAccessors.values()) { |
| if (embeddingAccessor == null) { |
| embeddingAccessor = currentEmbeddingAccessor; |
| continue; |
| } |
| |
| if (! embeddingAccessor.getAccessType().equals(currentEmbeddingAccessor.getAccessType())) { |
| throw ValidationException.conflictingAccessTypeForEmbeddable(getJavaClassName(), embeddingAccessor.getJavaClassName(), embeddingAccessor.getAccessType(), currentEmbeddingAccessor.getJavaClassName(), currentEmbeddingAccessor.getAccessType()); |
| } |
| } |
| |
| // Set the default access type on the descriptor and log a message |
| // that we are defaulting the access type for this embeddable. |
| if (embeddingAccessor == null) { |
| // We don't have an owning entity (only possible during |
| // canonical model generation) so look at the mapped |
| // superclasses. Ultimate default will be FIELD. |
| String defaultAccessType = JPA_ACCESS_FIELD; |
| for (MappedSuperclassAccessor mappedSuperclass : getMappedSuperclasses()) { |
| if (! mappedSuperclass.hasAccess()) { |
| if (mappedSuperclass.hasObjectRelationalFieldMappingAnnotationsDefined()) { |
| defaultAccessType = JPA_ACCESS_FIELD; |
| } else if (mappedSuperclass.hasObjectRelationalMethodMappingAnnotationsDefined()) { |
| defaultAccessType = JPA_ACCESS_PROPERTY; |
| } |
| |
| break; |
| } |
| } |
| |
| getDescriptor().setDefaultAccess(defaultAccessType); |
| } else { |
| // Use the access type from the embedding accessor. |
| getDescriptor().setDefaultAccess(embeddingAccessor.getAccessType()); |
| } |
| |
| getLogger().logConfigMessage(MetadataLogger.ACCESS_TYPE, getDescriptor().getDefaultAccess(), getJavaClass()); |
| |
| getDescriptor().setAccessTypeOnClassDescriptor(getAccessType()); |
| } |
| } |
| |
| /** |
| * INTERNAL |
| * From an embeddable we need pair down what we process as things like |
| * ID metadata does not apply. |
| */ |
| @Override |
| protected void processMappedSuperclassMetadata(MappedSuperclassAccessor mappedSuperclass) { |
| // Process the attribute override metadata. |
| mappedSuperclass.processAttributeOverrides(); |
| |
| // Process the association override metadata. |
| mappedSuperclass.processAssociationOverrides(); |
| |
| // Process the change tracking metadata. |
| mappedSuperclass.processChangeTracking(); |
| |
| // Process the customizer metadata. |
| mappedSuperclass.processCustomizer(); |
| |
| // Process the copy policy metadata. |
| mappedSuperclass.processCopyPolicy(); |
| |
| // Process the property metadata. |
| mappedSuperclass.processProperties(); |
| } |
| } |