| /* |
| * Copyright (c) 2011, 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: |
| // 03/19/2009-2.0 dclarke - initial API start |
| // 06/30/2009-2.0 mobrien - finish JPA Metadata API modifications in support |
| // of the Metamodel implementation for EclipseLink 2.0 release involving |
| // Map, ElementCollection and Embeddable types on MappedSuperclass descriptors |
| // - 266912: JPA 2.0 Metamodel API (part of the JSR-317 EJB 3.1 Criteria API) |
| // 11/05/2009-2.0 mobrien - DI 86: MapKey support when only generics |
| // are used to determine the keyType for an IdClass that used an embeddable |
| // 11/10/2009-2.0 mobrien - DI 98: Use keyMapping on MappedKeyMappedContainerPolicy |
| // keep workaround for bug# 294765 for Basic keyType when MapKey annotation not specified. |
| // keep workaround for bug# 294811 for Entity, Embeddable, Transient keyType support |
| // when MapKey name attribute not specified (MapContainerPolicy) |
| // add BasicMap support via DirectMapContainerPolicy |
| // 13/10/2009-2.0 mobrien - 294765 - fix allows removal of workaround for |
| // when MapKey annotation not specified |
| // 06/14/2010-2.1 mobrien - 314906: getJavaType should return the |
| // collection javaType C in <X,C,V) of <X, Map<K,V>, V> instead off the elementType V |
| package org.eclipse.persistence.internal.jpa.metamodel; |
| |
| import java.util.Map; |
| |
| import jakarta.persistence.metamodel.MapAttribute; |
| import jakarta.persistence.metamodel.Type; |
| |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.internal.queries.ContainerPolicy; |
| import org.eclipse.persistence.internal.queries.MappedKeyMapContainerPolicy; |
| import org.eclipse.persistence.mappings.CollectionMapping; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.mappings.foundation.MapKeyMapping; |
| |
| /** |
| * <p> |
| * <b>Purpose</b>: Provides the implementation for the MapAttribute interface |
| * of the JPA 2.0 Metamodel API (part of the JSR-317 EJB 3.1 Criteria API) |
| * <p> |
| * <b>Description</b>: |
| * Instances of the type MapAttribute represent persistent Map-valued |
| * attributes. |
| * |
| * @see jakarta.persistence.metamodel.MapAttribute |
| * |
| * @since EclipseLink 1.2 - JPA 2.0 |
| * |
| * @param <X> The type the represented Map belongs to |
| * @param <K> The type of the key of the represented Map |
| * @param <V> The type of the value of the represented Map |
| |
| */ |
| public class MapAttributeImpl<X, K, V> extends PluralAttributeImpl<X, Map<K, V>, V> implements MapAttribute<X, K, V> { |
| |
| /** Item 54: DI 89: explicit UID will avoid performance hit runtime generation of one */ |
| private static final long serialVersionUID = 5702748112869113135L; |
| |
| /** The key type that this Map type is based on **/ |
| private Type<K> keyType; |
| |
| /** |
| * INTERNAL: |
| * @param managedType |
| * @param mapping |
| */ |
| protected MapAttributeImpl(ManagedTypeImpl<X> managedType, CollectionMapping mapping) { |
| this(managedType, mapping, false); |
| } |
| |
| /** |
| * INTERNAL: |
| * Create a new MapAttribute instance. |
| * The elementType field is instantiated in the superclass. |
| * The keyType field is instantiated in this constructor by using one of the following |
| * A) MapContainerPolicy by consulting the keyField or PK class |
| * B) MappedKeyMapContainerPolicy by using the mapKeyTargetType on the keyMapping or the attributeClassification on the attributeAccessor |
| * @param managedType - the owning type (EmbeddableTypes do not support mappings) |
| * @param mapping - contains the mapping policy |
| * @param validationEnabled - report errors in the metamodel |
| */ |
| protected MapAttributeImpl(ManagedTypeImpl<X> managedType, CollectionMapping mapping, boolean validationEnabled) { |
| // Set the managedType (X or owning Type) - Note: EmbeddableTypes are only supported as Map keys here |
| super(managedType, mapping, validationEnabled); |
| // We need to set the keyType Type that represents the type of the Map key for this mapping |
| ContainerPolicy policy = mapping.getContainerPolicy(); |
| Class<?> javaClass = null; |
| MapKeyMapping keyMapping = null; |
| Object policyKeyType = null; |
| |
| /** |
| * Note: the (at) sign for annotations has been replaced by the & sign for javadoc processing. |
| * |
| * We have the following policy structure and behavior |
| * ContainerPolicy (A) |
| * +=== InterfaceContainerPolicy (A) |
| * +=== DirectMapContainerPolicy |
| * +=== MapContainerPolicy (use keyField or PK class) |
| * +=== MappedKeyMapContainerPolicy (use keyMapping.mapKeyTargetType or attributeClassification) |
| * |
| * Use Case segmentation for keyType |
| A) MapContainerPolicy |
| A1) keyField set (lazy loaded) |
| UC2 - name attribute defines mapKey, generics are not required and are secondary |
| &OneToMany(cascade=ALL, mappedBy="mappedEmployerUC2") |
| &MapKey(name="name") |
| private Map<String, HardwareDesigner> hardwareDesignersMapUC2; |
| UC4 - name attribute defines mapKey, generics are not required |
| &OneToMany(targetEntity=HardwareDesigner.class, cascade=ALL, mappedBy="mappedEmployerUC4") |
| &MapKey(name="name") |
| private Map hardwareDesignersMapUC4; |
| UC8 - mapKey defined via generics |
| &OneToMany(cascade=ALL, mappedBy="mappedEmployerUC8") |
| &MapKey // name attribute will default to "id" |
| private Map<Integer, HardwareDesigner> hardwareDesignersMapUC8; |
| A2) Use mapPolicy.elementDescriptor.cmppolicy.pkClass (since KeyField == null) |
| UC10 - mapKey defined via generics and is a java class defined as an IdClass on the element(value) class - here Enclosure |
| &OneToMany(mappedBy="computer", cascade=ALL, fetch=EAGER) |
| &MapKey // key defaults to an instance of the composite pk class |
| private Map<EnclosureIdClassPK, Enclosure> enclosures; |
| &Entity &IdClass(EnclosureIdClassPK.class) public class Enclosure {} |
| UC11 - or (get keyClass from mapping if the Id is a get() function) |
| TBD - use reflection |
| B) MappedKeyMapContainerPolicy |
| B1) mapKeyTargetType set on the keyMapping - normal processing |
| UC9 - mapKey defined by generics in the absence of a MapKey annotation |
| &OneToMany(cascade=CascadeType.ALL, mappedBy="mappedManufacturerUC9") |
| private Map<Board, Enclosure> enclosureByBoardMapUC9; |
| UC13 - mapKey defined by generics in the absence of a MapKey name attribute (unidirectional M:1 becomes M:M) |
| &MapKey // on Computer inverse |
| private Map<EmbeddedPK, GalacticPosition> position; |
| &ManyToOne(fetch=EAGER) // on GalacticPosition owner |
| private Computer computer; |
| B2) - secondary processing for Basic (DirectToField) mappings |
| Use AttributeClassification (since keyMapping.attributeAccessor.attributeClass == null) |
| UC1a - mapKey defined by generics in the absence of a MapKey annotation |
| &OneToMany(cascade=ALL, mappedBy="mappedEmployerUC1a") |
| private Map<String, HardwareDesigner> hardwareDesignersMapUC1a; |
| UC7 - mapKey defined by generics in the absence of a MapKey annotation |
| &OneToMany(targetEntity=HardwareDesigner.class, cascade=ALL, mappedBy="mappedEmployerUC7") |
| private Map<String, HardwareDesigner> hardwareDesignersMapUC7; |
| */ |
| // Step 1: We check via the ContainerPolicy interface for a mapPolicy for the keyMapping (covers MappedKeyMapContainerPolicy and its superclass MapContainerPolicy |
| if (policy.isMapPolicy()) { |
| // check for Either a generic Map (MapContainerPolicy) or specific MappedKeyMapContainerPolicy subclass |
| if (policy.isMappedKeyMapPolicy()) { |
| // See UC9 |
| // The cast below is unavoidable because getKeyMapping() is not overridden from the MapContainerPolicy superclass |
| keyMapping = ((MappedKeyMapContainerPolicy)policy).getKeyMapping(); |
| policyKeyType = keyMapping.getMapKeyTargetType(); |
| } else { |
| /** |
| * Assume we have a MapContainerPolicy general superclass with a lazy-loaded keyType |
| * or a DirectMapContainerPolicy using a &BasicMap |
| * See UC2, UC4, UC8, UC13 (unidirectional ManyToOne becomes ManyToMany) |
| * returns a Class<?> or descriptor (via AggregateObjectMapping) or null in 2 cases - |
| * 1 - if the ContainerPolicy does not support maps |
| * 2 - If the keyField is null (we handle this below by consulting the CMPPolicy) |
| */ |
| policyKeyType = policy.getKeyType(); |
| } |
| } |
| |
| /** |
| * Step 2: We determine the java class from the policyKeyType (class or ClassDecriptor) |
| * We also perform alternate keyType lookup for the case where |
| * the name attribute is not specified in a MapKey annotation where |
| * the map key is one of the following (via the MapContainerPolicy superclass of MappedKeyMapContainerPolicy) |
| * - map key is an Entity with an IdClass |
| * - map key is Java class that is defined as the IdClass of an Entity (above) |
| */ |
| if(null == policyKeyType) { |
| // Pending reproduction case: @MapKey private Map<K,V> // no name column specified and the PK is defined by a method |
| if(null == javaClass) { |
| if(policy.isMappedKeyMapPolicy()) { |
| // See UC10, UC11 |
| javaClass = getOwningPKTypeWhenMapKeyAnnotationMissingOrDefaulted( |
| (MappedKeyMapContainerPolicy)policy); |
| } |
| } |
| } else { |
| // Process the policyKeyType normally |
| if(policyKeyType instanceof ClassDescriptor) { // from AggregateObjectMapping |
| javaClass = ((ClassDescriptor)policyKeyType).getJavaClass(); |
| } else { |
| javaClass = (Class<?>)policyKeyType; |
| } |
| } |
| |
| // Optional: catch any holes in our keyType logic (8 hits in clover coverage) |
| if(null == javaClass) { |
| javaClass = Object.class; |
| } |
| |
| // Step 3: We wrap the java type in a Metamodel Type instance or retrieve an existing Type |
| this.keyType = (Type<K>) getMetamodel().getType(javaClass); |
| } |
| |
| /** |
| * INTERNAL: |
| * Default to the PK of the owning descriptor when no MapKey or MapKey:name attribute is specified. |
| * Prerequisites: policy on the mapping is of type MappedKeyMapPolicy |
| * @return |
| */ |
| private Class getOwningPKTypeWhenMapKeyAnnotationMissingOrDefaulted(MappedKeyMapContainerPolicy policy) { |
| Class<?> javaClass = null;; |
| MapKeyMapping mapKeyMapping = policy.getKeyMapping(); |
| ClassDescriptor descriptor = ((DatabaseMapping)mapKeyMapping).getDescriptor(); |
| // If the reference descriptor is null then we are on a direct mapping |
| if(null != descriptor) { |
| javaClass = ((DatabaseMapping)mapKeyMapping).getAttributeClassification(); |
| if(null == javaClass) { |
| // Default to the PK of the owning descriptor when no MapKey or MapKey:name attribute is specified |
| if (descriptor.getCMPPolicy() != null) { |
| javaClass = descriptor.getCMPPolicy().getPKClass(); |
| } |
| } |
| } |
| return javaClass; |
| } |
| |
| /** |
| * Return the collection type. |
| * @return collection type |
| */ |
| @Override |
| public CollectionType getCollectionType() { |
| return CollectionType.MAP; |
| } |
| |
| /** |
| * Return the Java type of the represented attribute. |
| * @return Java type |
| */ |
| @Override |
| public Class getJavaType() { |
| return Map.class; |
| } |
| |
| /** |
| * Return the Java type of the map key. |
| * @return Java key type |
| * @see MapAttribute |
| */ |
| @Override |
| public Class<K> getKeyJavaType() { |
| return keyType.getJavaType(); |
| } |
| |
| /** |
| * Return the type representing the key type of the map. |
| * @return type representing key type |
| * @see MapAttribute |
| */ |
| @Override |
| public Type<K> getKeyType() { |
| return this.keyType; |
| } |
| } |